`timescale 1ns / 1ns

module tb_demo;

  //-------------------- Instantiate Xilinx glbl module ----------------------
  // - this is needed to get ModelSim to work because e.g. I/O buffer models
  //   refer directly to glbl.GTS and similar signals

  wire GSR;
  wire GTS;
  xlnx_glbl glbl( .GSR( GSR ), .GTS( GTS ) );

  reg  VLOG_ExitSignal = 0;
  reg  Done = 0;
  reg  Error = 0;

  //-------------------------------------------------------------------------

  reg        Reset_n;
  reg        Clk_100M;
  reg        Clk_125M;

  wire       RS232_TXD;
  wire       RS232_RXD;

  wire       USB_TXD;
  wire       USB_RXD;

  //--- 10/100/1000BASE-T Ethernet PHY (MII/GMII)
  wire       PHY_RESET_n;

  wire       PHY_RXC;
  wire [7:0] PHY_RXD;
  wire       PHY_RXDV;
  wire       PHY_RXER;

  wire       PHY_GTX_CLK; // GMII only
  wire       PHY_TXC;
  wire [7:0] PHY_TXD;
  wire       PHY_TXEN;
  wire       PHY_TXER;

  wire       PHY_COL = 0;
  wire       PHY_CRS = 0;

  wire       PHY_MDC;
  wire       PHY_MDIO;

  wire [1:4] LED;

  reg [1:4]  Button = 4'b0000;

  //-------------------------------------------------------------------------
  // Local declarations
  //-------------------------------------------------------------------------

  //-------------------------------------------------------------------------
  // Instantiation of sub-modules
  //-------------------------------------------------------------------------

  //--- DUT

  demo demo(
    .Reset_n ( Reset_n  ),
    .Clk_100M( Clk_100M ),
    .Clk_125M( Clk_125M ),

    .RS232_TXD( RS232_TXD ),
    .RS232_RXD( RS232_RXD ),

    .USB_TXD( USB_TXD ),
    .USB_RXD( USB_RXD ),

    //--- 10/100/1000BASE-T Ethernet PHY (MII/GMII)
    .PHY_RESET_n( PHY_RESET_n ),

    .PHY_RXC ( PHY_RXC  ),
    .PHY_RXD ( PHY_RXD  ),
    .PHY_RXDV( PHY_RXDV ),
    .PHY_RXER( PHY_RXER ),

    .PHY_GTX_CLK( PHY_GTX_CLK ), // GMII only
    .PHY_TXC    ( PHY_TXC  ),
    .PHY_TXD    ( PHY_TXD  ),
    .PHY_TXEN   ( PHY_TXEN ),
    .PHY_TXER   ( PHY_TXER ),

    .PHY_COL( PHY_COL ),
    .PHY_CRS( PHY_CRS ),

    .PHY_MDC ( PHY_MDC  ),
    .PHY_MDIO( PHY_MDIO ),

    // Misc. I/Os
    .LED   ( LED    ),
    .Button( Button )
  );

  //-------------------------------------------------------------------------
  // MII/GMII Ethernet PHY model

  reg [2:0]  Speed = 3'b000;

  Phy_sim U_Phy_sim(
    .Gtx_clk( PHY_GTX_CLK ),
    .Rx_clk ( PHY_RXC  ),
    .Tx_clk ( PHY_TXC  ),
    .Tx_er  ( PHY_TXER ),
    .Tx_en  ( PHY_TXEN ),
    .Txd    ( PHY_TXD  ),
    .Rx_er  ( PHY_RXER ),
    .Rx_dv  ( PHY_RXDV ),
    .Rxd    ( PHY_RXD  ),
    .Crs    ( PHY_CRS  ),
    .Col    ( PHY_COL  ),
    .Speed  ( Speed    ),
    .Done   ( Done     )
  );

  //-------------------------------------------------------------------------
  // Generate all clocks & reset
  //-------------------------------------------------------------------------

  // Core master clock (100 MHz)
  initial 
    begin
      #10;
      while ( !Done )
        begin
          #5 Clk_100M = 0;
          #5 Clk_100M = 1;
        end
    end

  // GMII master clock (125 MHz)
  initial 
    begin
      #10;
      while ( !Done )
        begin
          #4 Clk_125M = 0;
          #4 Clk_125M = 1;
        end
    end

  initial
    begin
      Reset_n = 0;

      #103;
      Reset_n = 1;
    end

  //--- Emulate UART Transmitter --------------------------------------------

  parameter    PRESCALER_16X = 3;
  integer      Prescaler;
  integer      TxLen = 0;
  reg [2:0]    TxState;
  integer      TxBit;
  reg [1023:0] TxMsg;
  reg          TXD;
  reg          TxDone;

  always @( negedge Reset_n or posedge Clk_100M )
    if ( ~Reset_n )
      begin
        Prescaler <= 0;
        TxState   = 0;
        TXD       = 1;
        TxBit     = 0;
        TxDone    <= 0;
      end
    else
      begin
        TxDone <= 0;

        if ( Prescaler == ((PRESCALER_16X + 1)*16 -1) )
          Prescaler <= 0;
        else
          Prescaler <= Prescaler + 1;

        if ( Prescaler==0 )
          begin
            casez ( TxState )
              0: // IDLE
                begin
                  if ( TxLen != 0 )
                    begin // Send start bit!
                      TxBit = (TxLen-1)*8;
                      TxLen = TxLen - 1;
                      TXD = 0;
                      TxState = 1;
                    end
                end

              1: // Send next data bit
                begin
                  // Send next data bit
                  TXD = TxMsg[ TxBit ];
                  TxBit = TxBit + 1;
                  if ( (TxBit % 8)==0 )
                    // Next send two stop bits
                    TxState = 2;
                end

              2: // First of two stop bits
                begin
                  TXD = 1;
                  TxState = 3;
                end

              3: // Second of two stop bits
                begin
                  TXD = 1;
                  TxState = 0;
                  if ( TxLen == 0 )
                    // Done with transmission!
                    TxDone <= 1;
                end
            endcase
          end
      end

  assign RS232_RXD = TXD;
  assign USB_RXD = 1;

  //--- Send commands to the DUT --------------------------------------------

  initial
    begin
      #10;
      while ( ~Reset_n ) #10;

      // Wait a couple of clock edges before continuing to allow
      // internal logic to get out of reset
      repeat ( 5 )
        @( posedge Clk_100M );

      // Wait for the "READY" message to complete transmission
      #60000;

      // Select 100 Mbps
      Speed = 3'b010;
      TxMsg = "W 0022 0002 ";
      TxLen = 12;
      while ( ~TxDone )
        @( posedge Clk_100M );

      #50000;

      TxMsg = "W 8000 8003 ";
      TxLen = 12;
      while ( ~TxDone )
        @( posedge Clk_100M );

      #50000;

      TxMsg = "W 8001 0011 ";
      TxLen = 12;
      while ( ~TxDone )
        @( posedge Clk_100M );

      #50000;

      TxMsg = "W 8002 1234 ";
      TxLen = 12;
      while ( ~TxDone )
        @( posedge Clk_100M );

      #50000;

      TxMsg = "W 8003 5678 ";
      TxLen = 12;
      while ( ~TxDone )
        @( posedge Clk_100M );

      #50000;

      TxMsg = "W 8004 9ABC ";
      TxLen = 12;
      while ( ~TxDone )
        @( posedge Clk_100M );

      #50000;

      TxMsg = "W 8005 DEF0 ";
      TxLen = 12;
      while ( ~TxDone )
        @( posedge Clk_100M );

      #50000;

      TxMsg = "W 8006 C5C0 ";
      TxLen = 12;
      while ( ~TxDone )
        @( posedge Clk_100M );

      #50000;

      TxMsg = "W 8007 BABE ";
      TxLen = 12;
      while ( ~TxDone )
        @( posedge Clk_100M );

      #50000;

      TxMsg = "R 8006 ";
      TxLen = 7;
      while ( ~TxDone )
        @( posedge Clk_100M );

      #50000;

      // Enable PG!
      TxMsg = "W 1000 0001 ";
      TxLen = 12;
      while ( ~TxDone )
        @( posedge Clk_100M );

      #50000;

      // Read back that PG has been enabled!
      TxMsg = "R 1000 ";
      TxLen = 7;
      while ( ~TxDone )
        @( posedge Clk_100M );

      #50000;

      #50000;

      Done = 1; #10;

      $stop;
    end

  //--- Directly accesses a register on the internal Wishbone bus, bypassing the UART interface

  task WrReg;
    input [15:0] Reg;
    input [15:0] Data;

    begin
    end
  endtask

endmodule