模拟程序模拟了简化的 LXT971A 芯片(Inter 公司的外部 PHY 芯片)。PHY 芯片通过 MIIM(媒体无关接口管理模块)来连接以太网控制器,因此:
• 当以太网控制器向 PHY 芯片模拟程序发送数据时,PHY 芯片模拟程序控制数据按照协议的进行传输;
• 当 PHY 芯片向以太网控制器发送数据时,外部 PHY 芯片模拟程序首先按照协议要求产生需要传输的数据,然后发送到以太网控制器。
外部 PHY 芯片模拟程序的主要代码如下:
- `include "timescale.v"
- `include "eth_phy_defines.v"
- `include "tb_eth_defines.v"
- module eth_phy (m_rst_n_i, mtx_clk_o, mtxd_i, mtxen_i, mtxerr_i, mrx_clk_o, mrxd_o, mrxdv_o,
- mrxerr_o,
- mcoll_o, mcrs_o,mdc_i, md_io, phy_log);
- //输入输出信号
- input m_rst_n_i;
- ……
- //寄存器和连线
- reg control_bit15; // self clearing bit
- ……
- // PHY 芯片模拟程序的 MIIM 部分
- ……
- //初始化
- initial
- begin
- md_io_enable = 1'b0;
- respond_to_all_phy_addr = 1'b0;
- no_preamble = 1'b0;
- end
- // 使输出处于三态
- assign #1 md_io = (m_rst_n_i && md_io_enable) ? md_io_output : 1'bz ;
- //寄存器输入
- 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
- // 获得 PHY 地址、寄存器地址和数据输入,把需要输出的数据移位输出
- // 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
- // 统计通过 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 的传输控制
- 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
- // 复位信号
- // 检查报头
- 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
- //检查开始位
- 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
- //错误
- `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
- // 错误
- `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
- // 错误
- #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
- // 寄存器 op-code
- 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
- $fdisplay(phy_log, " (%0t)(%m)MIIM - op-code for WRITING to registers", $time);
- `endif
- end
- else
- begin
- // 操作码错误
- `ifdef VERBOSE
- #1 $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong OP-CODE", $time);
- `endif
- #10 $stop;
- end
- // 获得 PHY 地址
- begin
- #1 md_get_phy_address = 1'b1;
- end
- end
- 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
- // 获得寄存器地址
- else if (md_transfer_cnt == 46)
- begin
- #4 md_get_reg_address = 1'b0;
- #1 md_put_reg_data_out = 1'b1;
- end
- ……
- // PHY 芯片与以太网控制器之间数据传输的控制
- // 寄存器
- reg mcoll_o;
- ……
- //初始化所有寄存器
- 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
- // 数据冲突
- 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
- //载波监听多路访问
- 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
- // 以太网控制器发送数据控制,PHY 芯片接收数据
- //寄存器
- reg [7:0] tx_mem [0:4194303]; // 4194304 是 22 位地址线所能提供的所有地址,每个地址是 8
- 位
- ……
- //发送数据控制
- always@(posedge mtx_clk_o)
- begin
- // 保存数据并进行基本的帧数据检查
- if (!m_rst_n_i)
- begin
- tx_cnt <= 0;
- tx_preamble_ok <= 0;
- tx_sfd_ok <= 0;
- tx_len <= 0;
- tx_len_err <= 0;
- end
- else
- begin
- if (!mtxen_i)
- begin
- tx_cnt <= 0;
- end
- else
- begin
- //发送四位字节数据的计数器
- tx_cnt <= tx_cnt + 1;
- //设置初始化值,检查第一个四位字节数据的报头
- if (tx_cnt == 0)
- begin
- `ifdef VERBOSE
- $fdisplay(phy_log, " (%0t)(%m) TX frame started with tx_en set!", $time);
- `endif
- if (mtxd_i == 4'h5)
- tx_preamble_ok <= 1;
- else
- tx_preamble_ok <= 0;
- tx_sfd_ok <= 0;
- tx_byte_aligned_ok <= 0;
- tx_len <= 0;
- tx_len_err <= 0;
- end
- // 检查报头
- if ((tx_cnt > 0) && (tx_cnt <= 13))
- begin
- if ((tx_preamble_ok != 1) || (mtxd_i != 4'h5))
- tx_preamble_ok <= 0;
- end
- // 检查 SFD
- if (tx_cnt == 14)
- begin
- `ifdef VERBOSE
- if (tx_preamble_ok == 1)
- $fdisplay(phy_log, " (%0t)(%m) TX frame preamble OK!", $time);
- else
- $fdisplay(phy_log, "*E (%0t)(%m) TX frame preamble NOT OK!", $time);
- `endif
- if (mtxd_i == 4'h5)
- tx_sfd_ok <= 1;
- else
- tx_sfd_ok <= 0;
- end
- if (tx_cnt == 15)
- begin
- if ((tx_sfd_ok != 1) || (mtxd_i != 4'hD))
- tx_sfd_ok <= 0;
- end
- // 控制存储地址数据、类型/长度、数据内容和 FCS 到发送数据缓冲区
- if (tx_cnt > 15)
- begin
- if (tx_cnt == 16)
- begin
- `ifdef VERBOSE
- if (tx_sfd_ok == 1)
- $fdisplay(phy_log, " (%0t)(%m) TX frame SFD OK!", $time);
- else
- $fdisplay(phy_log, "*E (%0t)(%m) TX frame SFD NOT OK!", $time);
- `endif
- end
- if (tx_cnt[0] == 0)
- begin
- tx_mem_data_in[3:0] <= mtxd_i; // storing LSB nibble
- tx_byte_aligned_ok <= 0; // if transfer will stop after this, then there was drible
- nibble
- end
- else
- begin
- tx_mem[tx_mem_addr_in[21:0]] <= {mtxd_i, tx_mem_data_in[3:0]}; // storing data into
- tx memory
- tx_len <= tx_len + 1; // enlarge byte length counter
- tx_byte_aligned_ok <= 1; // if transfer will stop after this, then transfer is byte
- alligned
- tx_mem_addr_in <= tx_mem_addr_in + 1'b1;
- end
- if (mtxerr_i)
- tx_len_err <= tx_len;
- end
- end
- end
- //为发送数据产生载波信号
- if (!m_rst_n_i)
- begin
- mcrs_tx <= 0;
- mtxen_d1 <= 0;
- mtxen_d2 <= 0;
- mtxen_d3 <= 0;
- mtxen_d4 <= 0;
- mtxen_d5 <= 0;
- mtxen_d6 <= 0;
- end
- else
- begin
- mtxen_d1 <= mtxen_i;
- mtxen_d2 <= mtxen_d1;
- mtxen_d3 <= mtxen_d2;
- mtxen_d4 <= mtxen_d3;
- mtxen_d5 <= mtxen_d4;
- mtxen_d6 <= mtxen_d5;
- if (real_carrier_sense)
- mcrs_tx <= mtxen_d6;
- else
- mcrs_tx <= mtxen_i;
- end
- end
- `ifdef VERBOSE
- reg frame_started;
- initial
- begin
- frame_started = 0;
- end
- always@(posedge mtxen_i)
- begin
- frame_started <= 1;
- end
- always@(negedge mtxen_i)
- begin
- if (frame_started)
- begin
- $fdisplay(phy_log, " (%0t)(%m) TX frame ended with tx_en reset!", $time);
- frame_started <= 0;
- end
- end
- always@(posedge mrxerr_o)
- begin
- $fdisplay(phy_log, " (%0t)(%m) RX frame ERROR signal was set!", $time);
- end
- `endif
- ……
- endmodule