導讀
當前,互聯網已經極大地改變了我們的生產和生活。與之相適應的,在嵌入式系統的研究開發方面,也越來越重視網絡功能。嵌入式系統已經不再局限于一個個孤立的控制、處理單元,而是走向網絡集成化,從而實現了多個系統的集中控制、信息共享。
以太網(Ethernet)技術在嵌入式系統上的開發應用,已經成為當前嵌入式研究領域的技術熱點之一。一方面,與傳統的 RS-485、CAN 等相比較,以太網更加高速、通用,而且還可以直接與 Internet 相連接,提供更大范圍的遠程訪問;此外,經過適當剪裁和優化的 TCP/IP協議棧,也完全可以適應工業用途的需求。另一方面,相對于新興的 USB 2.0、IEEE 1394 等總線,以太網技術在傳輸距離、布線成本以及控制軟件的通用性上都有明顯的優勢。
基于以太網的嵌入式系統,在以下方面都有良好的應用前景:
? 工業:工業控制、網絡儀表、遠程的分布式數據采集……
? 家庭自動化:智能家庭、信息家電、家庭網關……
? 商業:遠程銷售平臺、智能自動售貨機、公共電話卡發行系統……
? 環保:水源和空氣污染監測,防洪體系及水土質量監測、堤壩安全……
? 其他:交通管理、車輛導航、自動抄表……
因此在使用 FPGA 設計各種嵌入式應用系統時,需要考慮為系統提供以太網接口。本章將通過 FPGA 實現一個以太網控制器(MAC)的實例,詳細介紹實現過程。
第三篇內容摘要:本篇會介紹程序的仿真與測試和總結,包括頂層程序、外部 PHY 芯片模擬程序、仿真結果等相關內容。
四、程序的仿真與測試
上面已經介紹了程序的主要部分。為了檢驗程序是否實現預先設定的功能,需要編寫仿真程序。
以太網控制器的仿真程序(Testbench)需要同時模擬數據通信的兩端:主機(上層協議)和外部 PHY 芯片。因此,設計仿真程序的結構如圖 12 所示。
圖 12 以太網控制器程序Testbench 的結構
從圖 12 上可以看到仿真程序應該包括:頂層程序、模擬 PHY 程序、模擬主機程序和以太網控制程序。
4.1 頂層程序
頂層程序負責連接仿真程序的各個部分:模擬 PHY 程序、模擬主機程序和以太網控制程序。同時頂層程序需要控制仿真的進行,主要代碼如下:
`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; …… //連接以太網控制器 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), //發送數據 .mtx_clk_pad_i(mtx_clk), .mtxd_pad_o(MTxD), .mtxen_pad_o(MTxEn), .mtxerr_pad_o(MTxErr), //接收數據部分 .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), //媒體無關接口模塊 .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 發送數據 .mtx_clk_o(mtx_clk), .mtxd_i(MTxD), .mtxen_i(MTxEn), .mtxerr_i(MTxErr), // MAC 接收數據 .mrx_clk_o(mrx_clk), .mrxd_o(MRxD), .mrxdv_o(MRxDV), .mrxerr_o(MRxErr), .mcoll_o(MColl), .mcrs_o(MCrs), //媒體無關接口模塊 .mdc_i(Mdc_O), .md_io(Mdio_IO), .phy_log(phy_log_file_desc) ); // 連接主機模塊 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 //復位信號 wb_rst = 1'b1; #423 wb_rst = 1'b0; //清除存儲器內容 clear_memories; clear_buffer_descriptors; #423 StartTB = 1'b1; end //產生時鐘信號 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 //開始測試內容 initial begin wait(StartTB); // 開始測試 //初始化全局變量 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); //測試的各個任務 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); //測試全雙工方式下傳輸數據 test_mac_full_duplex_receive(0, 13); //測試全雙工方式下接收數據 test_mac_full_duplex_flow_control(0, 4); // 測試整個數據流程 test_note("PHY generates 'real delayed' Carrier sense and Collision signals for following tests"); eth_phy.carrier_sense_real_delay(1); // 結束測試 test_summary; $stop; end
測試內容通過多個測試任務來執行。限于篇幅,測試任務的內容不一一列出。
4.2 外部 PHY 芯片模擬程序
模擬程序模擬了簡化的 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?orphy_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?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 // 以太網控制器發送數據控制,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?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 //為發送數據產生載波信號 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 仿真結果
如圖 13 所示是對全雙工方式下傳輸數據的測試,圖中加亮的 MTxD 是以太網控制器的數據輸出。
圖 13 全雙工模式下發送數據的測試結果
如圖 14 所示的是全雙工模式下接收數據的測試,圖中加亮的 MRxD 是以太網控制器接收數據的輸入。
圖 14 全雙工模式下接收數據的測試結果
如圖 15 所示的是全雙工模式下數據發送和接收整個過程的測試結果,圖中加亮的 MTxD和 MRxD 是以太網控制器發送數據和接收數據的輸出和輸入端口。
圖 15 全雙模式下數據發送和接收全過程的測試結果
如圖 16 所示的是半雙工模式下發送和接收數據全過程的測試結果。
圖16 半雙工模式下發送和接收數據全過程的測試結果
五、總結
本篇介紹了一個以太網控制器(MAC)的實例。首先介紹了以太網的基本原理,然后介紹了以太網控制器程序的主要結構和主要功能模塊的實現過程。最后用一個測試程序驗證程序的功能是否滿足要求。本章為讀者設計自己的以太網控制器提供了一個有用的方案,并且有助于加深對以太網協議的理解。
審核編輯:劉清
-
FPGA
+關注
關注
1640文章
21907瀏覽量
611546 -
Mac
+關注
關注
0文章
1114瀏覽量
52390 -
PHY
+關注
關注
2文章
310瀏覽量
52352 -
TCPIP協議棧
+關注
關注
0文章
6瀏覽量
6018 -
以太網控制器
+關注
關注
0文章
39瀏覽量
12895
原文標題:往期精選:基于FPGA的以太網控制器設計(附代碼)
文章出處:【微信號:HXSLH1010101010,微信公眾號:FPGA技術江湖】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
基于Xilinx FPGA的千兆以太網控制器的開發

采用多種工業以太網標準的單個FPGA平臺設計
請問怎樣去設計嵌入式以太網控制器?
基于FPGA的以太網MAC子層協議設計實現
萬兆以太網MAC層控制器設計

基于FPGA的以太網MAC控制器的設計與實現

Microchip以太網開關和EtherCAT工業控制器及MAC PHY控制設計解決方案

通過FPGA實現一個以太網控制器MAC的實例

評論