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

📄 eth_phy.v

📁 用Verilog实现的以太网接口
💻 V
📖 第 1 页 / 共 3 页
字号:
        $fdisplay(phy_log, "   (%0t)(%m)MIIM - op-code for WRITING to registers", $time);
        `endif
      end
      else
      begin
        // ERROR - wrong opcode !
        `ifdef VERBOSE
        #1 $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong OP-CODE", $time);
        `endif
        #10 $stop;
      end
    // set the signal - get PHY address
      begin
        #1 md_get_phy_address = 1'b1;
      end
    end

    // reset the signal - get PHY address
    else if (md_transfer_cnt == 41)
    begin
      #4 md_get_phy_address = 1'b0;
    // set the signal - get register address
      #1 md_get_reg_address = 1'b1;
    end

    // reset the signal - get register address
    // set the signal - put register data to output register
    else if (md_transfer_cnt == 46)
    begin
      #4 md_get_reg_address = 1'b0;
      #1 md_put_reg_data_out = 1'b1;
    end

    // reset the signal - put register data to output register
    // set the signal - enable md_io as output when read
    else if (md_transfer_cnt == 47)
    begin
      #4 md_put_reg_data_out = 1'b0;
      if (md_io_rd_wr) //read
      begin
        if (md_io_reg !== 1'bz)
        begin
          // ERROR - turn around !
          `ifdef VERBOSE
          #1 $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong turn-around cycle before reading data out", $time);
          `endif
          #10 $stop;
        end
        if ((phy_address === `ETH_PHY_ADDR) || respond_to_all_phy_addr) // check the PHY address
        begin
          #1 md_io_enable = 1'b1;
          `ifdef VERBOSE
          $fdisplay(phy_log, "   (%0t)(%m)MIIM - received correct PHY ADDRESS: %x", $time, phy_address);
          `endif
        end
        else
        begin
          `ifdef VERBOSE
          #1 $fdisplay(phy_log, "*W (%0t)(%m)MIIM - received different PHY ADDRESS: %x", $time, phy_address);
          `endif
        end
      end
      else // write
      begin
        #1 md_io_enable = 1'b0;
    // check turn around cycle when write on clock 47
        if (md_io_reg !== 1'b1) 
        begin
          // ERROR - turn around !
          `ifdef VERBOSE
          #1 $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong 1. turn-around cycle before writing data in", 
                       $time);
          `endif
          #10 $stop;
        end
      end
    end

    // set the signal - get register data in when write
    else if (md_transfer_cnt == 48)
    begin
      #4;
      if (!md_io_rd_wr) // write
      begin
        #1 md_get_reg_data_in = 1'b1;
    // check turn around cycle when write on clock 48
        if (md_io_reg !== 1'b0)
        begin
          // ERROR - turn around !
          `ifdef VERBOSE
          #1 $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong 2. turn-around cycle before writing data in", 
                       $time);
          `endif
          #10 $stop;
        end
      end
      else // read
      begin
        #1 md_get_reg_data_in = 1'b0;
      end
    end

    // reset the signal - enable md_io as output when read
    // reset the signal - get register data in when write
    // set the signal - put registered data in the register when write
    else if (md_transfer_cnt == 64)
    begin
      #1 md_io_enable = 1'b0;
      #4 md_get_reg_data_in = 1'b0;
      if (!md_io_rd_wr) // write
      begin
        if ((phy_address === `ETH_PHY_ADDR) || respond_to_all_phy_addr) // check the PHY address
        begin
          #1 md_put_reg_data_in = 1'b1;
          `ifdef VERBOSE
          $fdisplay(phy_log, "   (%0t)(%m)MIIM - received correct PHY ADDRESS: %x", $time, phy_address);
          $fdisplay(phy_log, "   (%0t)(%m)MIIM - WRITING to register %x COMPLETED!", $time, reg_address);
          `endif
        end
        else
        begin
          `ifdef VERBOSE
          #1 $fdisplay(phy_log, "*W (%0t)(%m)MIIM - received different PHY ADDRESS: %x", $time, phy_address);
          $fdisplay(phy_log, "*W (%0t)(%m)MIIM - NO WRITING to register %x !", $time, reg_address);
          `endif
        end
      end
      else // read
      begin
        `ifdef VERBOSE
        if ((phy_address === `ETH_PHY_ADDR) || respond_to_all_phy_addr) // check the PHY address
          #1 $fdisplay(phy_log, "   (%0t)(%m)MIIM - READING from register %x COMPLETED!", 
                       $time, reg_address);
        else
          #1 $fdisplay(phy_log, "*W (%0t)(%m)MIIM - NO READING from register %x !", $time, reg_address);
        `endif
      end
    end

    // wait for one clock period
    @(posedge mdc_i)
      #1;
  end 
end

//====================================================================
//
// PHY management (MIIM) REGISTERS
//
//====================================================================
//
//   Supported registers (normal operation):
//
// Addr | Register Name 
//--------------------------------------------------------------------
//   0  | Control reg.  
//   1  | Status reg. #1 
//   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  = `DEFAULT_STATUS;
// 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 management (MIIM) REGISTER control
//
//////////////////////////////////////////////////////////////////////

// wholy writable registers for walking ONE's on data, phy and reg. addresses
reg     registers_addr_data_test_operation;

// Non writable status registers
initial // always
begin
  #1 status_bit6_0[6] = no_preamble;
  status_bit6_0[5] = 1'b0;
  status_bit6_0[3] = 1'b1;
  status_bit6_0[0] = 1'b1;
end
always@(posedge mrx_clk_o)
begin
  status_bit6_0[4] <= #1 1'b0;
  status_bit6_0[1] <= #1 1'b0;
end
initial
begin
  status_bit6_0[2] = 1'b1;
  registers_addr_data_test_operation = 0;
end

// Reading from a selected registers
always@(reg_address or registers_addr_data_test_operation or md_put_reg_data_out or
        control_bit15 or control_bit14_10 or control_bit9 or control_bit8_0 or 
        status_bit15_9 or status_bit8 or status_bit7 or status_bit6_0 or
        phy_id1 or phy_id2)
begin
  if (registers_addr_data_test_operation) // test operation
  begin
    if (md_put_reg_data_out) // read enable
    begin
      register_bus_out = #1 data_mem[reg_address];
    end
  end
  else // normal operation
  begin
    if (md_put_reg_data_out) // read enable
    begin
      case (reg_address)
      5'h0:    register_bus_out = #1 {control_bit15, control_bit14_10, control_bit9, control_bit8_0};
      5'h1:    register_bus_out = #1 {status_bit15_9, status_bit8, status_bit7, status_bit6_0};
      5'h2:    register_bus_out = #1 phy_id1;
      5'h3:    register_bus_out = #1 phy_id2;
      default: register_bus_out = #1 16'hDEAD;
      endcase
    end
  end
end

// Self clear control signals
reg    self_clear_d0;
reg    self_clear_d1;
reg    self_clear_d2;
reg    self_clear_d3;
// Self clearing control
always@(posedge mdc_i or negedge m_rst_n_i)
begin
  if (!m_rst_n_i)
  begin
    self_clear_d0    <= #1 0;
    self_clear_d1    <= #1 0;
    self_clear_d2    <= #1 0;
    self_clear_d3    <= #1 0;
  end
  else
  begin
    self_clear_d0    <= #1 md_put_reg_data_in;
    self_clear_d1    <= #1 self_clear_d0;
    self_clear_d2    <= #1 self_clear_d1;
    self_clear_d3    <= #1 self_clear_d2;
  end
end

// Writing to a selected register
always@(posedge mdc_i or negedge m_rst_n_i)
begin
  if ((!m_rst_n_i) || (control_bit15))
  begin
    if (!registers_addr_data_test_operation) // normal operation
    begin
      control_bit15    <= #1 0;
      control_bit14_10 <= #1 {1'b0, (`LED_CFG1 || `LED_CFG2), `LED_CFG1, 2'b0};
      control_bit9     <= #1 0;
      control_bit8_0   <= #1 {`LED_CFG3, 8'b0};
    end
  end
  else
  begin
    if (registers_addr_data_test_operation) // test operation
    begin
      if (md_put_reg_data_in)
      begin
        data_mem[reg_address] <= #1 register_bus_in[15:0];
      end
    end
    else // normal operation
    begin
      // bits that are normaly written
      if (md_put_reg_data_in)
      begin
        case (reg_address)
        5'h0: 
        begin
          control_bit14_10 <= #1 register_bus_in[14:10];
          control_bit8_0   <= #1 register_bus_in[8:0];
        end
        default:
        begin
        end
        endcase
      end
      // self cleared bits written
      if ((md_put_reg_data_in) && (reg_address == 5'h0))
      begin
        control_bit15 <= #1 register_bus_in[15];
        control_bit9  <= #1 register_bus_in[9];
      end
      else if (self_clear_d3) // self cleared bits cleared
      begin
        control_bit15 <= #1 1'b0;
        control_bit9  <= #1 1'b0;
      end
    end
  end
end

//////////////////////////////////////////////////////////////////////
//
// PHY <-> MAC control (RX and TX clocks are at the begining)
//
//////////////////////////////////////////////////////////////////////

// CARRIER SENSE & COLLISION

// MAC common signals
reg             mcoll_o;
reg             mcrs_o;
// Internal signals controling Carrier sense & Collision
  // MAC common signals generated when appropriate transfer
reg             mcrs_rx;
reg             mcrs_tx;
  // delayed mtxen_i signal for generating delayed tx carrier sense
reg             mtxen_d1;
reg             mtxen_d2;
reg             mtxen_d3;
reg             mtxen_d4;
reg             mtxen_d5;
reg             mtxen_d6;
  // collision signal set or rest within task for controling collision
reg             task_mcoll;
  // carrier sense signal set or rest within task for controling carrier sense
reg             task_mcrs;
reg             task_mcrs_lost;
  // do not generate collision in half duplex - not normal operation
reg             no_collision_in_half_duplex;
  // generate collision in full-duplex mode also - not normal operation
reg             collision_in_full_duplex;
  // do not generate carrier sense in half duplex mode - not normal operation
reg             no_carrier_sense_in_tx_half_duplex;
reg             no_carrier_sense_in_rx_half_duplex;
  // generate carrier sense during TX in full-duplex mode also - not normal operation
reg             carrier_sense_in_tx_full_duplex;
  // do not generate carrier sense during RX in full-duplex mode - not normal operation
reg             no_carrier_sense_in_rx_full_duplex;
  // on RX: delay after carrier sense signal; on TX: carrier sense delayed (delay is one clock period)
reg             real_carrier_sense;

initial
begin
  mcrs_rx = 0;
  mcrs_tx = 0;
  task_mcoll = 0;
  task_mcrs = 0;
  task_mcrs_lost = 0;
  no_collision_in_half_duplex = 0;
  collision_in_full_duplex = 0;
  no_carrier_sense_in_tx_half_duplex = 0;
  no_carrier_sense_in_rx_half_duplex = 0;
  carrier_sense_in_tx_full_duplex = 0;
  no_carrier_sense_in_rx_full_duplex = 0;
  real_carrier_sense = 0;
end

// Collision
always@(m_rst_n_i or control_bit8_0 or collision_in_full_duplex or 
        mcrs_rx or mcrs_tx or task_mcoll or no_collision_in_half_duplex
        )
begin
  if (!m_rst_n_i)
    mcoll_o = 0;
  else
  begin
    if (control_bit8_0[8]) // full duplex
    begin
      if (collision_in_full_duplex) // collision is usually not asserted in full duplex
      begin
        mcoll_o = ((mcrs_rx && mcrs_tx) || task_mcoll);
        `ifdef VERBOSE
        if (mcrs_rx && mcrs_tx)
          $fdisplay(phy_log, "   (%0t)(%m) Collision set in FullDuplex!", $time);
        if (task_mcoll)
          $fdisplay(phy_log, "   (%0t)(%m) Collision set in FullDuplex from TASK!", $time);
        `endif
      end
      else
      begin
        mcoll_o = task_mcoll;
        `ifdef VERBOSE
        if (task_mcoll)
          $fdisplay(phy_log, "   (%0t)(%m) Collision set in FullDuplex from TASK!", $time);
        `endif
      end
    end
    else // half duplex
    begin
      mcoll_o = ((mcrs_rx && mcrs_tx && !no_collision_in_half_duplex) || 
                  task_mcoll);
      `ifdef VERBOSE
      if (mcrs_rx && mcrs_tx)
        $fdisplay(phy_log, "   (%0t)(%m) Collision set in HalfDuplex!", $time);
      if (task_mcoll)
        $fdisplay(phy_log, "   (%0t)(%m) Collision set in HalfDuplex from TASK!", $time);
      `endif
    end
  end
end

// Carrier sense
always@(m_rst_n_i or control_bit8_0 or carrier_sense_in_tx_full_duplex or
        no_carrier_sense_in_rx_full_duplex or
        no_carrier_sense_in_tx_half_duplex or 
        no_carrier_sense_in_rx_half_duplex or 
        mcrs_rx or mcrs_tx or task_mcrs or task_mcrs_lost
        )
begin
  if (!m_rst_n_i)
    mcrs_o = 0;
  else
  begin
    if (control_bit8_0[8]) // full duplex
    begin
      if (carrier_sense_in_tx_full_duplex) // carrier sense is usually not asserted during TX in full duplex
        mcrs_o = ((mcrs_rx && !no_carrier_sense_in_rx_full_duplex) || 
                   mcrs_tx || task_mcrs) && !task_mcrs_lost;
      else
        mcrs_o = ((mcrs_rx && !no_carrier_sense_in_rx_full_duplex) || 
                   task_mcrs) && !task_mcrs_lost;
    end
    else // half duplex
    begin
      mcrs_o = ((mcrs_rx && !no_carrier_sense_in_rx_half_duplex) || 
                (mcrs_tx && !no_carrier_sense_in_tx_half_duplex) || 
                 task_mcrs) && !task_mcrs_lost;
    end
  end
end

// MAC TX CONTROL (RECEIVING AT PHY)

// storage memory for TX data received from MAC
reg     [7:0]  tx_mem [0:4194303]; // 4194304 locations (22 address lines) of 8-bit data width
reg    [31:0]  tx_mem_addr_in; // address for storing to TX memory
reg     [7:0]  tx_mem_data_in; // data for storing to TX memory
reg    [31:0]  tx_cnt; // counts nibbles

// control data of a TX packet for upper layer of testbench
reg            tx_preamble_ok;
reg            tx_sfd_ok;
// if there is a drible nibble, then tx packet is not byte aligned!
reg            tx_byte_aligned_ok;
// complete length of TX packet (Bytes) received (without preamble and SFD)
reg    [31:0]  tx_len;
// complete length of TX packet (Bytes) received (without preamble and SFD) untill MTxErr signal was set first

⌨️ 快捷键说明

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