0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

通過FPGA實現(xiàn)一個以太網(wǎng)控制器MAC的實例

FPGA技術(shù)江湖 ? 來源:FPGA技術(shù)江湖 ? 2023-01-29 10:48 ? 次閱讀

導(dǎo)讀

當(dāng)前,互聯(lián)網(wǎng)已經(jīng)極大地改變了我們的生產(chǎn)和生活。與之相適應(yīng)的,在嵌入式系統(tǒng)的研究開發(fā)方面,也越來越重視網(wǎng)絡(luò)功能。嵌入式系統(tǒng)已經(jīng)不再局限于一個個孤立的控制、處理單元,而是走向網(wǎng)絡(luò)集成化,從而實現(xiàn)了多個系統(tǒng)的集中控制、信息共享。

以太網(wǎng)Ethernet)技術(shù)在嵌入式系統(tǒng)上的開發(fā)應(yīng)用,已經(jīng)成為當(dāng)前嵌入式研究領(lǐng)域的技術(shù)熱點之一。一方面,與傳統(tǒng)的 RS-485CAN 等相比較,以太網(wǎng)更加高速、通用,而且還可以直接與 Internet 相連接,提供更大范圍的遠程訪問;此外,經(jīng)過適當(dāng)剪裁和優(yōu)化的 TCP/IP協(xié)議棧,也完全可以適應(yīng)工業(yè)用途的需求。另一方面,相對于新興的 USB 2.0、IEEE 1394 等總線,以太網(wǎng)技術(shù)在傳輸距離、布線成本以及控制軟件的通用性上都有明顯的優(yōu)勢。

基于以太網(wǎng)的嵌入式系統(tǒng),在以下方面都有良好的應(yīng)用前景:

? 工業(yè):工業(yè)控制、網(wǎng)絡(luò)儀表、遠程的分布式數(shù)據(jù)采集……

? 家庭自動化:智能家庭、信息家電、家庭網(wǎng)關(guān)……

? 商業(yè):遠程銷售平臺、智能自動售貨機、公共電話卡發(fā)行系統(tǒng)……

? 環(huán)保:水源和空氣污染監(jiān)測,防洪體系及水土質(zhì)量監(jiān)測、堤壩安全……

? 其他:交通管理、車輛導(dǎo)航、自動抄表……

因此在使用 FPGA 設(shè)計各種嵌入式應(yīng)用系統(tǒng)時,需要考慮為系統(tǒng)提供以太網(wǎng)接口。本章將通過 FPGA 實現(xiàn)一個以太網(wǎng)控制器(MAC)的實例,詳細介紹實現(xiàn)過程。

第三篇內(nèi)容摘要:本篇會介紹程序的仿真與測試和總結(jié),包括頂層程序、外部 PHY 芯片模擬程序、仿真結(jié)果等相關(guān)內(nèi)容。

四、程序的仿真與測試

上面已經(jīng)介紹了程序的主要部分。為了檢驗程序是否實現(xiàn)預(yù)先設(shè)定的功能,需要編寫仿真程序。

以太網(wǎng)控制器的仿真程序(Testbench)需要同時模擬數(shù)據(jù)通信的兩端:主機(上層協(xié)議)和外部 PHY 芯片。因此,設(shè)計仿真程序的結(jié)構(gòu)如圖 12 所示。

f872eb56-9ef6-11ed-bfe3-dac502259ad0.png

圖 12 以太網(wǎng)控制器程序Testbench 的結(jié)構(gòu)

從圖 12 上可以看到仿真程序應(yīng)該包括:頂層程序、模擬 PHY 程序、模擬主機程序和以太網(wǎng)控制程序。

4.1 頂層程序

頂層程序負責(zé)連接仿真程序的各個部分:模擬 PHY 程序、模擬主機程序和以太網(wǎng)控制程序。同時頂層程序需要控制仿真的進行,主要代碼如下:

`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),
                //媒體無關(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),
                  //媒體無關(guān)接口模塊
                  .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
  //復(fù)位信號
  wb_rst = 1'b1;
  #423 wb_rst = 1'b0;
//清除存儲器內(nèi)容
  clear_memories;
  clear_buffer_descriptors;
  #423 StartTB = 1'b1;
  end


//產(chǎn)生時鐘信號
  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


//開始測試內(nèi)容
  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);


//測試的各個任務(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); //測試全雙工方式下傳輸數(shù)據(jù)
  test_mac_full_duplex_receive(0, 13); //測試全雙工方式下接收數(shù)據(jù)
  test_mac_full_duplex_flow_control(0, 4); // 測試整個數(shù)據(jù)流程
  test_note("PHY generates 'real delayed' Carrier sense and Collision signals for following
  tests");
eth_phy.carrier_sense_real_delay(1);
// 結(jié)束測試
  test_summary;
  $stop;
  end

測試內(nèi)容通過多個測試任務(wù)來執(zhí)行。限于篇幅,測試任務(wù)的內(nèi)容不一一列出。

4.2 外部 PHY 芯片模擬程序

模擬程序模擬了簡化的 LXT971A 芯片(Inter 公司的外部 PHY 芯片)。PHY 芯片通過 MIIM(媒體無關(guān)接口管理模塊)來連接以太網(wǎng)控制器,因此:

? 當(dāng)以太網(wǎng)控制器向 PHY 芯片模擬程序發(fā)送數(shù)據(jù)時,PHY 芯片模擬程序控制數(shù)據(jù)按照協(xié)議的進行傳輸;

? 當(dāng) PHY 芯片向以太網(wǎng)控制器發(fā)送數(shù)據(jù)時,外部 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);


//輸入輸出信號
  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)計通過 MIIM(媒體無關(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ù)位信號
// 檢查報頭
    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 芯片與以太網(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)聽多路訪問
  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?位地址線所能提供的所有地址,每個地址是?8位
  ……


//發(fā)送數(shù)據(jù)控制
  always@(posedge mtx_clk_o)
  begin
//?保存數(shù)據(jù)并進行基本的幀數(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ù)的計數(shù)器
            tx_cnt <= tx_cnt + 1;
//設(shè)置初始化值,檢查第一個四位字節(jié)數(shù)據(jù)的報頭
        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
  // 控制存儲地址數(shù)據(jù)、類型/長度、數(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)生載波信號
                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 所示是對全雙工方式下傳輸數(shù)據(jù)的測試,圖中加亮的 MTxD 是以太網(wǎng)控制器的數(shù)據(jù)輸出。

f890c3f6-9ef6-11ed-bfe3-dac502259ad0.png

圖 13 全雙工模式下發(fā)送數(shù)據(jù)的測試結(jié)果

如圖 14 所示的是全雙工模式下接收數(shù)據(jù)的測試,圖中加亮的 MRxD 是以太網(wǎng)控制器接收數(shù)據(jù)的輸入。

f8aef4d4-9ef6-11ed-bfe3-dac502259ad0.png

圖 14 全雙工模式下接收數(shù)據(jù)的測試結(jié)果

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

f8dfb902-9ef6-11ed-bfe3-dac502259ad0.png

圖 15 全雙模式下數(shù)據(jù)發(fā)送和接收全過程的測試結(jié)果

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

f90b0eae-9ef6-11ed-bfe3-dac502259ad0.png

圖16 半雙工模式下發(fā)送和接收數(shù)據(jù)全過程的測試結(jié)果

五、總結(jié)

本篇介紹了一個以太網(wǎng)控制器(MAC)的實例。首先介紹了以太網(wǎng)的基本原理,然后介紹了以太網(wǎng)控制器程序的主要結(jié)構(gòu)和主要功能模塊的實現(xiàn)過程。最后用一個測試程序驗證程序的功能是否滿足要求。本章為讀者設(shè)計自己的以太網(wǎng)控制器提供了一個有用的方案,并且有助于加深對以太網(wǎng)協(xié)議的理解。






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • FPGA
    +關(guān)注

    關(guān)注

    1624

    文章

    21573

    瀏覽量

    600676
  • Mac
    Mac
    +關(guān)注

    關(guān)注

    0

    文章

    1087

    瀏覽量

    51283
  • PHY
    PHY
    +關(guān)注

    關(guān)注

    2

    文章

    299

    瀏覽量

    51623
  • TCPIP協(xié)議棧
    +關(guān)注

    關(guān)注

    0

    文章

    6

    瀏覽量

    5887
  • 以太網(wǎng)控制器
    +關(guān)注

    關(guān)注

    0

    文章

    37

    瀏覽量

    12673

原文標(biāo)題:往期精選:基于FPGA的以太網(wǎng)控制器設(shè)計(附代碼)

文章出處:【微信號:HXSLH1010101010,微信公眾號:FPGA技術(shù)江湖】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    基于Xilinx FPGA的千兆以太網(wǎng)控制器的開發(fā)

    MAC子層的FPGA設(shè)計、MAC子層與上層協(xié)議的接口設(shè)計以及MAC與物理層(PHY)的MII接口設(shè)計。##Xilinx 提供了三態(tài)以太網(wǎng)
    發(fā)表于 01-23 11:13 ?3w次閱讀
    基于Xilinx <b class='flag-5'>FPGA</b>的千兆<b class='flag-5'>以太網(wǎng)</b><b class='flag-5'>控制器</b>的開發(fā)

    基于FPGA以太網(wǎng)系統(tǒng)軟硬件實現(xiàn)方案

    ,曾在重要軍工項目中擔(dān)任分系統(tǒng)負責(zé)人,利用altera FPGA平臺實現(xiàn)高性能的
    發(fā)表于 06-19 12:04

    基于FPGA以太網(wǎng)系統(tǒng)軟硬件實現(xiàn)方案

    ,曾在重要軍工項目中擔(dān)任分系統(tǒng)負責(zé)人,利用altera FPGA平臺實現(xiàn)高性能的
    發(fā)表于 06-19 12:06

    以太網(wǎng)控制器(MAC)的基本框架怎么搭建

    以太網(wǎng)控制器MAC實現(xiàn)以太網(wǎng)標(biāo)準(zhǔn)的第二層協(xié)議——MAC(媒體訪問
    發(fā)表于 12-28 17:30

    采用多種工業(yè)以太網(wǎng)標(biāo)準(zhǔn)的單個FPGA平臺設(shè)計

    Altera公司工業(yè)以太網(wǎng)是指使用基于以太網(wǎng)的協(xié)議實現(xiàn)工業(yè)自動化和產(chǎn)品機械控制中實時可靠的通信,在車間底層控制器之間、車間之間,以及車間和辦
    發(fā)表于 07-29 07:40

    以太網(wǎng)控制器如何工作

    我正在開新帖子,因為我認(rèn)為舊的已經(jīng)完成了工作,并且指出了以太網(wǎng)控制器的方向。我熟悉UART通信,并在幾個pic微控制器
    發(fā)表于 04-30 10:39

    請問怎樣去設(shè)計嵌入式以太網(wǎng)控制器?

    以太網(wǎng)控制器的總體結(jié)構(gòu)有哪些模塊?MAC發(fā)送模塊是由哪些部分組成的?MAC接收模塊是由哪些部分組成的?MAC還有哪些其它的模塊?如何
    發(fā)表于 04-14 06:09

    以太網(wǎng)芯片MAC和PHY的關(guān)系 精選資料分享

    問:如何實現(xiàn)單片以太網(wǎng)控制器?答:訣竅是將微控制器、以太網(wǎng)媒體接入控制器(
    發(fā)表于 07-29 09:22

    基于FPGA以太網(wǎng)MAC子層協(xié)議設(shè)計實現(xiàn)

    摘 要:介紹了基于現(xiàn)場可編程門陣列(FPGA)的以太網(wǎng)MAC子層協(xié)議的硬件實現(xiàn)方法.硬件結(jié)構(gòu)上由控制模塊、發(fā)送模塊和接收模塊3
    發(fā)表于 07-15 11:27 ?24次下載

    以太網(wǎng)控制器芯片的設(shè)計及實現(xiàn)

    以太網(wǎng)控制器芯片的設(shè)計及實現(xiàn) 網(wǎng)絡(luò)控制器芯片的功能與設(shè)計實現(xiàn)IEEE 802.3協(xié)議是針對以太網(wǎng)
    發(fā)表于 07-26 22:34 ?1480次閱讀
    <b class='flag-5'>以太網(wǎng)</b><b class='flag-5'>控制器</b>芯片的設(shè)計及<b class='flag-5'>實現(xiàn)</b>

    萬兆以太網(wǎng)MAC控制器設(shè)計

    根據(jù)IEEE 802.3 和802.3 ae 協(xié)議, 設(shè)計實現(xiàn)了萬兆以太網(wǎng)MAC控制器. 文中使用交叉流水CRC 和異步雙口RAM技術(shù), 解決了非固定數(shù)據(jù)寬度CRC 編碼校驗, 以
    發(fā)表于 05-13 18:57 ?59次下載
    萬兆<b class='flag-5'>以太網(wǎng)</b><b class='flag-5'>MAC</b>層<b class='flag-5'>控制器</b>設(shè)計

    基于FPGA以太網(wǎng)MAC控制器的設(shè)計與實現(xiàn)

    介紹了基于FPGA以太網(wǎng)MAC控制器的設(shè)計,主要實現(xiàn)了半雙工模式下CSMA/CD協(xié)議、全雙工模式下Pause幀的收發(fā),以及對物理層芯片中寄
    發(fā)表于 11-15 11:38 ?280次下載
    基于<b class='flag-5'>FPGA</b>的<b class='flag-5'>以太網(wǎng)</b><b class='flag-5'>MAC</b><b class='flag-5'>控制器</b>的設(shè)計與<b class='flag-5'>實現(xiàn)</b>

    Microchip以太網(wǎng)開關(guān)和EtherCAT工業(yè)控制器MAC PHY控制設(shè)計解決方案

    Microchip提供了旨在支持新以太網(wǎng)開關(guān)、EtherCAT工業(yè)控制器和10/100工業(yè)以太網(wǎng)MAC/PHY
    發(fā)表于 06-15 17:26 ?36次下載
    Microchip<b class='flag-5'>以太網(wǎng)</b>開關(guān)和EtherCAT工業(yè)<b class='flag-5'>控制器</b>及<b class='flag-5'>MAC</b> PHY<b class='flag-5'>控制</b>設(shè)計解決方案

    車載以太網(wǎng)MAC和PHY的問題詳解

    問:如何實現(xiàn)單片 以太網(wǎng)控制器 ?答:訣竅是將微控制器以太網(wǎng) 媒體接入控制器
    發(fā)表于 06-02 08:00 ?5次下載
    車載<b class='flag-5'>以太網(wǎng)</b><b class='flag-5'>MAC</b>和PHY的問題詳解

    通過FPGA實現(xiàn)以太網(wǎng)控制器MAC實例

    般所說的以太網(wǎng)協(xié)議是指根據(jù) IEEE 802.3 規(guī)范制定的局域網(wǎng)協(xié)議(LAN,Local AreaNetwork)中的 CSMA/CD 協(xié)議。目前,以太網(wǎng)通信常用的介質(zhì)是雙絞線和光
    發(fā)表于 05-22 10:48 ?1636次閱讀
    <b class='flag-5'>通過</b><b class='flag-5'>FPGA</b><b class='flag-5'>實現(xiàn)</b><b class='flag-5'>一</b><b class='flag-5'>個</b><b class='flag-5'>以太網(wǎng)</b><b class='flag-5'>控制器</b><b class='flag-5'>MAC</b>的<b class='flag-5'>實例</b>