⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 eth_phy.v

📁 用Verilog实现的以太网接口
💻 V
📖 第 1 页 / 共 3 页
字号:


`include "timescale.v"
`include "eth_phy_defines.v"
`include "tb_eth_defines.v"
module eth_phy // This PHY model simulate simplified Intel LXT971A PHY
(
        // COMMON
        m_rst_n_i,

        // MAC TX
        mtx_clk_o,
        mtxd_i,
        mtxen_i,
        mtxerr_i,

        // MAC RX
        mrx_clk_o,
        mrxd_o,
        mrxdv_o,
        mrxerr_o,

        mcoll_o,
        mcrs_o,

        // MIIM
        mdc_i,
        md_io,

        // SYSTEM
        phy_log
);

//////////////////////////////////////////////////////////////////////
//
// Input/output signals
//
//////////////////////////////////////////////////////////////////////

// MAC miscellaneous signals
input           m_rst_n_i;
// MAC TX signals
output          mtx_clk_o;
input   [3:0]   mtxd_i;
input           mtxen_i;
input           mtxerr_i;
// MAC RX signals
output          mrx_clk_o;
output  [3:0]   mrxd_o;
output          mrxdv_o;
output          mrxerr_o;
// MAC common signals
output          mcoll_o;
output          mcrs_o;
// MAC management signals
input           mdc_i;
inout           md_io;
// SYSTEM
input   [31:0]  phy_log;


//////////////////////////////////////////////////////////////////////
//
// PHY management (MIIM) REGISTER definitions
//
//////////////////////////////////////////////////////////////////////
//
//   Supported registers:
//
// Addr | Register Name
//--------------------------------------------------------------------
//   0  | Control reg.     |
//   1  | Status reg. #1   |--> normal operation
//   2  | PHY ID reg. 1    |
//   3  | PHY ID reg. 2    |
//----------------------
// Addr | Data MEMORY      |-->  for testing
//
//--------------------------------------------------------------------
//
// Control register
reg            control_bit15; // self clearing bit
reg    [14:10] control_bit14_10;
reg            control_bit9; // self clearing bit
reg    [8:0]   control_bit8_0;
// Status register
wire   [15:9]  status_bit15_9 = `SUPPORTED_SPEED_AND_PORT;
wire           status_bit8    = `EXTENDED_STATUS;
wire           status_bit7    = 1'b0; // reserved
reg    [6:0]   status_bit6_0;
// PHY ID register 1
wire   [15:0]  phy_id1        = `PHY_ID1;
// PHY ID register 2
wire   [15:0]  phy_id2        = {`PHY_ID2, `MAN_MODEL_NUM, `MAN_REVISION_NUM};
//--------------------------------------------------------------------
//
// Data MEMORY
reg    [15:0]  data_mem [0:31]; // 32 locations of 16-bit data width
//
//////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////
//
// PHY clocks - RX & TX
//
//////////////////////////////////////////////////////////////////////

reg       mtx_clk_o;
reg       mrx_clk_o;

// random generator for a RX period when link is down
real      rx_link_down_halfperiod;

always@(status_bit6_0[2])
begin
  if (!status_bit6_0[2]) // Link is down
  begin
    #1 rx_link_down_halfperiod = ({$random} % 243) + 13;
    `ifdef VERBOSE
    #1 $fdisplay(phy_log, "   (%0t)(%m)MAC RX clock is %f MHz while ethernet link is down!", 
                 $time, (1000/(rx_link_down_halfperiod*2)) );
    `endif
  end
end

`ifdef VERBOSE
always@(status_bit6_0[2])
begin
  if (!status_bit6_0[2]) // Link is down
    #1 $fdisplay(phy_log, "   (%0t)(%m)Ethernet link is down!", $time);
  else
    #1 $fdisplay(phy_log, "   (%0t)(%m)Ethernet link is up!", $time);
end
`endif

// speed selection signal eth_speed: 1'b1 - 100 Mbps, 1'b0 - 10 Mbps
wire      eth_speed;

assign eth_speed = ( (control_bit14_10[13]) && !((`LED_CFG1) && (`LED_CFG2)) );

`ifdef VERBOSE
always@(eth_speed)
begin
  if (eth_speed)
    #1 $fdisplay(phy_log, "   (%0t)(%m)PHY configured to 100 Mbps!", $time);
  else
    #1 $fdisplay(phy_log, "   (%0t)(%m)PHY configured tp 10 Mbps!", $time);
end
`endif

// different clock calculation between RX and TX, so that there is alsways a litle difference
/*initial
begin
  set_mrx_equal_mtx = 1; // default
end*/

always
begin
  mtx_clk_o = 0;
  #7;
  forever
  begin
    if (eth_speed) // 100 Mbps - 25 MHz, 40 ns
    begin
      #20 mtx_clk_o = ~mtx_clk_o;
    end
    else // 10 Mbps - 2.5 MHz, 400 ns
    begin
      #200 mtx_clk_o = ~mtx_clk_o;
    end
  end
end

always
begin
  // EQUAL mrx_clk to mtx_clk
  mrx_clk_o = 0;
  #7;
  forever
  begin
    if (eth_speed) // 100 Mbps - 25 MHz, 40 ns
    begin
      #20 mrx_clk_o = ~mrx_clk_o;
    end
    else // 10 Mbps - 2.5 MHz, 400 ns
    begin
      #200 mrx_clk_o = ~mrx_clk_o;
    end
  end
  // DIFFERENT mrx_clk than mtx_clk
/*  mrx_clk_diff_than_mtx = 1;
  #3;
  forever
  begin
    if (status_bit6_0[2]) // Link is UP
    begin
      if (eth_speed) // 100 Mbps - 25 MHz, 40 ns
      begin
        //#(((1/0.025001)/2)) 
        #19.99 mrx_clk_diff_than_mtx = ~mrx_clk_diff_than_mtx; // period is calculated from frequency in GHz
      end
      else // 10 Mbps - 2.5 MHz, 400 ns
      begin
        //#(((1/0.0024999)/2)) 
        #200.01 mrx_clk_diff_than_mtx = ~mrx_clk_diff_than_mtx; // period is calculated from frequency in GHz
      end
    end
    else // Link is down
    begin
      #(rx_link_down_halfperiod) mrx_clk_diff_than_mtx = ~mrx_clk_diff_than_mtx; // random frequency between 2 MHz and 40 MHz
    end
  end*/
//  // set output mrx_clk
//  if (set_mrx_equal_mtx)
//    mrx_clk_o = mrx_clk_equal_to_mtx;
//  else
//    mrx_clk_o = mrx_clk_diff_than_mtx;
end

// set output mrx_clk
//assign mrx_clk_o = set_mrx_equal_mtx ? mrx_clk_equal_to_mtx : mrx_clk_diff_than_mtx ;

//////////////////////////////////////////////////////////////////////
//
// PHY management (MIIM) interface
//
//////////////////////////////////////////////////////////////////////
reg             respond_to_all_phy_addr; // PHY will respond to all phy addresses
reg             no_preamble; // PHY responds to frames without preamble

integer         md_transfer_cnt; // counter countes the value of whole data transfer
reg             md_transfer_cnt_reset; // for reseting the counter
reg             md_io_reg; // registered input
reg             md_io_output; // registered output
reg             md_io_rd_wr;  // op-code latched (read or write)
reg             md_io_enable; // output enable
reg     [4:0]   phy_address; // address of PHY device
reg     [4:0]   reg_address; // address of a register
reg             md_get_phy_address; // for shifting PHY address in
reg             md_get_reg_address; // for shifting register address in
reg     [15:0]  reg_data_in; // data to be written in a register
reg             md_get_reg_data_in; // for shifting data in
reg             md_put_reg_data_in; // for storing data into a selected register
reg     [15:0]  reg_data_out; // data to be read from a register
reg             md_put_reg_data_out; // for registering data from a selected register

wire    [15:0]  register_bus_in; // data bus to a selected register
reg     [15:0]  register_bus_out; // data bus from a selected register

initial
begin
  md_io_enable = 1'b0;
  respond_to_all_phy_addr = 1'b0;
  no_preamble = 1'b0;
end

// tristate output
assign #1 md_io = (m_rst_n_i && md_io_enable) ? md_io_output : 1'bz ;

// registering input
always@(posedge mdc_i or negedge m_rst_n_i)
begin
  if (!m_rst_n_i)
    md_io_reg <= #1 0;
  else
    md_io_reg <= #1 md_io;
end

// getting (shifting) PHY address, Register address and Data in
// putting Data out and shifting
always@(posedge mdc_i or negedge m_rst_n_i)
begin
  if (!m_rst_n_i)
  begin
    phy_address <= 0;
    reg_address <= 0;
    reg_data_in <= 0;
    reg_data_out <= 0;
    md_io_output <= 0;
  end
  else
  begin
    if (md_get_phy_address)
    begin
      phy_address[4:1] <= phy_address[3:0]; // correct address is `ETH_PHY_ADDR
      phy_address[0]   <= md_io;
    end
    if (md_get_reg_address)
    begin
      reg_address[4:1] <= reg_address[3:0];
      reg_address[0]   <= md_io;
    end
    if (md_get_reg_data_in)
    begin
      reg_data_in[15:1] <= reg_data_in[14:0];
      reg_data_in[0]    <= md_io;
    end
    if (md_put_reg_data_out)
    begin
      reg_data_out <= register_bus_out;
    end
    if (md_io_enable)
    begin
      md_io_output       <= reg_data_out[15];
      reg_data_out[15:1] <= reg_data_out[14:0];
      reg_data_out[0]    <= 1'b0;
    end
  end
end

assign #1 register_bus_in = reg_data_in; // md_put_reg_data_in - allows writing to a selected register

// counter for transfer to and from MIIM
always@(posedge mdc_i or negedge m_rst_n_i)
begin
  if (!m_rst_n_i)
  begin
    if (no_preamble)
      md_transfer_cnt <= 33;
    else
      md_transfer_cnt <= 1;
  end
  else
  begin
    if (md_transfer_cnt_reset)
    begin
      if (no_preamble)
        md_transfer_cnt <= 33;
      else
        md_transfer_cnt <= 1;
    end
    else if (md_transfer_cnt < 64)
    begin
      md_transfer_cnt <= md_transfer_cnt + 1'b1;
    end
    else
    begin
      if (no_preamble)
        md_transfer_cnt <= 33;
      else
        md_transfer_cnt <= 1;
    end
  end
end

// MIIM transfer control
always@(m_rst_n_i or md_transfer_cnt or md_io_reg or md_io_rd_wr or 
        phy_address or respond_to_all_phy_addr or no_preamble)
begin
  #1;
  while ((m_rst_n_i) && (md_transfer_cnt <= 64))
  begin
    // reset the signal - put registered data in the register (when write)
    // check preamble
    if (md_transfer_cnt < 33)
    begin
      #4 md_put_reg_data_in = 1'b0;
      if (md_io_reg !== 1'b1)
      begin
        #1 md_transfer_cnt_reset = 1'b1;
      end
      else
      begin
        #1 md_transfer_cnt_reset = 1'b0;
      end
    end

    // check start bits
    else if (md_transfer_cnt == 33)
    begin
      if (no_preamble)
      begin
        #4 md_put_reg_data_in = 1'b0;
        if (md_io_reg === 1'b0)
        begin
          #1 md_transfer_cnt_reset = 1'b0;
        end
        else
        begin
          #1 md_transfer_cnt_reset = 1'b1;
          //if ((md_io_reg !== 1'bz) && (md_io_reg !== 1'b1))
          if (md_io_reg !== 1'bz)
          begin
            // ERROR - start !
            `ifdef VERBOSE
            $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong first start bit (without preamble)", $time);
            `endif
            #10 $stop;
          end
        end
      end
      else // with preamble
      begin
        #4 ;
        `ifdef VERBOSE
        $fdisplay(phy_log, "   (%0t)(%m)MIIM - 32-bit preamble received", $time);
        `endif
        // check start bit only if md_transfer_cnt_reset is inactive, because if
        // preamble suppression was changed start bit should not be checked
        if ((md_io_reg !== 1'b0) && (md_transfer_cnt_reset == 1'b0))
        begin
          // ERROR - start !
          `ifdef VERBOSE
          $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong first start bit", $time);
          `endif
          #10 $stop;
        end
      end
    end

    else if (md_transfer_cnt == 34)
    begin
      #4;
      if (md_io_reg !== 1'b1)
      begin
        // ERROR - start !
        #1;
        `ifdef VERBOSE
        if (no_preamble)
          $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong second start bit (without preamble)", $time);
        else
          $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong second start bit", $time);
        `endif
        #10 $stop;
      end
      else
      begin
        `ifdef VERBOSE
        if (no_preamble)
          #1 $fdisplay(phy_log, "   (%0t)(%m)MIIM - 2 start bits received (without preamble)", $time);
        else
          #1 $fdisplay(phy_log, "   (%0t)(%m)MIIM - 2 start bits received", $time);
        `endif
      end
    end

    // register the op-code (rd / wr)
    else if (md_transfer_cnt == 35)
    begin
      #4;
      if (md_io_reg === 1'b1)
      begin
        #1 md_io_rd_wr = 1'b1;
      end
      else 
      begin
        #1 md_io_rd_wr = 1'b0;
      end
    end

    else if (md_transfer_cnt == 36)
    begin
      #4;
      if ((md_io_reg === 1'b0) && (md_io_rd_wr == 1'b1))
      begin
        #1 md_io_rd_wr = 1'b1; // reading from PHY registers
        `ifdef VERBOSE
        $fdisplay(phy_log, "   (%0t)(%m)MIIM - op-code for READING from registers", $time);
        `endif
      end
      else if ((md_io_reg === 1'b1) && (md_io_rd_wr == 1'b0))
      begin
        #1 md_io_rd_wr = 1'b0; // writing to PHY registers
        `ifdef VERBOSE

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -