導(dǎo)讀
當(dāng)前,互聯(lián)網(wǎng)已經(jīng)極大地改變了我們的生產(chǎn)和生活。與之相適應(yīng)的,在嵌入式系統(tǒng)的研究開(kāi)發(fā)方面,也越來(lái)越重視網(wǎng)絡(luò)功能。嵌入式系統(tǒng)已經(jīng)不再局限于一個(gè)個(gè)孤立的控制、處理單元,而是走向網(wǎng)絡(luò)集成化,從而實(shí)現(xiàn)了多個(gè)系統(tǒng)的集中控制、信息共享。
以太網(wǎng)(Ethernet)技術(shù)在嵌入式系統(tǒng)上的開(kāi)發(fā)應(yīng)用,已經(jīng)成為當(dāng)前嵌入式研究領(lǐng)域的技術(shù)熱點(diǎn)之一。一方面,與傳統(tǒng)的 RS-485、CAN 等相比較,以太網(wǎng)更加高速、通用,而且還可以直接與 Internet 相連接,提供更大范圍的遠(yuǎn)程訪問(wèn);此外,經(jīng)過(guò)適當(dāng)剪裁和優(yōu)化的 TCP/IP協(xié)議棧,也完全可以適應(yīng)工業(yè)用途的需求。另一方面,相對(duì)于新興的 USB 2.0、IEEE 1394 等總線,以太網(wǎng)技術(shù)在傳輸距離、布線成本以及控制軟件的通用性上都有明顯的優(yōu)勢(shì)。
基于以太網(wǎng)的嵌入式系統(tǒng),在以下方面都有良好的應(yīng)用前景:
? 工業(yè):工業(yè)控制、網(wǎng)絡(luò)儀表、遠(yuǎn)程的分布式數(shù)據(jù)采集……
? 家庭自動(dòng)化:智能家庭、信息家電、家庭網(wǎng)關(guān)……
? 商業(yè):遠(yuǎn)程銷售平臺(tái)、智能自動(dòng)售貨機(jī)、公共電話卡發(fā)行系統(tǒng)……
? 環(huán)保:水源和空氣污染監(jiān)測(cè),防洪體系及水土質(zhì)量監(jiān)測(cè)、堤壩安全……
? 其他:交通管理、車輛導(dǎo)航、自動(dòng)抄表……
因此在使用 FPGA 設(shè)計(jì)各種嵌入式應(yīng)用系統(tǒng)時(shí),需要考慮為系統(tǒng)提供以太網(wǎng)接口。本章將通過(guò) FPGA 實(shí)現(xiàn)一個(gè)以太網(wǎng)控制器(MAC)的實(shí)例,詳細(xì)介紹實(shí)現(xiàn)過(guò)程。
第三篇內(nèi)容摘要:本篇會(huì)介紹程序的仿真與測(cè)試和總結(jié),包括頂層程序、外部 PHY 芯片模擬程序、仿真結(jié)果等相關(guān)內(nèi)容。
四、程序的仿真與測(cè)試
上面已經(jīng)介紹了程序的主要部分。為了檢驗(yàn)程序是否實(shí)現(xiàn)預(yù)先設(shè)定的功能,需要編寫仿真程序。
以太網(wǎng)控制器的仿真程序(Testbench)需要同時(shí)模擬數(shù)據(jù)通信的兩端:主機(jī)(上層協(xié)議)和外部 PHY 芯片。因此,設(shè)計(jì)仿真程序的結(jié)構(gòu)如圖 12 所示。

圖 12 以太網(wǎng)控制器程序Testbench 的結(jié)構(gòu)
從圖 12 上可以看到仿真程序應(yīng)該包括:頂層程序、模擬 PHY 程序、模擬主機(jī)程序和以太網(wǎng)控制程序。
4.1 頂層程序
頂層程序負(fù)責(zé)連接仿真程序的各個(gè)部分:模擬 PHY 程序、模擬主機(jī)程序和以太網(wǎng)控制程序。同時(shí)頂層程序需要控制仿真的進(jìn)行,主要代碼如下:
`include "eth_phy_defines.v" `include "wb_model_defines.v" `include "tb_eth_defines.v" `include "eth_defines.v" `include "timescale.v" module tb_ethernet(); //寄存器與連線 reg wb_clk; …… //連接以太網(wǎng)控制器 eth_top eth_top( .wb_clk_i(wb_clk), .wb_rst_i(wb_rst), .wb_adr_i(eth_sl_wb_adr_i[11:2]), .wb_sel_i(eth_sl_wb_sel_i), .wb_we_i(eth_sl_wb_we_i), .wb_cyc_i(eth_sl_wb_cyc_i), .wb_stb_i(eth_sl_wb_stb_i), .wb_ack_o(eth_sl_wb_ack_o), .wb_err_o(eth_sl_wb_err_o), .wb_dat_i(eth_sl_wb_dat_i), .wb_dat_o(eth_sl_wb_dat_o), .m_wb_adr_o(eth_ma_wb_adr_o), .m_wb_sel_o(eth_ma_wb_sel_o), .m_wb_we_o(eth_ma_wb_we_o), .m_wb_dat_i(eth_ma_wb_dat_i), .m_wb_dat_o(eth_ma_wb_dat_o), .m_wb_cyc_o(eth_ma_wb_cyc_o), .m_wb_stb_o(eth_ma_wb_stb_o), .m_wb_ack_i(eth_ma_wb_ack_i), .m_wb_err_i(eth_ma_wb_err_i), //發(fā)送數(shù)據(jù) .mtx_clk_pad_i(mtx_clk), .mtxd_pad_o(MTxD), .mtxen_pad_o(MTxEn), .mtxerr_pad_o(MTxErr), //接收數(shù)據(jù)部分 .mrx_clk_pad_i(mrx_clk), .mrxd_pad_i(MRxD), .mrxdv_pad_i(MRxDV), .mrxerr_pad_i(MRxErr), .mcoll_pad_i(MColl), .mcrs_pad_i(MCrs), //媒體無(wú)關(guān)接口模塊 .mdc_pad_o(Mdc_O), .md_pad_i(Mdi_I), .md_pad_o(Mdo_O), .md_padoe_o(Mdo_OE), .int_o(wb_int) ) //連接模擬 PHY 部分 assign Mdio_IO = Mdo_OE ? Mdo_O : 1'bz ; assign Mdi_I = Mdio_IO; integerphy_log_file_desc; eth_phyeth_phy( .m_rst_n_i(!wb_rst), // MAC 發(fā)送數(shù)據(jù) .mtx_clk_o(mtx_clk), .mtxd_i(MTxD), .mtxen_i(MTxEn), .mtxerr_i(MTxErr), // MAC 接收數(shù)據(jù) .mrx_clk_o(mrx_clk), .mrxd_o(MRxD), .mrxdv_o(MRxDV), .mrxerr_o(MRxErr), .mcoll_o(MColl), .mcrs_o(MCrs), //媒體無(wú)關(guān)接口模塊 .mdc_i(Mdc_O), .md_io(Mdio_IO), .phy_log(phy_log_file_desc) ); // 連接主機(jī)模塊 integer host_log_file_desc; WB_MASTER_BEHAVIORALwb_master( .CLK_I(wb_clk), .RST_I(wb_rst), .TAG_I({`WB_TAG_WIDTH{1'b0}}), .TAG_O(), .ACK_I(eth_sl_wb_ack_o), .ADR_O(eth_sl_wb_adr), // only eth_sl_wb_adr_i[11:2] used .CYC_O(eth_sl_wb_cyc_i), .DAT_I(eth_sl_wb_dat_o), .DAT_O(eth_sl_wb_dat_i), .ERR_I(eth_sl_wb_err_o), .RTY_I(1'b0), // inactive (1'b0) .SEL_O(eth_sl_wb_sel_i), .STB_O(eth_sl_wb_stb_i), .WE_O (eth_sl_wb_we_i), .CAB_O() // NOT USED for now! ) assign eth_sl_wb_adr_i = {20'h0, eth_sl_wb_adr[11:2], 2'h0}; …… //初始化 initial begin //復(fù)位信號(hào) wb_rst = 1'b1; #423 wb_rst = 1'b0; //清除存儲(chǔ)器內(nèi)容 clear_memories; clear_buffer_descriptors; #423 StartTB = 1'b1; end //產(chǎn)生時(shí)鐘信號(hào) initial begin wb_clk=0; forever #15 wb_clk = ~wb_clk; // 2*10 ns -> 33.3 MHz end integer tests_successfull; integer tests_failed; reg [799:0] test_name; // used for tb_log_file reg [3:0] wbm_init_waits; // initial wait cycles between CYC_O and STB_O of WB Master reg [3:0] wbm_subseq_waits; // subsequent wait cycles between STB_Os of WB Master reg [2:0] wbs_waits; // wait cycles befor WB Slave responds reg [7:0] wbs_retries; // if RTY response, then this is the number of retries before ACK regwbm_working;//taskswbm_writeandwbm_readsetsignalwhenworkingandresetitwhenstopworking //開(kāi)始測(cè)試內(nèi)容 initial begin wait(StartTB); // 開(kāi)始測(cè)試 //初始化全局變量 tests_successfull = 0; tests_failed = 0; wbm_working = 0; wbm_init_waits = 4'h1; wbm_subseq_waits = 4'h3; wbs_waits = 4'h1; wbs_retries = 8'h2; wb_slave.cycle_response(`ACK_RESPONSE, wbs_waits, wbs_retries); //測(cè)試的各個(gè)任務(wù) test_note("PHY generates ideal Carrier sense and Collision signals for following tests"); eth_phy.carrier_sense_real_delay(0); test_mac_full_duplex_transmit(0, 21); //測(cè)試全雙工方式下傳輸數(shù)據(jù) test_mac_full_duplex_receive(0, 13); //測(cè)試全雙工方式下接收數(shù)據(jù) test_mac_full_duplex_flow_control(0, 4); // 測(cè)試整個(gè)數(shù)據(jù)流程 test_note("PHY generates 'real delayed' Carrier sense and Collision signals for following tests"); eth_phy.carrier_sense_real_delay(1); // 結(jié)束測(cè)試 test_summary; $stop; end
測(cè)試內(nèi)容通過(guò)多個(gè)測(cè)試任務(wù)來(lái)執(zhí)行。限于篇幅,測(cè)試任務(wù)的內(nèi)容不一一列出。
4.2 外部 PHY 芯片模擬程序
模擬程序模擬了簡(jiǎn)化的 LXT971A 芯片(Inter 公司的外部 PHY 芯片)。PHY 芯片通過(guò) MIIM(媒體無(wú)關(guān)接口管理模塊)來(lái)連接以太網(wǎng)控制器,因此:
? 當(dāng)以太網(wǎng)控制器向 PHY 芯片模擬程序發(fā)送數(shù)據(jù)時(shí),PHY 芯片模擬程序控制數(shù)據(jù)按照協(xié)議的進(jìn)行傳輸;
? 當(dāng) PHY 芯片向以太網(wǎng)控制器發(fā)送數(shù)據(jù)時(shí),外部 PHY 芯片模擬程序首先按照協(xié)議要求產(chǎn)生需要傳輸?shù)臄?shù)據(jù),然后發(fā)送到以太網(wǎng)控制器。
外部 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);
//輸入輸出信號(hào)
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
// 使輸出處于三態(tài)
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 地址、寄存器地址和數(shù)據(jù)輸入,把需要輸出的數(shù)據(jù)移位輸出
// 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
// 統(tǒng)計(jì)通過(guò) MIIM(媒體無(wú)關(guān)接口管理模塊)傳輸?shù)臄?shù)據(jù)
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?orphy_address?or?respond_to_all_phy_addr?or?no_preamble)
begin
#1;
while ((m_rst_n_i) && (md_transfer_cnt <= 64))
begin
// 復(fù)位信號(hào)
// 檢查報(bào)頭
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
//檢查開(kāi)始位
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
//錯(cuò)誤
`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
// 錯(cuò)誤
`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
// 錯(cuò)誤
#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
// 操作碼錯(cuò)誤
`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 芯片與以太網(wǎng)控制器之間數(shù)據(jù)傳輸?shù)目刂?// 寄存器
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
// 數(shù)據(jù)沖突
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
//載波監(jiān)聽(tīng)多路訪問(wèn)
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?duringTX?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
// 以太網(wǎng)控制器發(fā)送數(shù)據(jù)控制,PHY 芯片接收數(shù)據(jù)
//寄存器
??reg?[7:0]?tx_mem?[0:4194303];?//?4194304?是?22?位地址線所能提供的所有地址,每個(gè)地址是?8位
……
//發(fā)送數(shù)據(jù)控制
always@(posedge mtx_clk_o)
begin
//?保存數(shù)據(jù)并進(jìn)行基本的幀數(shù)據(jù)檢查
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
//發(fā)送四位字節(jié)數(shù)據(jù)的計(jì)數(shù)器
tx_cnt <= tx_cnt + 1;
//設(shè)置初始化值,檢查第一個(gè)四位字節(jié)數(shù)據(jù)的報(bào)頭
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
// 檢查報(bào)頭
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
// 控制存儲(chǔ)地址數(shù)據(jù)、類型/長(zhǎng)度、數(shù)據(jù)內(nèi)容和 FCS 到發(fā)送數(shù)據(jù)緩沖區(qū)
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?driblenibble
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
//為發(fā)送數(shù)據(jù)產(chǎn)生載波信號(hào)
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
4.3 仿真結(jié)果
如圖 13 所示是對(duì)全雙工方式下傳輸數(shù)據(jù)的測(cè)試,圖中加亮的 MTxD 是以太網(wǎng)控制器的數(shù)據(jù)輸出。

圖 13 全雙工模式下發(fā)送數(shù)據(jù)的測(cè)試結(jié)果
如圖 14 所示的是全雙工模式下接收數(shù)據(jù)的測(cè)試,圖中加亮的 MRxD 是以太網(wǎng)控制器接收數(shù)據(jù)的輸入。

圖 14 全雙工模式下接收數(shù)據(jù)的測(cè)試結(jié)果
如圖 15 所示的是全雙工模式下數(shù)據(jù)發(fā)送和接收整個(gè)過(guò)程的測(cè)試結(jié)果,圖中加亮的 MTxD和 MRxD 是以太網(wǎng)控制器發(fā)送數(shù)據(jù)和接收數(shù)據(jù)的輸出和輸入端口。

圖 15 全雙模式下數(shù)據(jù)發(fā)送和接收全過(guò)程的測(cè)試結(jié)果
如圖 16 所示的是半雙工模式下發(fā)送和接收數(shù)據(jù)全過(guò)程的測(cè)試結(jié)果。

圖16 半雙工模式下發(fā)送和接收數(shù)據(jù)全過(guò)程的測(cè)試結(jié)果
五、總結(jié)
本篇介紹了一個(gè)以太網(wǎng)控制器(MAC)的實(shí)例。首先介紹了以太網(wǎng)的基本原理,然后介紹了以太網(wǎng)控制器程序的主要結(jié)構(gòu)和主要功能模塊的實(shí)現(xiàn)過(guò)程。最后用一個(gè)測(cè)試程序驗(yàn)證程序的功能是否滿足要求。本章為讀者設(shè)計(jì)自己的以太網(wǎng)控制器提供了一個(gè)有用的方案,并且有助于加深對(duì)以太網(wǎng)協(xié)議的理解。
審核編輯:劉清
-
FPGA
+關(guān)注
關(guān)注
1660文章
22408瀏覽量
636227 -
Mac
+關(guān)注
關(guān)注
0文章
1127瀏覽量
55309 -
PHY
+關(guān)注
關(guān)注
2文章
334瀏覽量
54077 -
TCPIP協(xié)議棧
+關(guān)注
關(guān)注
0文章
6瀏覽量
6244 -
以太網(wǎng)控制器
+關(guān)注
關(guān)注
0文章
52瀏覽量
13184
原文標(biāo)題:往期精選:基于FPGA的以太網(wǎng)控制器設(shè)計(jì)(附代碼)
文章出處:【微信號(hào):HXSLH1010101010,微信公眾號(hào):FPGA技術(shù)江湖】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
基于Xilinx FPGA的千兆以太網(wǎng)控制器的開(kāi)發(fā)
基于FPGA的以太網(wǎng)系統(tǒng)軟硬件實(shí)現(xiàn)方案
基于FPGA的以太網(wǎng)系統(tǒng)軟硬件實(shí)現(xiàn)方案
以太網(wǎng)控制器(MAC)的基本框架怎么搭建
采用多種工業(yè)以太網(wǎng)標(biāo)準(zhǔn)的單個(gè)FPGA平臺(tái)設(shè)計(jì)
以太網(wǎng)控制器如何工作
請(qǐng)問(wèn)怎樣去設(shè)計(jì)嵌入式以太網(wǎng)控制器?
以太網(wǎng)芯片MAC和PHY的關(guān)系 精選資料分享
基于FPGA的以太網(wǎng)MAC子層協(xié)議設(shè)計(jì)實(shí)現(xiàn)
以太網(wǎng)控制器芯片的設(shè)計(jì)及實(shí)現(xiàn)
萬(wàn)兆以太網(wǎng)MAC層控制器設(shè)計(jì)
基于FPGA的以太網(wǎng)MAC控制器的設(shè)計(jì)與實(shí)現(xiàn)
Microchip以太網(wǎng)開(kāi)關(guān)和EtherCAT工業(yè)控制器及MAC PHY控制設(shè)計(jì)解決方案
車載以太網(wǎng)MAC和PHY的問(wèn)題詳解
通過(guò)FPGA實(shí)現(xiàn)一個(gè)以太網(wǎng)控制器MAC的實(shí)例
通過(guò)FPGA實(shí)現(xiàn)一個(gè)以太網(wǎng)控制器MAC的實(shí)例
評(píng)論