FPGA:PCI Express接口
隨著 PCI Express 在高端 FPGA 中變得司空見慣,讓我們看看 FPGA 供應商如何輕松實現(xiàn)該技術。特別是,我們更仔細地研究了賽靈思的 PCI Express 解決方案。
本文引用地址:http://www.ex-cimer.com/article/202401/454643.htmPCI Express 1 - 連接器
PCI Express 通常有兩種尺寸:1 通道和 16 通道,其中 1 通道用于普通主板,16 通道用于顯卡。
連接器
1 通道連接器有 36 個觸點,排列成兩排,每排 18 個觸點。
這是俯視圖。
在 36 個觸點中,只有 6 個對數(shù)據(jù)傳輸有用,其余是電源引腳和其他輔助信號。 6 個功能觸點以 3 對使用:
名為 REFCLK 的時鐘對。
名為 PER 的接收對。
稱為 PET 的傳輸對。
這些信號對通常被稱為“差分對”,因為來自一對的每個信號都攜帶相同的信號,但一個信號與另一個信號相反。 使用差分對的原因主要是傳輸?shù)目煽啃?,稍后將更詳細地討論?/span>
在 PCI Express 第 1 代(或簡稱為“Gen1”)中,PET 和 PER 對的數(shù)據(jù)傳輸速度為 2.5Gbps。 Gen2 將這一數(shù)字翻了一番。
查看 Dragon-E 板,我們可以識別出 FPGA 下方的 PET 對。
為了正常工作,差分對中的線路需要電耦合并且沒有阻抗不連續(xù)性,這在實踐中意味著“保持緊密”和“沒有銳角”。 這就是 Dragon-E 的 PET 對蛇形形狀的原因。 板的另一側顯示了另外兩對蛇形對 REFCLK 和 PER。
PCI Express x16 插槽
為了提高速度,可以使用多條車道。 不需要復制 REFCLK 對,例如,具有 2 個通道的 PCI Express 使用 5 個對(1 個 REFCLK + 2 個 PET + 2 個 PER)。
圖形板通常使用 16 通道連接器,通常稱為 PCI Express x16。
PCI Express 2 - 拓撲
點對點架構
在 2.5Gsps 時,PCI Express Gen1 線路速度比 75MHz 傳統(tǒng) PCI 速度快 33 倍。
這怎么可能?只是因為 PCI express 是點對點總線。
還記得PCI是共享總線嗎?
使用PCI時,必須指定足夠的時間,讓信號在每個時鐘周期內穩(wěn)定下來。 這是因為PCI總線的每條線路都在同一總線上的PCI連接器和板上共享。 使用PCI Express,每個信號都是點對點的,這意味著不再有建立時間,線路速度可以更高。
例如,如果主板有兩個 1 通道連接器和一個 16 通道連接器,則需要橋接器上有 6+6+34=46 個引腳,僅用于 REFCLK、PER 和 PET(因為不允許共享)。
時鐘恢復
在2.5GHz開始的速度下,點對點架構仍然是一個挑戰(zhàn),因為每個位的持續(xù)時間非常短,以至于時序抖動(圍繞每個位到達的時間不確定性)成為一個問題。 即使每個信號對都有相關的時鐘對同時傳輸,時鐘對也會受到定時抖動的影響。 因此,使用了一種稱為“時鐘恢復”的新技術。
時鐘恢復很簡單。 基本上,對于每個信號對,接收器對都會查看信號轉換(位 0 后跟位 1,反之亦然),從中可以推斷出周圍位的位置。 一個問題是,如果許多連續(xù)的位以相同的值傳輸(如許多0),則看不到信號轉換。 因此,傳輸額外的位以確保信號轉換不會相距太遠(這會“重新同步”時鐘恢復機制)。
額外的比特使用一種稱為 8b/10b 編碼的方案發(fā)送,因此對于每 8 位有用數(shù)據(jù),實際上有 10 位以特定方式傳輸(開銷為 20%),以保證足夠的信號轉換。 但這也意味著,在2.5GHz時,我們每對只能獲得250MBps的有用帶寬(而不是沒有編碼開銷的312MBps)。
差分對
現(xiàn)在還記得信號是在差分對上發(fā)送的事實嗎? 這有很多優(yōu)點:
它更不受外部干擾。
它能夠在低電壓下工作(=更低的功耗)。
...最后但并非最不重要的一點是:這有助于時鐘恢復獲得精確的信號轉換。
差分對有一個明顯的缺點:傳輸信號需要兩倍的導線。
PCI Express 3 - 數(shù)據(jù)包、堆棧和網(wǎng)絡的故事
分組事務
PCI express 是串行總線。 或者是嗎? 從計算機的角度來看,它是一種可以實現(xiàn)讀寫事務的傳統(tǒng)總線。
訣竅是所有操作都已打包。 假設 CPU 想要將一些數(shù)據(jù)寫入設備。 它將訂單轉發(fā)到 PCI Express 網(wǎng)橋,然后 PCI Express 網(wǎng)橋創(chuàng)建一個數(shù)據(jù)包。 數(shù)據(jù)包包含要寫入的地址和數(shù)據(jù),并串行轉發(fā)到目標設備,目標設備將寫入順序解包并執(zhí)行。
如果 CPU 想要讀取怎么辦? 同樣,網(wǎng)橋將數(shù)據(jù)包轉發(fā)到目標設備,目標設備現(xiàn)在必須執(zhí)行讀取,創(chuàng)建返回數(shù)據(jù)包并將其發(fā)送到網(wǎng)橋。
所有這一切都在實踐中非常容易做到,感謝來自...
PCI Express 協(xié)議棧
讓數(shù)據(jù)包沿著線路可靠地流動需要一些魔力。 由于數(shù)據(jù)包以非常高的速度串行傳輸,因此必須對它們進行反序列化/匯編、在目的地解碼(刪除 8b/10b 編碼)、去交錯(如果使用多個通道)并檢查線路損壞(CRC 檢查)。
聽起來很復雜? 大概是這樣。 問題是,我們并不真正關心,因為大部分復雜性都是在由三層組成的“PCI Express堆棧”中處理的。
物理層。
數(shù)據(jù)鏈路層。
事務層。
前兩層是在PCI Express FPGA內核(通常是硬核和軟核的組合)中為我們實現(xiàn)的,用于處理所有復雜性。 作為用戶,我們只在交易層工作,那里的生活很輕松,天空很藍,女孩很漂亮。
更多細節(jié):
物理層:這是引腳切換的地方。8b/10b 編碼/解碼和通道拆卸/重組都在那里完成。
數(shù)據(jù)鏈路層:檢查數(shù)據(jù)完整性 (CRC) 并在需要時重新傳輸數(shù)據(jù)包(希望這種情況很少發(fā)生)。
交易層:即用戶級別。一旦數(shù)據(jù)包到達這里,它就可以保證是好數(shù)據(jù)。
好數(shù)據(jù)?太好了,這就是我們想要的!
讓我們看看在事務層中工作是什么樣的。
PCI Express 4 - 事務層
在交易層,我們接收“數(shù)據(jù)包”。 有一個 32 位總線,數(shù)據(jù)包到達總線(數(shù)據(jù)包長度始終是 32 位的倍數(shù))。 也許一個數(shù)據(jù)包會說“在地址0xABCD寫入數(shù)據(jù)1234x0”,另一個數(shù)據(jù)包會說“從地址0xDCBA讀?。ú⒎祷仨憫獢?shù)據(jù)包)”。
數(shù)據(jù)包有很多種類型:內存讀取、內存寫入、I/O 讀取、I/O 寫入、消息、完成等...... 我們在事務層的工作是接受數(shù)據(jù)包和發(fā)出數(shù)據(jù)包。 數(shù)據(jù)包以稱為“事務層數(shù)據(jù)包”(TLP)的特定格式呈現(xiàn)給我們,到達總線的每個 32 位數(shù)據(jù)稱為“雙字”(或簡稱 DW)。
所以一個數(shù)據(jù)包(哎呀,對不起,一個 TLP)是一堆 DW。
TLP 的外觀
TLP 的解釋非常簡單。 以下是其結構的一般視圖。
標頭包含 3 或 4 個 DW,但最重要的字段是第一個 DW 的一部分。
“Fmt”字段表示標頭的長度,以及是否存在數(shù)據(jù)有效負載。
然后與“類型”一起描述TLP操作。 TLP 標頭內容的其余部分取決于 TLP 操作。
例如,下面是一個 32 位內存寫入 TLP 標頭,您可以在其中看到寫入地址位于標頭的末尾(并且要寫入的數(shù)據(jù)位于標頭之后的有效負載中)。
“Fmt”字段為“10”,表示“3 DW,有數(shù)據(jù)”。 這是有道理的,內存寫入需要寫入數(shù)據(jù),因此在標頭之后獲得數(shù)據(jù)有效負載后,我們將該數(shù)據(jù)寫入某個內存(或以某種方式使用它),然后我們就完成了它。 字段“長度”表示有效負載中有多少 DW(從 0 到 1023)。 通常,要寫入的是單個 DW,在這種情況下,長度等于 1,總 TLP 長度為 4 DW(標頭為 3,有效負載為 1)。
現(xiàn)在內存讀取呢?不知何故,我們必須返回數(shù)據(jù)。
用數(shù)據(jù)完成
如果 TLP 是內存讀取而不是寫入,我們必須執(zhí)行讀取,然后做出響應。 該響應有一個特殊的 TLP,它稱為 CplD(數(shù)據(jù)完成),其有效負載包含我們要返回的數(shù)據(jù)。
讓我們仔細看看 32 位內存讀取 TLP 標頭 - 它與我們之前的 32 位內存寫入非常相似。
一個區(qū)別是 Fmt=00,這意味著“沒有數(shù)據(jù)”。 有道理,我們不需要數(shù)據(jù)來讀取,只需要一個地址。 但我們現(xiàn)在必須用數(shù)據(jù)來回應。 同樣重要的是,響應需要路由回請求讀取的人...... 你看到問題了嗎?
好的,我們收到了一個讀取請求。 它來自CPU嗎? 還是來自中斷控制器? 還是從顯卡? 畢竟,許多設備都能夠發(fā)出這樣的請求。 答案在“請求者 ID”中給出 - 它顯示誰請求讀取。 因此,當我們創(chuàng)建 CplD TLP 時,我們必須重新復制其中的“請求者 ID”。 這樣,它將通過PCI Express網(wǎng)橋路由到它所屬的位置。 順便說一句,我們還必須重新復制“標簽”(這在多次讀取待處理的情況下很有用)。
TLP 大小
典型的 32 位地址/數(shù)據(jù)存儲器讀取 TLP 由報頭中的 3 個 DW 組成,沒有有效載荷(因此總共 96 位),而類似的內存寫入由 4 個 DW(3 個用于報頭,1 個用于有效負載)組成。 由于 TLP 標頭開銷,這在帶寬方面效率不高,因此最好盡可能使用更大的 TLP 有效負載。 TLP 有效負載理論上可以達到 1023 DW,這對于突發(fā)讀取和寫入非常方便,盡管 PC 可以將最大大小限制為較低的值(通常為 32 DW)。
有關更多信息,請通過谷歌搜索 PCI Express 規(guī)范來查看官方 PCI Express 規(guī)范,例如PCI_Express_Base_11.pdf
理論已經夠多了,讓我們玩得開心,玩玩 Xilinx PCI Express 向導。
PCI Express 5 - Xilinx 向導
Xilinx 使 PCI express 的使用變得簡單 - 它們提供了一個免費的 PCI Express 內核(稱為“Endpoint Block Plus”)和一個用于配置它的向導。 所有這些都在他們的免費版 ISE - ISE WebPack 中。
因此,讓我們啟動Xilinx CORE生成器,選擇Endpoint Block Plus。
內核處于非活動狀態(tài),我們需要使用 File --> New Project 創(chuàng)建一個項目并選擇一個 FPGA(這里我們使用的是 Dragon-E,所以我們選擇 Virtex-5)...
...,然后選擇您喜歡的語言(在“生成”選項卡中)。
現(xiàn)在,Endpoint Block Plus內核變?yōu)榛顒訝顟B(tài),您可以雙擊它以啟動向導。
在第一頁上,為組件命名。在這里,我們選擇了“my_endpoint_blk_plus”。 剩下的對 Dragon-E 來說沒問題,所以點擊“下一步>”。
現(xiàn)在,您可以更改供應商/設備 ID...
...和地址空間。
接下來的頁面沒有太多興趣,所以點擊“生成”來生成核心及其文檔。
現(xiàn)在,我們已準備好創(chuàng)建第一個PCI Express FPGA位文件,在FPGA中對其進行編程,并生成真正的PCI Express流量。
PCI Express 6 - 簡單事務
讓我們嘗試從 PCI Express 總線控制 LED。
Xilinx 的“Endpoint Block Plus”內核允許我們在事務層級別工作,因此只需幾行代碼即可。
“Endpoint Block Plus”不是在32位總線上提供數(shù)據(jù),而是使用64位總線(因此我們在每個時鐘周期獲得的數(shù)據(jù)量是原來的兩倍)。 這不是問題,一個簡單的狀態(tà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)在我們準備更新 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];
對于內存寫入,僅此而已。 對于內存讀取,您需要使用要返回的數(shù)據(jù)創(chuàng)建響應數(shù)據(jù)包。 生成中斷也非常容易 - 只需斷言一個名為“cfg_interrupt_n”的信號即可。
想要更多?請查看 Dragon-E 的啟動套件以獲取更完整的示例,并查看 Xilinx 的 UG341 Endpoint Block Plus 規(guī)范文檔,了解所有信號的描述。
評論