<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          FPGA:Ethernet接口

          作者: 時(shí)間:2024-01-10 來源:EEPW編譯 收藏

          以太網(wǎng)全雙工協(xié)議易于在中實(shí)現(xiàn)。 這里的目標(biāo)是將連接到10BASE-T連接。

          本文引用地址:http://www.ex-cimer.com/article/202401/454644.htm

          以太網(wǎng)數(shù)據(jù)包:發(fā)送和接收

          10BASE-T 接口 0 - 發(fā)送以太網(wǎng)流量的方案

          在這里,我們演示了如何將以太網(wǎng)流量直接從FPGA發(fā)送到PC。

          對(duì)于此食譜,您需要:
          1. FPGA 開發(fā)板,具有 2 個(gè)空閑 IO 和一個(gè) 20MHz 時(shí)鐘。

          2. 一臺(tái)帶有以太網(wǎng)卡并安裝了 TCP-IP 堆棧的 PC(如果你能瀏覽 Internet,你就很好)。

          3. (可選)網(wǎng)絡(luò)集線器或交換機(jī)。

          1. 將FPGA板連接到以太網(wǎng)

          以下是使用以太網(wǎng)集線器或交換機(jī)的典型測(cè)試設(shè)置視圖。



          使用集線器或交換機(jī)可讓電腦在執(zhí)行此實(shí)驗(yàn)時(shí)保持與常規(guī)網(wǎng)絡(luò)(如果有)的連接。 但您也可以將FPGA直接連接到PC。
          我們?cè)谶@里使用帶有外部 20MHz 振蕩器的 Pluto 板。

          將FPGA板上的兩個(gè)IO連接到以太網(wǎng)電纜。

          • 如果電纜的另一端連接到集線器或交換機(jī)(如上圖所示),請(qǐng)使用以太網(wǎng)電纜的引腳 1 和 2。

          • 如果電纜的另一端直接連接到 PC,請(qǐng)使用引腳 3 和 6。

          有關(guān)引腳編號(hào),請(qǐng)從這張圖片中獲取幫助:

          請(qǐng)注意,極性通常無關(guān)緊要,因?yàn)樾盘?hào)是差分的,以太網(wǎng)設(shè)備可以從輸入信號(hào)中檢測(cè)極性。
          此外,即使這在實(shí)踐中有效,我們也無法通過僅將FPGA連接到電纜(我們需要濾波器和變壓器)來滿足以太網(wǎng)電氣要求。 因此,讓我們將其視為“實(shí)驗(yàn)室”實(shí)驗(yàn)。

          2.從PC獲取網(wǎng)絡(luò)信息

          在命令行中鍵入“ipconfig /all”。

          寫下您的“物理地址”和“IP 地址”。

          3. 對(duì)FPGA進(jìn)行編程

          編譯以下 Verilog HDL 代碼。

          確保:

          • 更新代碼中的數(shù)據(jù)值(“IP 源”、“IP 目標(biāo)”和“物理地址”)。

          • 為您的電路板分配正確的引腳(只使用了 3 個(gè)引腳!

          module TENBASET_TxD(clk20, Ethernet_TDp, Ethernet_TDm);

          // a 20MHz clock (this code won't work with a different frequency)
          input clk20;

          // the two differential 10BASE-T outputs
          output Ethernet_TDp, Ethernet_TDm;

          // "IP source" - put an unused IP - if unsure, see comment below after the source code
          parameter IPsource_1 = 192;
          parameter IPsource_2 = 168;
          parameter IPsource_3 = 0;
          parameter IPsource_4 = 44;

          // "IP destination" - put the IP of the PC you want to send to
          parameter IPdestination_1 = 192;
          parameter IPdestination_2 = 168;
          parameter IPdestination_3 = 0;
          parameter IPdestination_4 = 2;

          // "Physical Address" - put the address of the PC you want to send to
          parameter PhysicalAddress_1 = 8'h00;
          parameter PhysicalAddress_2 = 8'h07;
          parameter PhysicalAddress_3 = 8'h95;
          parameter PhysicalAddress_4 = 8'h0B;
          parameter PhysicalAddress_5 = 8'hFB;
          parameter PhysicalAddress_6 = 8'hAF;

          //////////////////////////////////////////////////////////////////////
          // sends a packet roughly every second
          reg [23:0] counter; always @(posedge clk20) counter<=counter+1;
          reg StartSending; always @(posedge clk20) StartSending<=&counter;

          //////////////////////////////////////////////////////////////////////
          // we send a UDP packet, 18 bytes payload

          // calculate the IP checksum, big-endian style
          parameter IPchecksum1 = 32'h0000C53F + (IPsource_1<<8)+IPsource_2+(IPsource_3<<8)+IPsource_4+
                                                                          (IPdestination_1<<8)+IPdestination_2+(IPdestination_3<<8)+(IPdestination_4);
          parameter IPchecksum2 =  ((IPchecksum1&32'h0000FFFF)+(IPchecksum1>>16));
          parameter IPchecksum3 = ~((IPchecksum2&32'h0000FFFF)+(IPchecksum2>>16));

          reg [6:0] rdaddress;
          reg [7:0] pkt_data;

          always @(posedge clk20)
          case(rdaddress)
          // Ethernet preamble
            7'h00: pkt_data <= 8'h55;
            7'h01: pkt_data <= 8'h55;
            7'h02: pkt_data <= 8'h55;
            7'h03: pkt_data <= 8'h55;
            7'h04: pkt_data <= 8'h55;
            7'h05: pkt_data <= 8'h55;
            7'h06: pkt_data <= 8'h55;
            7'h07: pkt_data <= 8'hD5;
          // Ethernet header
            7'h08: pkt_data <= PhysicalAddress_1;
            7'h09: pkt_data <= PhysicalAddress_2;
            7'h0A: pkt_data <= PhysicalAddress_3;
            7'h0B: pkt_data <= PhysicalAddress_4;
            7'h0C: pkt_data <= PhysicalAddress_5;
            7'h0D: pkt_data <= PhysicalAddress_6;
            7'h0E: pkt_data <= 8'h00;
            7'h0F: pkt_data <= 8'h12;
            7'h10: pkt_data <= 8'h34;
            7'h11: pkt_data <= 8'h56;
            7'h12: pkt_data <= 8'h78;
            7'h13: pkt_data <= 8'h90;
          // IP header
            7'h14: pkt_data <= 8'h08;
            7'h15: pkt_data <= 8'h00;
            7'h16: pkt_data <= 8'h45;
            7'h17: pkt_data <= 8'h00;
            7'h18: pkt_data <= 8'h00;
            7'h19: pkt_data <= 8'h2E;
            7'h1A: pkt_data <= 8'h00;
            7'h1B: pkt_data <= 8'h00;
            7'h1C: pkt_data <= 8'h00;
            7'h1D: pkt_data <= 8'h00;
            7'h1E: pkt_data <= 8'h80;
            7'h1F: pkt_data <= 8'h11;
            7'h20: pkt_data <= IPchecksum3[15:8];
            7'h21: pkt_data <= IPchecksum3[ 7:0];
            7'h22: pkt_data <= IPsource_1;
            7'h23: pkt_data <= IPsource_2;
            7'h24: pkt_data <= IPsource_3;
            7'h25: pkt_data <= IPsource_4;
            7'h26: pkt_data <= IPdestination_1;
            7'h27: pkt_data <= IPdestination_2;
            7'h28: pkt_data <= IPdestination_3;
            7'h29: pkt_data <= IPdestination_4;
          // UDP header
            7'h2A: pkt_data <= 8'h04;
            7'h2B: pkt_data <= 8'h00;
            7'h2C: pkt_data <= 8'h04;
            7'h2D: pkt_data <= 8'h00;
            7'h2E: pkt_data <= 8'h00;
            7'h2F: pkt_data <= 8'h1A;
            7'h30: pkt_data <= 8'h00;
            7'h31: pkt_data <= 8'h00;
          // payload
            7'h32: pkt_data <= 8'h00; // put here the data that you want to send
            7'h33: pkt_data <= 8'h01; // put here the data that you want to send
            7'h34: pkt_data <= 8'h02; // put here the data that you want to send
            7'h35: pkt_data <= 8'h03; // put here the data that you want to send
            7'h36: pkt_data <= 8'h04; // put here the data that you want to send
            7'h37: pkt_data <= 8'h05; // put here the data that you want to send
            7'h38: pkt_data <= 8'h06; // put here the data that you want to send
            7'h39: pkt_data <= 8'h07; // put here the data that you want to send
            7'h3A: pkt_data <= 8'h08; // put here the data that you want to send
            7'h3B: pkt_data <= 8'h09; // put here the data that you want to send
            7'h3C: pkt_data <= 8'h0A; // put here the data that you want to send
            7'h3D: pkt_data <= 8'h0B; // put here the data that you want to send
            7'h3E: pkt_data <= 8'h0C; // put here the data that you want to send
            7'h3F: pkt_data <= 8'h0D; // put here the data that you want to send
            7'h40: pkt_data <= 8'h0E; // put here the data that you want to send
            7'h41: pkt_data <= 8'h0F; // put here the data that you want to send
            7'h42: pkt_data <= 8'h10; // put here the data that you want to send
            7'h43: pkt_data <= 8'h11; // put here the data that you want to send
            default: pkt_data <= 8'h00;
          endcase

          //////////////////////////////////////////////////////////////////////
          // and finally the 10BASE-T's magic
          reg [3:0] ShiftCount;
          reg SendingPacket;
          always @(posedge clk20) if(StartSending) SendingPacket<=1; else if(ShiftCount==14 && rdaddress==7'h48) SendingPacket<=0;
          always @(posedge clk20) ShiftCount <= SendingPacket ? ShiftCount+1 : 15;
          wire readram = (ShiftCount==15);
          always @(posedge clk20) if(ShiftCount==15) rdaddress <= SendingPacket ? rdaddress+1 : 0;
          reg [7:0] ShiftData; always @(posedge clk20) if(ShiftCount[0]) ShiftData <= readram ? pkt_data : {1'b0, ShiftData[7:1]};

          // generate the CRC32
          reg [31:0] CRC;
          reg CRCflush; always @(posedge clk20) if(CRCflush) CRCflush <= SendingPacket; else if(readram) CRCflush <= (rdaddress==7'h44);
          reg CRCinit; always @(posedge clk20) if(readram) CRCinit <= (rdaddress==7);
          wire CRCinput = CRCflush ? 0 : (ShiftData[0] ^ CRC[31]);
          always @(posedge clk20) if(ShiftCount[0]) CRC <= CRCinit ? ~0 : ({CRC[30:0],1'b0} ^ ({32{CRCinput}} & 32'h04C11DB7));

          // generate the NLP
          reg [17:0] LinkPulseCount; always @(posedge clk20) LinkPulseCount <= SendingPacket ? 0 : LinkPulseCount+1;
          reg LinkPulse; always @(posedge clk20) LinkPulse <= &LinkPulseCount[17:1];

          // TP_IDL, shift-register and manchester encoder
          reg SendingPacketData; always @(posedge clk20) SendingPacketData <= SendingPacket;
          reg [2:0] idlecount; always @(posedge clk20) if(SendingPacketData) idlecount<=0; else if(~&idlecount) idlecount<=idlecount+1;
          wire dataout = CRCflush ? ~CRC[31] : ShiftData[0];
          reg qo; always @(posedge clk20) qo <= SendingPacketData ? ~dataout^ShiftCount[0] : 1;
          reg qoe; always @(posedge clk20) qoe <= SendingPacketData | LinkPulse | (idlecount<6);
          reg Ethernet_TDp; always @(posedge clk20) Ethernet_TDp <= (qoe ? qo : 1'b0);
          reg Ethernet_TDm; always @(posedge clk20) Ethernet_TDm <= (qoe ? ~qo : 1'b0);

          endmodule

          About the "IP source" that you have to choose in the code above, pick something that is compatible with your network, but still unused.
          The example network shown above have IPs starting with "192.168.0" (the PC IP is 192.168.0.2 with a mask of 255.255.255.0). So here IPs like 192.168.0.x can be used. To check if an IP is used or not, "ping" it.

          4. 傳入數(shù)據(jù)包

          在 PC 上運(yùn)行此 UDP 接收器軟件(包括源代碼)。

          你會(huì)得到這樣的東西:

          發(fā)送有用的流量

          上面的代碼在每個(gè) UDP 數(shù)據(jù)包中發(fā)送 18 個(gè)數(shù)據(jù)字節(jié)。 這 18 個(gè)字節(jié)可以來自任何地方,因此,例如,您可以修改代碼以發(fā)送 FPGA 引腳的值。

          玩得開心發(fā)送UDP數(shù)據(jù)包!

          10BASE-T FPGA 接口 1 - 以太網(wǎng)的工作原理

          這是對(duì)以太網(wǎng)技術(shù)的簡(jiǎn)要介紹。
          如果你是新手,你可以從Charles Spurgeon的以太網(wǎng)網(wǎng)站上獲得更多細(xì)節(jié)。

          此頁面上的評(píng)論同樣適用于 10BASE-T 和 100BASE-T(后者快 10 倍)。

          IEEE 802.3協(xié)議

          IEEE 10.100 標(biāo)準(zhǔn)中描述了 802/3BASE-T 接口。
          該標(biāo)準(zhǔn)可在 IEEE 802.3 標(biāo)準(zhǔn)協(xié)會(huì)頁面上免費(fèi)獲得。 如果您想要副本,請(qǐng)選擇最新標(biāo)準(zhǔn)“IEEE 802.3-2002”。 相關(guān)章節(jié)包括第3章(MAC幀結(jié)構(gòu))和第14章(10BASE-T)。

          RJ-45 連接器

          10/100BASE-T 使用 RJ-45 8 針連接器。

          它們看起來像這樣:


          在 8 個(gè)引腳中,只使用了 4 個(gè):
          1. TD+(傳輸+)

          2. TD-(傳輸-)

          3. RD+(接收+)



          4. RD-(接收-)



          一對(duì)引腳用于發(fā)送(TD+/TD-),一對(duì)用于接收(RD+/RD-)。

          注意:此引腳排列適用于從計(jì)算機(jī)輸出的 RJ-45。 集線器或交換機(jī)的引腳排列是反轉(zhuǎn)的(TD在引腳3和6上,RD在引腳1和2上)。

          差分信號(hào)

          每對(duì)都使用差分電信號(hào)(差分信號(hào)對(duì)外部干擾的免疫力更強(qiáng))。 此外,每對(duì)線都絞合在電纜中,這進(jìn)一步提高了抗擾度。

          數(shù)據(jù)包編碼

          要在以太網(wǎng)上發(fā)送數(shù)據(jù),您不能就這樣發(fā)送;您必須將其封裝到以太網(wǎng)數(shù)據(jù)包中。 數(shù)據(jù)包包含一個(gè)標(biāo)頭,其中包含數(shù)據(jù)到達(dá)目的地所需的信息。

          以太網(wǎng)基于共享介質(zhì)的理念 - 如果一個(gè)站點(diǎn)發(fā)送數(shù)據(jù)包,線路上的每個(gè)人都會(huì)收到它。 每個(gè)以太網(wǎng)卡都有一個(gè)唯一的ID(“MAC地址”),因此每個(gè)卡都可以自動(dòng)丟棄發(fā)往另一個(gè)站點(diǎn)的數(shù)據(jù)包。 MAC 地址長(zhǎng)度為 6 個(gè)字節(jié)(48 位),足以讓地球上的每個(gè)以太網(wǎng)卡都有一個(gè)唯一的編號(hào)!

          半雙工與全雙工

          以太網(wǎng)最初是使用真正的共享介質(zhì)(連接到多個(gè)站點(diǎn)的單根同軸電纜)構(gòu)建的。 傳輸和接收都使用同一根電纜。 所以當(dāng)然,你不能同時(shí)發(fā)送和接收。通信是半雙工的。

          半雙工使用稱為“CSMA/CD”(帶沖突檢測(cè)的載波偵聽多址)的協(xié)議: 在半雙工中:
          • 在發(fā)射之前,每個(gè)電臺(tái)都必須監(jiān)聽以確保線路是空閑的(“載波檢測(cè)”)。

          • 盡管如此,兩個(gè)電臺(tái)仍有可能同時(shí)傳輸。 因此,必須存在一個(gè)復(fù)雜的協(xié)議來中止(“沖突檢測(cè)”)并在以后恢復(fù)傳輸。

          10/100BASE-T 使用“非屏蔽雙絞線”電纜(“UTP”電纜)代替同軸電纜。 UTP 電纜允許全雙工,因?yàn)樗鼈儼糜趥鬏敽徒邮盏膯为?dú)線對(duì)。

          全雙工通信更好:
          • 您可以獲得兩倍的帶寬。

          • 每個(gè)站點(diǎn)都有一個(gè)專用的介質(zhì),可以隨時(shí)開始傳輸,而不會(huì)出現(xiàn)并發(fā)癥(CSMA/CD 不再適用)。

          所以現(xiàn)在的問題是,如何讓您的 10/100BASE-T 網(wǎng)絡(luò)在全雙工模式下工作?

          集線器和交換機(jī)

          10/100BASE-T是一個(gè)“星形拓?fù)洹本W(wǎng)絡(luò)。 它需要使用集中器設(shè)備將多臺(tái)計(jì)算機(jī)連接在一起。

          有兩種類型的集中器可用:“集線器”或“交換機(jī)”。

          • 集線器是一種簡(jiǎn)單的電子設(shè)備,它在每個(gè)鏈路之間提供電氣隔離,但仍然將它們“邏輯”地連接在一起。 這迫使通信是半雙工的。 更糟糕的是:在任何給定時(shí)間,只有一臺(tái)計(jì)算機(jī)可以說話。 因此,網(wǎng)絡(luò)帶寬在所有計(jì)算機(jī)之間共享。

          • 交換機(jī)(或“交換機(jī)集線器”)是一種更復(fù)雜的電子設(shè)備,它在電氣和邏輯上隔離每個(gè)計(jì)算機(jī)鏈路。 它通過在重新傳輸之前在內(nèi)部存儲(chǔ)每個(gè)傳輸?shù)臄?shù)據(jù)來做到這一點(diǎn)。 因此,每臺(tái)計(jì)算機(jī)都可以隨時(shí)說話:這允許全雙工通信。 更好的是:每臺(tái)計(jì)算機(jī)都具有完整的鏈路帶寬。 而且由于介質(zhì)不共享,因此每個(gè)站點(diǎn)僅接收發(fā)給自己的數(shù)據(jù)包(隱私性得到改善)。

          總之,對(duì)于 10BASE-T 網(wǎng)絡(luò)(將數(shù)字乘以 10,表示 100BASE-T):
          • 集線器:每個(gè)鏈路上的半雙工,所有計(jì)算機(jī)之間共享 10Mbps。慢。。。

          • 交換:每個(gè)鏈路上的全雙工,每臺(tái)計(jì)算機(jī)專用 20Mbps(單向 10Mbps)???!

          開關(guān)可能會(huì)花費(fèi)更多,但這是非常值得的!

          此項(xiàng)目建議使用全雙工鏈路,因?yàn)樗粚?shí)現(xiàn) CSMA/CD。 使用半雙工鏈路仍然有效,但代價(jià)是潛在的數(shù)據(jù)包丟失(尤其是在使用集線器共享介質(zhì)時(shí))。

          10BASE-T FPGA 接口 2 - 基于以太網(wǎng)的 IP/UDP

          讓我們專注于以太網(wǎng)/IP/UDP 數(shù)據(jù)包。
          這些數(shù)據(jù)包易于生產(chǎn);但功能強(qiáng)大,它們可以在互聯(lián)網(wǎng)上傳播(并被發(fā)送到世界任何地方!

          下面是一個(gè)示例:

          55 55 55 55 55 55 55 D5 00 10 A4 7B EA 80 00 12 34 56 78 90 08 00 45 00 00 2E B3 FE 00 00 80 11 05 40 C0 A8 00 2C C0 A8 00 04 04 00 04 00 00 1A 2D E8 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 B3 31 88 1B

          繼續(xù)閱讀,它在下面解碼。

          協(xié)議棧

          數(shù)據(jù)包使用三種不同的協(xié)議:以太網(wǎng)、IP 和 UDP(從低級(jí)到高級(jí)協(xié)議)。
          每個(gè)協(xié)議都添加了自己的功能,并嵌入到較低級(jí)別的協(xié)議中。
          • UDP 部分包含要發(fā)送的數(shù)據(jù)(“有效負(fù)載”)。

          • IP 部分允許數(shù)據(jù)包通過 Internet 路由。

          • 以太網(wǎng)部分允許數(shù)據(jù)包在以太網(wǎng)上本地發(fā)送。

          UDP 嵌入到 IP 中,而 IP 本身也嵌入到以太網(wǎng)中。 這是如何一起使用網(wǎng)絡(luò)協(xié)議的一個(gè)很好的例子。
          上面顯示的數(shù)據(jù)包在這里解碼:
          • 以太網(wǎng)前導(dǎo)碼/SFD(同步器):55 55 55 55 55 55 D55

          • 以太網(wǎng)目標(biāo)地址:00 10 A4 7B EA 80

          • 以太網(wǎng)源地址:00 12 34 56 78 90

          • 以太網(wǎng)類型: 08 00 (=IP)

          • IP 標(biāo)頭:45 00 00 2E B3 FE 00 00 80

          • IP 協(xié)議:11 (=UDP)

          • IP 校驗(yàn)和: 05 40

          • IP 源 (192.168.0.44): C0 A8 00 2C

          • IP 目標(biāo) (192.168.0.4): C0 A8 00 04

          • UPD 源端口 (1024):04 00

          • UPD 目標(biāo)端口 (1024):04 00

          • UDP 有效載荷長(zhǎng)度 (18): 00 1A

          • UPD 校驗(yàn)和: 2D E8

          • UDP 有效負(fù)載(18 字節(jié)):00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11

          • 以太網(wǎng)校驗(yàn)和: B3 31 88 1B

          您可以注意到使用的 IP:從“192.168.0.44”到“192.168.0.4”。 這些是本地IP,這個(gè)數(shù)據(jù)包不會(huì)走得太遠(yuǎn)......

          創(chuàng)建自己的數(shù)據(jù)包

          這是一個(gè)簡(jiǎn)單的軟件,允許創(chuàng)建自定義以太網(wǎng)/IP/UDP 數(shù)據(jù)包。
          在此處下載。源代碼包括在內(nèi)(目前是 Delphi 6 - 在 Delphi XE2 中要小心,因?yàn)橐粋€(gè)用戶報(bào)告了錯(cuò)誤生成的數(shù)據(jù)包......



          每次按下“發(fā)送”按鈕時(shí),軟件都會(huì)構(gòu)建一個(gè)數(shù)據(jù)包,顯示它并將其發(fā)送到串行端口(這將在本項(xiàng)目的第 3 部分后面有用)。

          10BASE-T FPGA 接口 3 - 發(fā)送數(shù)據(jù)包

          現(xiàn)在我們知道需要傳輸什么,讓我們開始吧。
          第 2 部分中的軟件將原始以太網(wǎng)數(shù)據(jù)包數(shù)據(jù)發(fā)送到 PC 的串行端口。
          我們的FPGA板只需要從串行端口接收它(慢速...),然后通過10BASE-T線對(duì)發(fā)送(快?。?。

          讓 PC 計(jì)算原始數(shù)據(jù)包數(shù)據(jù)使我們能夠非常輕松地對(duì)所有數(shù)據(jù)包參數(shù)進(jìn)行實(shí)驗(yàn)。 這很好,但最終FPGA應(yīng)該在獨(dú)立模式下工作,并自行烹飪/發(fā)送數(shù)據(jù)包。

          曼徹斯特編碼

          10BASE-T 網(wǎng)絡(luò)以 10Mbps(每秒 10 兆比特)的速度工作。

          但 10BASE-T 要求這些位是“曼徹斯特編碼”的。這需要將位數(shù)增加一倍!
          • 邏輯“0”作為高位發(fā)送,后跟低位。

          • 邏輯“1”作為低位發(fā)送,后跟高位。

          因此,我們的 10Mbps 網(wǎng)絡(luò)實(shí)際上需要 20Mbps 的比特流......

          FPGA 時(shí)鐘

          簡(jiǎn)單性決定了我們使用10MHz時(shí)鐘,并通過將時(shí)鐘與輸出數(shù)據(jù)位“xXing”來生成20Mbps曼徹斯特比特流。

          那可能行得通。但我們正在把握時(shí)間。在FPGA設(shè)計(jì)中不建議這樣做。 在這種特殊情況下,這會(huì)降低20Mbps的信號(hào)質(zhì)量,因?yàn)槊總€(gè)位轉(zhuǎn)換都會(huì)出現(xiàn)毛刺(數(shù)據(jù)輸出永遠(yuǎn)不會(huì)與時(shí)鐘完全重合)。

          因此,讓我們使用20MHz時(shí)鐘。

          保持鏈接處于活動(dòng)狀態(tài)

          即使 10BASE-T 電纜上沒有發(fā)送數(shù)據(jù)包,也必須定期發(fā)送脈沖(稱為“正常鏈路脈沖”或“NLP”)。 它用于保持連接“活動(dòng)”。每 16 毫秒左右需要發(fā)送一次脈沖。

          NLP也可以在稱為“自動(dòng)協(xié)商”的過程中被“快速鏈接脈沖”(FLP)突發(fā)所取代。 FLP 攜帶有關(guān)發(fā)送方功能的信息,以便電纜兩端的硬件可以協(xié)商鏈路參數(shù),例如速度和半雙工/全雙工狀態(tài)。

          HDL設(shè)計(jì)

          假設(shè)要發(fā)送的數(shù)據(jù)包在FPGA的RAM中可用。
          ram512 ram(
            .data(ram_input), .wraddress(wraddress), .clock(clk),
            .q(ram_output), .rdaddress(rdaddress)
          );

          我們假設(shè)我們也知道數(shù)據(jù)包的長(zhǎng)度。我們將讀取數(shù)據(jù)包數(shù)據(jù)并將其發(fā)送到以太網(wǎng)上。
          首先,我們需要一個(gè)開始信號(hào)。
          wire StartSending; // pulse indicating when to start sending the packet

          reg SendingPacket;
          always @(posedge clk) if(StartSending) SendingPacket<=1; else if(DoneSending) SendingPacket<=0;

          在 20MHz 時(shí),我們要求每個(gè)位有 2 個(gè)時(shí)鐘周期。一個(gè) 16 位字節(jié)需要 8 個(gè)時(shí)鐘。
          reg [3:0] ShiftCount; // count from 0 to 15, as 16 clocks are required per 8-bits bytes
          always @(posedge clk) if(SendingPacket) ShiftCount <= ShiftCount+1; else ShiftCount <= 0;

          當(dāng)我們到達(dá)字節(jié)的最后一位時(shí),我們從RAM中讀取一個(gè)新字節(jié)并將其放入移位寄存器中。 移位寄存器每隔一個(gè)時(shí)鐘就會(huì)給我們一個(gè)新位。
          wire readram = (ShiftCount==15); // time to read a new byte from the RAM?
          reg [7:0] ShiftData;
          always @(posedge clk) if(ShiftCount[0]) ShiftData <= readram ? ram_output : {1'b0, ShiftData[7:1]};

          always @(posedge clk) if(readram) rdaddress <= SendingPacket ? rdaddress+1 : 0;

          每個(gè)數(shù)據(jù)包都需要以“TP_IDL”結(jié)尾(大約 3 位時(shí)間的正脈沖,然后是空閑期)。
          reg [2:0] idlecount; // enough bits to count 3 bit-times

          always @(posedge clk) if(SendingPacket) idlecount<=0; 

          else if(~&idlecount) idlecount<=idlecount+1;


          最后,曼徹斯特編碼器發(fā)送位,然后發(fā)送TP_IDL。
          reg qo; always @(posedge clk) qo <= SendingPacket ? ~ShiftData[0]^ShiftCount[0] : 1;
          reg qoe; always @(posedge clk) qoe <= SendingPacket | (idlecount<6);
          reg q1; always @(posedge clk) q1 <= (qoe ? qo : 1'b0);
          reg q2; always @(posedge clk) q2 <= (qoe ? ~qo : 1'b0);

          此處顯示的代碼略有簡(jiǎn)化。 例如,缺少保持鏈接活動(dòng)所需的 NLP 脈沖。 完整的代碼可以在這里找到。

          此代碼適用于集線器和交換機(jī)。 由于我們只使用了 NLP,因此鏈路是半雙工的,當(dāng)發(fā)生沖突時(shí)可能會(huì)丟失數(shù)據(jù)包。
          交換機(jī)顯示的丟棄很少 - 由于我們只在這里傳輸,除非另一個(gè)電臺(tái)發(fā)送廣播數(shù)據(jù)包,否則永遠(yuǎn)不會(huì)發(fā)生沖突(可以通過使用 FLP 脈沖將鏈路帶入全雙工來修復(fù))。
          由于發(fā)生沖突的可能性很高,集線器顯示大量丟包。

          傳入數(shù)據(jù)包

          FPGA 發(fā)送 UDP 數(shù)據(jù)包。 但是你如何檢測(cè)它們呢?

          這是一個(gè)簡(jiǎn)單的 UDP 測(cè)試軟件,可以在 PC 上發(fā)送和/或接收 UDP 數(shù)據(jù)包(使用 PC 的以太網(wǎng)網(wǎng)絡(luò)適配器)。
          • 軟件偵聽端口 1024。 如果它在此端口上收到數(shù)據(jù)包,則會(huì)顯示它獲得了多少字節(jié)。

          • 該軟件還可以在端口 1024 上發(fā)送。只需按下“發(fā)送 UDP”按鈕即可。



          您可以向自己發(fā)送數(shù)據(jù)包 - 只需使用您機(jī)器自己的 IP。 但在這種情況下,數(shù)據(jù)包是在內(nèi)部路由的,永遠(yuǎn)沒有機(jī)會(huì)進(jìn)入網(wǎng)絡(luò),所以這不太有用。
          要查找 PC 的 IP 或以太網(wǎng) MAC 地址,您可以在命令行中鍵入“ipconfig /all”。

          請(qǐng)注意,端口值 1024 在軟件中是硬編碼的(在 GUI 中只能配置目標(biāo) IP 和有效負(fù)載長(zhǎng)度)。 這在實(shí)踐中不是問題,因?yàn)槟挠?jì)算機(jī)不太可能有另一個(gè)應(yīng)用程序已經(jīng)在偵聽此特定端口。

          10BASE-T FPGA 接口 4 - 接收數(shù)據(jù)包

          接收器可用于 2 件事:
          • 在網(wǎng)絡(luò)上創(chuàng)建合法端口。

          • “嗅探”網(wǎng)絡(luò)(監(jiān)視數(shù)據(jù)包) - 只需將接收器并行連接到另一個(gè)連接即可。

          以下是我在本地網(wǎng)絡(luò)上嗅探的數(shù)據(jù)包示例:

          55 55 55 55 55 55 55 D5 00 C0 02 37 57 28 00 10 A4 7B EA 80 08 00 45 00 00 3C 02 24 00 00 80 01 B7 47 C0 A8 00 04 C0 A8 00 01 08 00 42 5C 02 00 09 00 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 61 62 63 64 65 66 67 68 69 62 31 C5 4E

          我會(huì)讓你剖析它(ping 從“192.168.0.4”到“192.168.0.1”)。

          10BASE-T接收器

          10BASE-T 接收器由以下部分組成:
          • 差分接收器電路(簡(jiǎn)單)。

          • 時(shí)鐘提取電路(困難部分)。

          • 前導(dǎo)碼同步器、解串器和以太網(wǎng)校驗(yàn)和檢查器(在FPGA中輕松完成)。

          差分輸入

          我們接收來自 RD+/RD- 的差分(2 線)信號(hào)。
          信號(hào)通常是磁耦合的(使用一個(gè)小變壓器), 然后使用集成比較器或幾個(gè)晶體管轉(zhuǎn)換為共模信號(hào)(1 線加接地)。

          我手上沒有變壓器,也沒有足夠快的集成比較器。 因此,我使用了電容耦合方案,然后是晶體管。

          我的電路原理圖是:


          第二個(gè)晶體管集電極上的2K電阻高于第一個(gè)晶體管(1K)上的值,以幫助在輸出端獲得50%的占空比信號(hào)。 否則,輸出端的信號(hào)不是方形的......

          關(guān)于電容耦合:我相信它與電感耦合一樣好用,但有一個(gè)缺點(diǎn): 當(dāng)您將電纜插入接收器時(shí),接收器和驅(qū)動(dòng)器之間的電壓電位差可能會(huì)向接收器發(fā)送電流脈沖 (如果驅(qū)動(dòng)器沒有被變壓器隔離)。 只是我的想法,我在任何地方都沒有找到任何相關(guān)信息。有人有更多信息嗎?

          時(shí)鐘提取

          共模數(shù)據(jù)仍采用曼徹斯特編碼。
          曼徹斯特編碼方案的工作方式是始終在邏輯位的中間創(chuàng)建轉(zhuǎn)換。 請(qǐng)記?。?/span>
          • 邏輯“0”以高電平(50ns)后跟低電平(50ns)(故障沿)的形式發(fā)送。

          • 邏輯“1”以低電平(50ns)發(fā)送,后跟高電平(50ns)(上升沿)。

          這意味著轉(zhuǎn)換也可能出現(xiàn)在 2 位之間(如果同一邏輯位連續(xù)發(fā)送兩次)。

          我知道有三種基本方法可以進(jìn)行時(shí)鐘提?。?/span>
          • 使用更快的時(shí)鐘對(duì)編碼信號(hào)進(jìn)行過采樣,并使用常規(guī)FPGA邏輯(邊沿檢測(cè)器和狀態(tài)機(jī))提取位。

          • 使用帶有改進(jìn)的相位比較器電路的PLL來重新生成發(fā)送器使用的時(shí)鐘。

          • 使用固定延遲不可重新觸發(fā)的單穩(wěn)態(tài),其持續(xù)時(shí)間為 1.5 編碼位(75BASE-T 為 10ns)。 單穩(wěn)態(tài)由中間位轉(zhuǎn)換觸發(fā),并忽略位間距轉(zhuǎn)換。

          我打算嘗試這三種技術(shù)?,F(xiàn)在,讓我們嘗試第一個(gè)。

          對(duì)信號(hào)進(jìn)行過采樣

          這就是“蠻力”方法。 優(yōu)點(diǎn)是完整的解碼是在FPGA中完成的。 不方便的是你需要一個(gè)高頻時(shí)鐘。
          位提取
          我決定使用48MHz的采樣頻率。
          進(jìn)行曼徹斯特解碼需要 3 個(gè)步驟。
          1. 對(duì)傳入的數(shù)據(jù)進(jìn)行采樣和同步。

          2. 檢測(cè)邊緣并啟動(dòng)計(jì)數(shù)器。訣竅在于,計(jì)數(shù)器被制作成忽略任何后續(xù)邊沿,直到它翻轉(zhuǎn)(僅檢測(cè)曼徹斯特中位轉(zhuǎn)換)。

          3. 一旦檢測(cè)到邊沿,下一個(gè)位在 3 個(gè)計(jì)數(shù)后可用 (在48MHz時(shí),周期約為21ns,因此三次計(jì)數(shù)為63ns,就在下一個(gè)位的中間)。 我們將每個(gè)位移入一個(gè) 8 位移位寄存器。

          reg [2:0] in_data;
          always @(posedge clk48) in_data <= {in_data[1:0], manchester_data_in};

          reg [1:0] cnt;
          always @(posedge clk48) if(|cnt || (in_data[2] ^ in_data[1])) cnt<=cnt+1;

          reg [7:0] data;
          reg new_bit_avail;
          always @(posedge clk48) new_bit_avail <= (cnt==3);
          always @(posedge clk48) if(cnt==3) data<={in_data[1],data[7:1]};

          位進(jìn)來了!
          前導(dǎo)碼/SFD同步
          到目前為止,我們只有比特同步(我們知道每個(gè)比特何時(shí)到來,以及它的值)。 我們知道一個(gè)字節(jié)每 8 位開始一次,但從哪位開始呢?

          為了允許字節(jié)同步,以太網(wǎng)幀以以下 8 字節(jié)序列開頭:
          55 55 55 55 55 55 55 D5

          在二進(jìn)制中,0x55 是01010101,而 0xD5 是11010101。 此外,以太網(wǎng)指定首先發(fā)送 LSB(例如,0xD5 以 1、0、1、0、1、0、1、1 的形式發(fā)送)。 因此,我們收到 1 和 0 的交替模式,一旦我們檢測(cè)到 2 個(gè)連續(xù)的 1,我們就知道接下來是真正的數(shù)據(jù)。

          reg end_of_Ethernet_frame;

          reg [4:0] sync1;
          always @(posedge clk48)
          if(end_of_Ethernet_frame)
            sync1 <= 0;
          else
          if(new_bit_avail)
          begin
            if(!(data==8'h55 || data==8'hAA)) // not preamble?
              sync1 <= 0;
            else
            if(~&sync1) // if all bits of this "sync1" counter are one, we decide that enough of the preamble
                            // has been received, so stop counting and wait for "sync2" to detect the SFD
              sync1 <= sync1 + 1; // otherwise keep counting
          end

          reg [9:0] sync2;
          always @(posedge clk48)
          if(end_of_Ethernet_frame)
            sync2 <= 0;
          else
          if(new_bit_avail)
          begin
            if(|sync2) // if the SFD has already been detected (Ethernet data is coming in)
              sync2 <= sync2 + 1; // then count the bits coming in
            else
            if(&sync1 && data==8'hD5) // otherwise, let's wait for the SFD (0xD5)
              sync2 <= sync2 + 1;
          end

          wire new_byte_available = new_bit_avail && (sync2[2:0]==3'h0) && (sync2[9:3]!=0);

          終于,以太網(wǎng)數(shù)據(jù)正在涌入!
          幀結(jié)束
          如果在一段時(shí)間內(nèi)未檢測(cè)到時(shí)鐘轉(zhuǎn)換,則以太網(wǎng)幀結(jié)束。
          reg [2:0] transition_timeout;

          always @(posedge clk48)
          if(in_data[2]^in_data[1]) // transition detected?
            transition_timeout <= 0;
          else
          if(~&cnt)
            transition_timeout <= transition_timeout + 1;

          always @(posedge clk48) end_of_Ethernet_frame <= &transition_timeout;
          結(jié)束語
          獨(dú)立應(yīng)用程序需要檢查CRC(位于以太網(wǎng)數(shù)據(jù)包的末尾)是否存在錯(cuò)誤的傳輸錯(cuò)誤。
          在這里,我只是將完整的數(shù)據(jù)發(fā)送到 PC - 它顯示它或做任何它喜歡的事情。

          wire [7:0] q_fifo;
          fifo myfifo(.data(data), .wrreq(new_byte_available), .wrclk(clk48),
                          .q(q_fifo), .rdreq(rdreq), .rdclk(clk), .rdempty(rdempty));

          wire TxD_busy;
          wire TxD_start = ~TxD_busy & ~rdempty;
          assign rdreq = TxD_start;

          async_transmitter async_txd(.clk(clk), .TxD(TxD), .TxD_start(TxD_start), .TxD_busy(TxD_busy), .TxD_data(q_fifo));


          關(guān)鍵詞: FPGA Ethernet接口

          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();