FPGA:PCI Express接口
隨著 PCI Express 在高端 FPGA 中變得司空見慣,讓我們看看 FPGA 供應(yīng)商如何輕松實(shí)現(xiàn)該技術(shù)。特別是,我們更仔細(xì)地研究了賽靈思的 PCI Express 解決方案。
本文引用地址:http://m.butianyuan.cn/article/202401/454643.htmPCI Express 1 - 連接器
PCI Express 通常有兩種尺寸:1 通道和 16 通道,其中 1 通道用于普通主板,16 通道用于顯卡。
連接器
1 通道連接器有 36 個(gè)觸點(diǎn),排列成兩排,每排 18 個(gè)觸點(diǎn)。
這是俯視圖。
在 36 個(gè)觸點(diǎn)中,只有 6 個(gè)對(duì)數(shù)據(jù)傳輸有用,其余是電源引腳和其他輔助信號(hào)。 6 個(gè)功能觸點(diǎn)以 3 對(duì)使用:
名為 REFCLK 的時(shí)鐘對(duì)。
名為 PER 的接收對(duì)。
稱為 PET 的傳輸對(duì)。
這些信號(hào)對(duì)通常被稱為“差分對(duì)”,因?yàn)閬?lái)自一對(duì)的每個(gè)信號(hào)都攜帶相同的信號(hào),但一個(gè)信號(hào)與另一個(gè)信號(hào)相反。 使用差分對(duì)的原因主要是傳輸?shù)目煽啃?,稍后將更詳?xì)地討論。
在 PCI Express 第 1 代(或簡(jiǎn)稱為“Gen1”)中,PET 和 PER 對(duì)的數(shù)據(jù)傳輸速度為 2.5Gbps。 Gen2 將這一數(shù)字翻了一番。
查看 Dragon-E 板,我們可以識(shí)別出 FPGA 下方的 PET 對(duì)。
為了正常工作,差分對(duì)中的線路需要電耦合并且沒有阻抗不連續(xù)性,這在實(shí)踐中意味著“保持緊密”和“沒有銳角”。 這就是 Dragon-E 的 PET 對(duì)蛇形形狀的原因。 板的另一側(cè)顯示了另外兩對(duì)蛇形對(duì) REFCLK 和 PER。
PCI Express x16 插槽
為了提高速度,可以使用多條車道。 不需要復(fù)制 REFCLK 對(duì),例如,具有 2 個(gè)通道的 PCI Express 使用 5 個(gè)對(duì)(1 個(gè) REFCLK + 2 個(gè) PET + 2 個(gè) PER)。
圖形板通常使用 16 通道連接器,通常稱為 PCI Express x16。
PCI Express 2 - 拓?fù)?/span>
點(diǎn)對(duì)點(diǎn)架構(gòu)
在 2.5Gsps 時(shí),PCI Express Gen1 線路速度比 75MHz 傳統(tǒng) PCI 速度快 33 倍。
這怎么可能?只是因?yàn)?PCI express 是點(diǎn)對(duì)點(diǎn)總線。
還記得PCI是共享總線嗎?
使用PCI時(shí),必須指定足夠的時(shí)間,讓信號(hào)在每個(gè)時(shí)鐘周期內(nèi)穩(wěn)定下來(lái)。 這是因?yàn)镻CI總線的每條線路都在同一總線上的PCI連接器和板上共享。 使用PCI Express,每個(gè)信號(hào)都是點(diǎn)對(duì)點(diǎn)的,這意味著不再有建立時(shí)間,線路速度可以更高。
例如,如果主板有兩個(gè) 1 通道連接器和一個(gè) 16 通道連接器,則需要橋接器上有 6+6+34=46 個(gè)引腳,僅用于 REFCLK、PER 和 PET(因?yàn)椴辉试S共享)。
時(shí)鐘恢復(fù)
在2.5GHz開始的速度下,點(diǎn)對(duì)點(diǎn)架構(gòu)仍然是一個(gè)挑戰(zhàn),因?yàn)槊總€(gè)位的持續(xù)時(shí)間非常短,以至于時(shí)序抖動(dòng)(圍繞每個(gè)位到達(dá)的時(shí)間不確定性)成為一個(gè)問(wèn)題。 即使每個(gè)信號(hào)對(duì)都有相關(guān)的時(shí)鐘對(duì)同時(shí)傳輸,時(shí)鐘對(duì)也會(huì)受到定時(shí)抖動(dòng)的影響。 因此,使用了一種稱為“時(shí)鐘恢復(fù)”的新技術(shù)。
時(shí)鐘恢復(fù)很簡(jiǎn)單。 基本上,對(duì)于每個(gè)信號(hào)對(duì),接收器對(duì)都會(huì)查看信號(hào)轉(zhuǎn)換(位 0 后跟位 1,反之亦然),從中可以推斷出周圍位的位置。 一個(gè)問(wèn)題是,如果許多連續(xù)的位以相同的值傳輸(如許多0),則看不到信號(hào)轉(zhuǎn)換。 因此,傳輸額外的位以確保信號(hào)轉(zhuǎn)換不會(huì)相距太遠(yuǎn)(這會(huì)“重新同步”時(shí)鐘恢復(fù)機(jī)制)。
額外的比特使用一種稱為 8b/10b 編碼的方案發(fā)送,因此對(duì)于每 8 位有用數(shù)據(jù),實(shí)際上有 10 位以特定方式傳輸(開銷為 20%),以保證足夠的信號(hào)轉(zhuǎn)換。 但這也意味著,在2.5GHz時(shí),我們每對(duì)只能獲得250MBps的有用帶寬(而不是沒有編碼開銷的312MBps)。
差分對(duì)
現(xiàn)在還記得信號(hào)是在差分對(duì)上發(fā)送的事實(shí)嗎? 這有很多優(yōu)點(diǎn):
它更不受外部干擾。
它能夠在低電壓下工作(=更低的功耗)。
...最后但并非最不重要的一點(diǎn)是:這有助于時(shí)鐘恢復(fù)獲得精確的信號(hào)轉(zhuǎn)換。
差分對(duì)有一個(gè)明顯的缺點(diǎn):傳輸信號(hào)需要兩倍的導(dǎo)線。
PCI Express 3 - 數(shù)據(jù)包、堆棧和網(wǎng)絡(luò)的故事
分組事務(wù)
PCI express 是串行總線。 或者是嗎? 從計(jì)算機(jī)的角度來(lái)看,它是一種可以實(shí)現(xiàn)讀寫事務(wù)的傳統(tǒng)總線。
訣竅是所有操作都已打包。 假設(shè) CPU 想要將一些數(shù)據(jù)寫入設(shè)備。 它將訂單轉(zhuǎn)發(fā)到 PCI Express 網(wǎng)橋,然后 PCI Express 網(wǎng)橋創(chuàng)建一個(gè)數(shù)據(jù)包。 數(shù)據(jù)包包含要寫入的地址和數(shù)據(jù),并串行轉(zhuǎn)發(fā)到目標(biāo)設(shè)備,目標(biāo)設(shè)備將寫入順序解包并執(zhí)行。
如果 CPU 想要讀取怎么辦? 同樣,網(wǎng)橋?qū)?shù)據(jù)包轉(zhuǎn)發(fā)到目標(biāo)設(shè)備,目標(biāo)設(shè)備現(xiàn)在必須執(zhí)行讀取,創(chuàng)建返回?cái)?shù)據(jù)包并將其發(fā)送到網(wǎng)橋。
所有這一切都在實(shí)踐中非常容易做到,感謝來(lái)自...
PCI Express 協(xié)議棧
讓數(shù)據(jù)包沿著線路可靠地流動(dòng)需要一些魔力。 由于數(shù)據(jù)包以非常高的速度串行傳輸,因此必須對(duì)它們進(jìn)行反序列化/匯編、在目的地解碼(刪除 8b/10b 編碼)、去交錯(cuò)(如果使用多個(gè)通道)并檢查線路損壞(CRC 檢查)。
聽起來(lái)很復(fù)雜? 大概是這樣。 問(wèn)題是,我們并不真正關(guān)心,因?yàn)榇蟛糠謴?fù)雜性都是在由三層組成的“PCI Express堆?!敝刑幚淼?。
物理層。
數(shù)據(jù)鏈路層。
事務(wù)層。
前兩層是在PCI Express FPGA內(nèi)核(通常是硬核和軟核的組合)中為我們實(shí)現(xiàn)的,用于處理所有復(fù)雜性。 作為用戶,我們只在交易層工作,那里的生活很輕松,天空很藍(lán),女孩很漂亮。
更多細(xì)節(jié):
物理層:這是引腳切換的地方。8b/10b 編碼/解碼和通道拆卸/重組都在那里完成。
數(shù)據(jù)鏈路層:檢查數(shù)據(jù)完整性 (CRC) 并在需要時(shí)重新傳輸數(shù)據(jù)包(希望這種情況很少發(fā)生)。
交易層:即用戶級(jí)別。一旦數(shù)據(jù)包到達(dá)這里,它就可以保證是好數(shù)據(jù)。
好數(shù)據(jù)?太好了,這就是我們想要的!
讓我們看看在事務(wù)層中工作是什么樣的。
PCI Express 4 - 事務(wù)層
在交易層,我們接收“數(shù)據(jù)包”。 有一個(gè) 32 位總線,數(shù)據(jù)包到達(dá)總線(數(shù)據(jù)包長(zhǎng)度始終是 32 位的倍數(shù))。 也許一個(gè)數(shù)據(jù)包會(huì)說(shuō)“在地址0xABCD寫入數(shù)據(jù)1234x0”,另一個(gè)數(shù)據(jù)包會(huì)說(shuō)“從地址0xDCBA讀?。ú⒎祷仨憫?yīng)數(shù)據(jù)包)”。
數(shù)據(jù)包有很多種類型:內(nèi)存讀取、內(nèi)存寫入、I/O 讀取、I/O 寫入、消息、完成等...... 我們?cè)谑聞?wù)層的工作是接受數(shù)據(jù)包和發(fā)出數(shù)據(jù)包。 數(shù)據(jù)包以稱為“事務(wù)層數(shù)據(jù)包”(TLP)的特定格式呈現(xiàn)給我們,到達(dá)總線的每個(gè) 32 位數(shù)據(jù)稱為“雙字”(或簡(jiǎn)稱 DW)。
所以一個(gè)數(shù)據(jù)包(哎呀,對(duì)不起,一個(gè) TLP)是一堆 DW。
TLP 的外觀
TLP 的解釋非常簡(jiǎn)單。 以下是其結(jié)構(gòu)的一般視圖。
標(biāo)頭包含 3 或 4 個(gè) DW,但最重要的字段是第一個(gè) DW 的一部分。
“Fmt”字段表示標(biāo)頭的長(zhǎng)度,以及是否存在數(shù)據(jù)有效負(fù)載。
然后與“類型”一起描述TLP操作。 TLP 標(biāo)頭內(nèi)容的其余部分取決于 TLP 操作。
例如,下面是一個(gè) 32 位內(nèi)存寫入 TLP 標(biāo)頭,您可以在其中看到寫入地址位于標(biāo)頭的末尾(并且要寫入的數(shù)據(jù)位于標(biāo)頭之后的有效負(fù)載中)。
“Fmt”字段為“10”,表示“3 DW,有數(shù)據(jù)”。 這是有道理的,內(nèi)存寫入需要寫入數(shù)據(jù),因此在標(biāo)頭之后獲得數(shù)據(jù)有效負(fù)載后,我們將該數(shù)據(jù)寫入某個(gè)內(nèi)存(或以某種方式使用它),然后我們就完成了它。 字段“長(zhǎng)度”表示有效負(fù)載中有多少 DW(從 0 到 1023)。 通常,要寫入的是單個(gè) DW,在這種情況下,長(zhǎng)度等于 1,總 TLP 長(zhǎng)度為 4 DW(標(biāo)頭為 3,有效負(fù)載為 1)。
現(xiàn)在內(nèi)存讀取呢?不知何故,我們必須返回?cái)?shù)據(jù)。
用數(shù)據(jù)完成
如果 TLP 是內(nèi)存讀取而不是寫入,我們必須執(zhí)行讀取,然后做出響應(yīng)。 該響應(yīng)有一個(gè)特殊的 TLP,它稱為 CplD(數(shù)據(jù)完成),其有效負(fù)載包含我們要返回的數(shù)據(jù)。
讓我們仔細(xì)看看 32 位內(nèi)存讀取 TLP 標(biāo)頭 - 它與我們之前的 32 位內(nèi)存寫入非常相似。
一個(gè)區(qū)別是 Fmt=00,這意味著“沒有數(shù)據(jù)”。 有道理,我們不需要數(shù)據(jù)來(lái)讀取,只需要一個(gè)地址。 但我們現(xiàn)在必須用數(shù)據(jù)來(lái)回應(yīng)。 同樣重要的是,響應(yīng)需要路由回請(qǐng)求讀取的人...... 你看到問(wèn)題了嗎?
好的,我們收到了一個(gè)讀取請(qǐng)求。 它來(lái)自CPU嗎? 還是來(lái)自中斷控制器? 還是從顯卡? 畢竟,許多設(shè)備都能夠發(fā)出這樣的請(qǐng)求。 答案在“請(qǐng)求者 ID”中給出 - 它顯示誰(shuí)請(qǐng)求讀取。 因此,當(dāng)我們創(chuàng)建 CplD TLP 時(shí),我們必須重新復(fù)制其中的“請(qǐng)求者 ID”。 這樣,它將通過(guò)PCI Express網(wǎng)橋路由到它所屬的位置。 順便說(shuō)一句,我們還必須重新復(fù)制“標(biāo)簽”(這在多次讀取待處理的情況下很有用)。
TLP 大小
典型的 32 位地址/數(shù)據(jù)存儲(chǔ)器讀取 TLP 由報(bào)頭中的 3 個(gè) DW 組成,沒有有效載荷(因此總共 96 位),而類似的內(nèi)存寫入由 4 個(gè) DW(3 個(gè)用于報(bào)頭,1 個(gè)用于有效負(fù)載)組成。 由于 TLP 標(biāo)頭開銷,這在帶寬方面效率不高,因此最好盡可能使用更大的 TLP 有效負(fù)載。 TLP 有效負(fù)載理論上可以達(dá)到 1023 DW,這對(duì)于突發(fā)讀取和寫入非常方便,盡管 PC 可以將最大大小限制為較低的值(通常為 32 DW)。
有關(guān)更多信息,請(qǐng)通過(guò)谷歌搜索 PCI Express 規(guī)范來(lái)查看官方 PCI Express 規(guī)范,例如PCI_Express_Base_11.pdf
理論已經(jīng)夠多了,讓我們玩得開心,玩玩 Xilinx PCI Express 向?qū)А?/span>
PCI Express 5 - Xilinx 向?qū)?/span>
Xilinx 使 PCI express 的使用變得簡(jiǎn)單 - 它們提供了一個(gè)免費(fèi)的 PCI Express 內(nèi)核(稱為“Endpoint Block Plus”)和一個(gè)用于配置它的向?qū)А?所有這些都在他們的免費(fèi)版 ISE - ISE WebPack 中。
因此,讓我們啟動(dòng)Xilinx CORE生成器,選擇Endpoint Block Plus。
內(nèi)核處于非活動(dòng)狀態(tài),我們需要使用 File --> New Project 創(chuàng)建一個(gè)項(xiàng)目并選擇一個(gè) FPGA(這里我們使用的是 Dragon-E,所以我們選擇 Virtex-5)...
...,然后選擇您喜歡的語(yǔ)言(在“生成”選項(xiàng)卡中)。
現(xiàn)在,Endpoint Block Plus內(nèi)核變?yōu)榛顒?dòng)狀態(tài),您可以雙擊它以啟動(dòng)向?qū)А?br/>在第一頁(yè)上,為組件命名。在這里,我們選擇了“my_endpoint_blk_plus”。 剩下的對(duì) Dragon-E 來(lái)說(shuō)沒問(wèn)題,所以點(diǎn)擊“下一步>”。
現(xiàn)在,您可以更改供應(yīng)商/設(shè)備 ID...
...和地址空間。
接下來(lái)的頁(yè)面沒有太多興趣,所以點(diǎn)擊“生成”來(lái)生成核心及其文檔。
現(xiàn)在,我們已準(zhǔn)備好創(chuàng)建第一個(gè)PCI Express FPGA位文件,在FPGA中對(duì)其進(jìn)行編程,并生成真正的PCI Express流量。
PCI Express 6 - 簡(jiǎn)單事務(wù)
讓我們嘗試從 PCI Express 總線控制 LED。
Xilinx 的“Endpoint Block Plus”內(nèi)核允許我們?cè)谑聞?wù)層級(jí)別工作,因此只需幾行代碼即可。
“Endpoint Block Plus”不是在32位總線上提供數(shù)據(jù),而是使用64位總線(因此我們?cè)诿總€(gè)時(shí)鐘周期獲得的數(shù)據(jù)量是原來(lái)的兩倍)。 這不是問(wèn)題,一個(gè)簡(jiǎn)單的狀態(tài)機(jī)將處理簡(jiǎn)單的內(nèi)存讀取和寫入。
// we use signals from Xilinx's "Endpoint Block Plus"
// first we declare that we are always ready to get data
assign trn_rdst_rdy_n = 1'b0;
// then we create a state machine that triggers when we get a PCI Express memory read or write
reg RXstate;
reg [63:0] RXrd;always @(posedge clk)case(RXstate)
// we are going to handle simple memory reads & writes
// we know that with the "Endpoint Block Plus" core, such simple transactions always happens
// using two cycles so we just need a two-states state machine
// first, we wait for the beginning of a memory transaction with up to 32-bit data (i.e. with length=1)
1'b0: if(~trn_rsrc_rdy_n && ~trn_rsof_n && trn_rd[61:56]==6'b0_00000 && trn_rd[41:32]==10'h001) begin
RXstate <= 1'b1;
RXrd <= trn_rd;
end
// then the second state waits for the end of the transaction
1'b1: if(~trn_rsrc_rdy_n) RXstate <= 1'b0;
endcase
現(xiàn)在我們準(zhǔn)備更新 LED。
wire [31:0] RXaddr = trn_rd[63:32];
// memory address (read or write) (valid during the second state of the state machine)
wire [31:0] RXdata = trn_rd[31:0];
// memory data (for a write) (valid during the second state of the state machine)
wire RXrdwr = RXrd[62];
// 0 for a read, 1 for a write
wire RXRead = ~trn_rsrc_rdy_n & RXstate & ~RXrdwr;
// true when a read is happeningwire RX
write = ~trn_rsrc_rdy_n & RXstate & RXrdwr;
// true when a write is happening
// update two LEDs using the two LSBs from the data written
reg [1:0] LEDs;
always @(posedge clk) if(RXwrite) LEDs <= RXdata[1:0];
對(duì)于內(nèi)存寫入,僅此而已。 對(duì)于內(nèi)存讀取,您需要使用要返回的數(shù)據(jù)創(chuàng)建響應(yīng)數(shù)據(jù)包。 生成中斷也非常容易 - 只需斷言一個(gè)名為“cfg_interrupt_n”的信號(hào)即可。
想要更多?請(qǐng)查看 Dragon-E 的啟動(dòng)套件以獲取更完整的示例,并查看 Xilinx 的 UG341 Endpoint Block Plus 規(guī)范文檔,了解所有信號(hào)的描述。
評(píng)論