Xilinx FPGA 的PCIE 設計


寫在前面

近兩年來和幾個單位接觸下來,發現PCIe還是一個比較常用的,有些難度的案例,主要是涉及面比較廣,需要了解邏輯設計、高速總線、Linux和Windows的驅動設計等相關知識。

這篇文章主要針對Xilinx家V6和K7兩個系列的PFGA,在Linux和Windows兩種系統平台下,基於Xilinx的參考案例XAPP1052的基礎上,設計實現了總線主控DMA(Bus Master DMA),透明映像內存空間和中斷機制,在實際工程實踐中得到了良好的應用,主要應用在光纖PCIe數據采集卡、FPGA加速卡、存儲子系統等所有需要和主機進行高速數據交互的場所。這篇博客記錄了在學習、開發、調試FPGA和主機之間的PCIe通訊接口的方方面面,記錄難免有些差錯,有任何事宜,歡迎郵件交流學習,jackxu8#163.com。需要完整設計文件或協助進行二次開發的,也可郵件聯系。

第一版本多以開發記錄為主,不盡完善,看得人多的話我在逐步完善,盡量講的更加清楚一下。

PCIe協議基礎知識

 
 

在高速互連領域中,使用高速差分總線替代並行總線是大勢所趨。與單端並行信號(PCI總線)相比,高速差分信號(PCIe總線)可以使用更高的時鍾頻率,從而使用更少的信號線,完成之前需要許多單端並行數據信號才能達到的總線帶寬。

PCI總線使用並行總線結構,在同一條總線上的所有外部設備共享總線帶寬,而PCIe總線使用了高速差分總線,並采用端到端的連接方式,因此在每一條PCIe鏈路中只能連接兩個設備。這使得PCIe與PCI總線采用的拓撲結構有所不同。PCIe總線除了在連接方式上與PCI總線不同之外,還使用了一些在網絡通信中使用的技術,如支持多種數據路由方式,基於多通路的數據傳遞方式,和基於報文的數據傳送方式,並充分考慮了在數據傳送中出現服務質量QoS (Quality of Service)問題。

PCIe總線

與PCI總線不同,PCIe總線使用端到端的連接方式,在一條PCIe鏈路的兩端只能各連接一個設備,這兩個設備互為是數據發送端和數據接收端。PCIe鏈路可以由多條Lane組成,目前PCIe鏈路×1、×2、×4、×8、×16和×32寬度的PCIe鏈路,還有幾乎不使用的×12鏈路。

PCIe總線規范

總線頻率

單Lane的峰值帶寬

編碼方式

單個Lane帶寬

1.x

1.25GHz

2.5GT/s

8/10b編碼

250MB/s

2.x

2.5GHz

5GT/s

8/10b編碼

500MB/s

3.0

4GHz

8GT/s

128/130b編碼

1GB/s

在PCIe總線中,使用GT(Gigatransfer)計算PCIe鏈路的峰值帶寬。GT是在PCIe鏈路上傳遞的峰值帶寬,其計算公式為 總線頻率×數據位寬×2。

按照常理新一代的帶寬要比上一代翻倍,PCIe3.0的原始數據傳輸帶寬應該是10GT/s才對而實際卻只有8.0GT/s。我們知道,在1.0,2.0標准中,采用的是8b/10b的編碼方式,也就是說,每傳輸8比特有效數據,要附帶兩比特的校驗位,實際要傳輸10比特數據。因此,有效帶寬=原始數據傳輸帶寬*80%。而3.0標准中,使用了更為有效的128b/130b編碼方案從而避免20%帶寬損失,3.0的浪費帶寬僅為1.538%,基本可以忽略不計,因此8GT/s的信號不再僅僅是一個理論數值,它將是一個實在的傳輸值。

PCIe總線采用了串行連接方式,並使用數據包(Packet)進行數據傳輸,采用這種結構有效去除了在PCI總線中存在的一些邊帶信號,如INTx和PME#等信號。在PCIe總線中,數據報文在接收和發送過程中,需要通過多個層次,包括事務層、數據鏈路層和物理層。PCIe總線的層次結構如下。

 

PCIe總線的層次組成結構與網絡中的層次結構有類似之處,但是PCIe總線的各個層次都是使用硬件邏輯實現的。在PCIe體系結構中,數據報文首先在設備的核心層(Device Core)中產生,然后再經過該設備的事務層(TransactionLayer)、數據鏈路層(Data Link Layer)和物理層(Physical Layer),最終發送出去。而接收端的數據也需要通過物理層、數據鏈路和事務層,並最終到達Device Core。

在PCIe總線層次結構中,事務層最易理解,同時也與系統軟件直接相關。

事務層定義了PCIe總線使用總線事務,其中多數總線事務與PCI總線兼容。這些總線事務可以通過Switch等設備傳送到其他PCIe設備或者RC,RC也可以使用這些總線事務訪問PCIe設備。事務層接收來自PCIe設備核心層的數據,並將其封裝為TLP(Transaction Layer Packet)后,發向數據鏈路層。此外事務層還可以從數據鏈路層中接收數據報文,然后轉發至PCIe設備的核心層。事務層還使用流量控制機制保證PCIe鏈路的使用效率。

總線信號

在一個處理器系統中,一般提供×16的PCIe插槽,並使用PETp0~15、PETn0~15和PERp0~15、PERn0~15共64根信號線組成32對差分信號,其中16對PETxx信號用於發送鏈路,另外16對PERxx信號用於接收鏈路。除此之外PCIe總線還使用了下列輔助信號。

1 PERST#信號

該信號為全局復位信號,由處理器系統提供,處理器系統需要為PCIe插槽和PCIe設備提供該復位信號。PCIe設備使用該信號復位內部邏輯,當該信號有效時,PCIe設備將進行復位操作。PCIe總線定義了多種復位方式,其中Cold Reset和Warm Reset這兩種復位方式的實現與該信號有關。

2 REFCLK+和REFCLK-信號

PCIe設備與PCIe插槽都具有REFCLK+和REFCLK-信號,其中PCIe插槽使用這組信號與處理器系統同步。

當PCIe設備作為Add-In卡連接在PCIe插槽時,可以直接使用PCIe插槽提供的REFCLK+和REFCLK-信號,也可以使用獨立的參考時鍾,只要這個參考時鍾在100MHz±300ppm范圍內即可。在PCIe設備配置空間的Link Control Register中,含有一個“Common ClockConfiguration”位。當該位為1時,表示該設備與PCIe鏈路的對端設備使用“同相位”的參考時鍾;如果為0,表示該設備與PCIe鏈路的對端設備使用的參考時鍾是異步的。如果主機系統使用用了擴譜時鍾,那么最好使用這個參考時鍾信號,不要使用自己的晶振產生時鍾。

3 WAKE#信號

當PCIe設備進入休眠狀態,主電源已經停止供電時,PCIe設備使用該信號向處理器系統提交喚醒請求,使處理器系統重新為該PCIe設備提供主電源Vcc。在PCIe總線中,WAKE#信號是可選的,因此使用WAKE#信號喚醒PCIe設備的機制也是可選的,產生該信號的硬件邏輯必須使用輔助電源Vaux供電。

WAKE#是一個Open Drain信號,一個處理器的所有PCIe設備可以將WAKE#信號進行線與后,統一發送給處理器系統的電源控制器。當某個PCIe設備需要被喚醒時,該設備首先置WAKE#信號有效,然后在經過一段延時之后,處理器系統開始為該設備提供主電源Vcc,並使用PERST#信號對該設備進行復位操作。此時WAKE#信號需要始終保持為低,當主電源Vcc上電完成之后,PERST#信號也將置為無效並結束復位,WAKE#信號也將隨之置為無效,結束整個喚醒過程。

配置空間    

PCI總線定義了兩類配置請求,一個是Type00h配置請求,另一個是Type 01h配置請求。

其中HOST主橋或者PCI橋使用Type 00h配置請求,訪問與HOST主橋或者PCI橋直接相連的PCI Agent設備或者PCI橋;而使用Type 01h配置請求,需要至少穿越一個PCI橋,訪問沒有與其直接相連的PCI Agent設備或者PCI橋。在PCI總線中,只有PCI橋能夠接收Type 01h配置請求。Type 01h配置請求不能直接發向最終的PCI Agent設備,而只能由PCI橋將其轉換為Type 01h繼續發向其他PCI橋,或者轉換為Type 00h配置請求發向PCI Agent設備。

基本配置空間

這個基本配置空間共由64個字節組成,其地址范圍為0x00~0x3F,這64個字節是所有PCI設備必須支持的。

PCI設備都有獨立的配置空間,HOST主橋通過配置讀寫總線事務訪問這段空間。PCI總線規定了三種類型的PCI配置空間,分別是PCI Agent設備使用的配置空間,PCI橋使用的配置空間和Cardbus橋片使用的配置空間。

在PCI設備配置空間中出現的地址都是PCI總線地址,屬於PCI總線域地址空間。

在PCI Agent設備的配置空間中包含了許多寄存器,這些寄存器決定了該設備在PCI總線中的使用方法,系統軟件只對部分配置寄存器感興趣。PCI Agent設備使用的Type 00配置空間如下。

在PCI Agent設備配置空間中包含的寄存器如下所示。

(1) DeviceID和Vendor ID寄存器

這兩個寄存器的值由PCISIG分配,只讀。其中Vendor ID代表PCI設備的生產廠商,而Device ID代表這個廠商所生產的具體設備。如Xilinx公司的K7,其Vendor ID為0x10EE,而Device ID為0x7028。

(5) SubsystemID和Subsystem Vendor ID寄存器

這兩個寄存器和DeviceID和Vendor ID類似,也是記錄PCI設備的生產廠商和設備名稱。但是這兩個寄存器和Device ID與Vendor ID寄存器略有不同。下文以一個實例說明Subsystem ID和Subsystem

(6) ExpansionROM base address寄存器

有些PCI設備在處理器還沒有運行操作系統之前,就需要完成基本的初始化設置,比如顯卡、鍵盤和硬盤等設備。為了實現這個“預先執行”功能,PCI設備需要提供一段ROM程序,而處理器在初始化過程中將運行這段ROM程序,初始化這些PCI設備。ExpansionROM base address記載這段ROM程序的基地址。

(7) CapabilitiesPointer寄存器

在PCI設備中,該寄存器是可選的,但是在PCIe設備中必須支持這個寄存器,Capabilities Pointer寄存器存放Capabilities寄存器組的基地址,PCI設備使用Capabilities寄存器組存放一些與PCI設備相關的擴展配置信息。

(8) InterruptLine寄存器

這個寄存器是系統軟件對PCI設備進行配置時寫入的,該寄存器記錄當前PCI設備使用的中斷向量號,設備驅動程序可以通過這個寄存器,判斷當前PCI設備使用處理器系統中的哪個中斷向量號,並將驅動程序的中斷服務例程注冊到操作系統中。

(9) InterruptPin寄存器

這個寄存器保存PCI設備使用的中斷引腳,PCI總線提供了四個中斷引腳INTA#、INTB#、INTC#和INTD#。InterruptPin寄存器為1時表示使用INTA#引腳向中斷控制器提交中斷請求,為2表示使用INTB#,為3表示使用INTC#,為4表示使用INTD#。

如果PCI設備只有一個子設備時,該設備只能使用INTA#;如果有多個子設備時,可以使用INTB~D#信號。如果PCI設備不使用這些中斷引腳,向處理器提交中斷請求時,該寄存器的值必須為0。值得注意的是,雖然在PCIe設備中並不含有INTA~D#信號,但是依然可以使用該寄存器,因為PCIe設備可以使用INTx中斷消息,模擬PCI設備的INTA~D#信號。

(10) BaseAddress Register 0~5寄存器

該組寄存器簡稱為BAR寄存器,BAR寄存器保存PCI設備使用的地址空間的基地址,該基地址保存的是該設備在PCI總線域中的地址。其中每一個設備最多可以有6個基址空間,但多數設備不會使用這么多組地址空間。

在PCI設備復位之后,該寄存器將存放PCI設備需要使用的基址空間大小,這段空間是I/O空間還是存儲器空間,如果是存儲器空間該空間是否可預取。

系統軟件對PCI總線進行配置時,首先獲得BAR寄存器中的初始化信息,之后根據處理器系統的配置,將合理的基地址寫入相應的BAR寄存器中。系統軟件還可以使用該寄存器,獲得PCI設備使用的BAR空間的長度,其方法是向BAR寄存器寫入0xFFFF-FFFF,之后再讀取該寄存器。

處理器訪問PCI設備的BAR空間時,需要使用BAR寄存器提供的基地址。值得注意的是,處理器使用存儲器域的地址,而BAR寄存器存放PCI總線域的地址。因此處理器系統並不能直接使用“BAR寄存器+偏移”的方式訪問PCI設備的寄存器空間,而需要將PCI總線域的地址轉換為存儲器域的地址。

擴展配置空間

此外PCI/PCI-X和PCIe設備還擴展了0x40~0xFF這段配置空間,在這段空間主要存放一些與MSI或者MSI-X中斷機制和電源管理相關的Capability結構。其中所有能夠提交中斷請求的PCIe設備,必須支持MSI或者MSI-X Capability結構。

PCIe設備還支持0x100~0xFFF這段擴展配置空間。PCIe設備使用的擴展配置空間最大為4KB,在PCIe總線的擴展配置空間中,存放PCIe設備所獨有的一些Capability結構,而PCI設備不能使用這段空間。

PCIe總線規范要求其設備必須支持Capabilities結構。在PCI總線的基本配置空間中,包含一個Capabilities Pointer寄存器,該寄存器存放Capabilities結構鏈表的頭指針。在一個PCIe設備中,可能含有多個Capability結構,這些寄存器組成一個鏈表。一個PCIe設備可以包含多個Capability結構,包括與電源管理相關、與PCIe總線相關的結構、與中斷請求相關的Capability結構、PCIe Capability結構和PCIe擴展的Capability結構。

事務層協議

事務層是PCIe總線層次結構的最高層,該層次將接收PCIe設備核心層的數據請求,並將其轉換為PCIe總線事務,PCIe總線使用的這些總線事務在TLP頭中定義。

在PCIe總線中,Non-Posted總線事務分兩部分進行,首先是發送端向接收端提交總線讀寫請求,之后接收端再向發送端發送完成(Completion)報文。PCIe總線使用Split傳送方式處理所有Non-Posted總線事務,存儲器讀、I/O讀寫和配置讀寫這些Non-Posted總線事務都使用Split傳送方式。PCIe的事務層還支持流量控制和虛通路管理等一系列特性。

TLP格式

當處理器或者其他PCIe設備訪問PCIe設備時,所傳送的數據報文首先通過事務層被封裝為一個或者多個TLP,之后才能通過PCIe總線的各個層次發送出去。
 

一個完整的TLP由1個或者多個TLP Prefix、TLP頭、Data Payload(數據有效負載)和TLP Digest組成。TLP頭是TLP最重要的標志,不同的TLP其頭的定義並不相同。TLP頭包含了當前TLP的總線事務類型、路由信息等一系列信息。在一個TLP中,Data Payload的長度可變,最小為0,最大為1024DW。

TLPDigest是一個可選項, 一個TLP是否需要TLP Digest由TLP頭決定。DataPayload也是一個可選項,有些TLP並不需要DataPayload,如存儲器讀請求、配置和I/O寫完成TLP並不需要Data Payload。

TLPPrefix由PCIe V2.1總線規范引入,分為LocalTLP Prefix和EP-EP TLP Prefix兩類。其中Local TLP Prefix的主要作用是在PCIe鏈路的兩端傳遞消息,而EP-EP TLP Prefix的主要作用是在發送設備和接收設備之間傳遞消息。

TLP頭由3個或者4個雙字(DW)組成。其中第一個雙字中保存通用TLP頭,其他字段與通用TLP頭的Type字段相關。一個通用TLP頭由Fmt、Type、TC、Length等字段組成,如圖5‑2所示。

如果存儲器讀寫TLP支持64位地址模式時,TLP頭的長度為4DW,否則為3DW。而完成報文的TLP頭不含有地址信息,使用的TLP頭長度為3DW。

通用TLP頭的Fmt字段和Type字段

Fmt和Type字段確認當前TLP使用的總線事務,TLP頭的大小是由3個雙字還是4個雙字組成,當前TLP是否包含有效負載。這里列舉一些常用的

Fmt[2:0]

TLP的格式

0b000

TLP大小為3個雙字,不帶數據。

0b001

TLP大小為4個雙字,不帶數據。

0b010

TLP大小為3個雙字,帶數據。

0b011

TLP大小為4個雙字,帶數據。

 

其中所有讀請求TLP都不帶數據,而寫請求TLP帶數據,而其他TLP可能帶數據也可能不帶數據,如完成報文可能含有數據,也可能僅含有完成標志而並不攜帶數據。在TLP的Type字段中存放TLP的類型,即PCIe總線支持的總線事務。該字段共由5位組成,這里列舉一些常用的

TLP類型

Fmt[2:0]

Type[4:0]

描述

MRd

0b000

0b001

0b0 0000

存儲器讀請求;TLP頭大小為3個或者4個雙字,不帶數據。

MWr

0b010

0b011

0b0 0000

存儲器寫請求;TLP頭大小為3個或者4個雙字,帶數據。

CplD

0b010

0b0 1010

帶數據的完成報文,TLP頭大小為3個雙字,包括存儲器讀、I/O讀、配置讀和原子操作讀完成。

PCIe總線的數據報文傳送方式與PCI總線數據傳送有類似之處。其中存儲器寫TLP使用Posted方式進行傳送,而其他總線事務使用Non-Posted方式。PCIe總線規定所有Non-Posted存儲器請求使用Split總線方式進行數據傳遞。當PCIe設備進行存儲器讀、I/O讀寫或者配置讀寫請求時,首先向目標設備發送數據讀寫請求TLP,當目標設備收到這些讀寫請求TLP后,將數據和完成信息通過完成報文(Cpl或者CplD)發送給源設備。

其中存儲器讀、I/O讀和配置讀需要使用CplD報文,因為目標設備需要將數據傳遞給源設備;而I/O寫和配置寫需要使用Cpl報文,因為目標設備不需要將任何數據傳遞給源設備,但是需要通知源設備,寫操作已經完成,數據已經成功地傳遞給目標設備。

存儲器和配置讀寫請求TLP

 (1) 存儲器讀請求TLP和讀完成TLP

當PCIe主設備,RC或者EP,訪問目標設備的存儲器空間時,使用Non-Posted總線事務向目標設備發出存儲器讀請求TLP,目標設備收到這個存儲器讀請求TLP后,使用存儲器讀完成TLP,主動向主設備傳遞數據。當主設備收到目標設備的存儲器讀完成TLP后,將完成一次存儲器讀操作。

(2) 存儲器寫請求TLP

在PCIe總線中,存儲器寫使用Posted總線事務。PCIe主設備僅使用存儲器寫請求TLP即可完成存儲器寫操作,主設備不需要目標設備的回應報文。

(5)  配置讀寫請求TLP和配置讀寫完成TLP

從總線事務的角度上看,配置讀寫請求操作的過程與I/O讀寫操作的過程類似。配置讀寫請求TLP都需要配置讀寫完成作為應答,從而完成一個完成的配置讀寫操作。

 

在PCIe總線中,存儲器寫請求TLP使用Posted數據傳送方式。而其他與存儲器和I/O相關的報文都使用Split方式進行數據傳送,這些請求報文需要完成報文,通知發送端之前的數據請求報文已經被處理完畢。存儲器讀寫請求TLP使用地址路由方式進行數據傳遞,在這類TLP頭中包含Address字段,Address字段具有兩種地址格式,分別是32位和64位地址。在存儲器讀寫和I/O讀寫請求的第3和第4個雙字中,存放TLP的32或者64位地址。存儲器、I/O和原子操作讀寫請求使用的TLP頭較為類似。

1 Length字段

在存儲器讀請求TLP中並不包含Data Payload,在該報文中,Length字段表示需要從目標設備數據區域讀取的數據長度;而在存儲器寫TLP中,Length字段表示當前報文的DataPayload長度。Length字段的最小單位為DW。當該字段為n時,表示需要獲得的數據長度或者當前報文的數據長度為n個DW,其中0£n£0x3FF。值得注意的是,當n等於0時,表示數據長度為1024個DW。

2 DWBE字段

PCIe總線以字節為基本單位進行數據傳遞,但是Length字段以DW為最小單位。為此TLP使用Last DW BE和First DW BE這兩個字段進行字節使能,使得在一個TLP中,有效數據以字節為單位。

這兩個DW BE字段各由4位組成,其中Last DW BE字段的每一位對應數據Payload最后一個雙字的字節使能位;而First DW BE字段的每一位對應數據Payload第一個雙字的字節使能位。

Last DW BE

第3位

為1表示數據Payload的最后一個雙字的字節3有效

第2位

為1表示數據Payload的最后一個雙字的字節2有效

第1位

為1表示數據Payload的最后一個雙字的字節1有效

第0位

為1表示數據Payload的最后一個雙字的字節0有效

First DW BE

第3位

為1表示數據Payload的第一個雙字的字節3有效

第2位

為1表示數據Payload的第一個雙字的字節2有效

第1位

為1表示數據Payload的第一個雙字的字節1有效

第0位

為1表示數據Payload的第一個雙字的字節0有效

 

LastDW BE和First DW BE這兩個字段的使用規則如下。

·         如果傳送的數據長度在一個對界的雙字(DW)之內,則Last DW BE字段為0b0000,而First DW BE的對應位置1;如果數據長度超過1DW,Last DW BE字段一定不能為0b0000。PCIe總線使用LastDW BE字段為0b0000表示所傳送的數據在一個對界的DW之內。

·         如果傳送的數據長度超過1DW,則First DW BE字段至少有一個位使能。不能出現First DW BE為0b0000的情況。

·         如果傳送的數據長度大於等於3DW,則在First DW BE和Last DW BE字段中不能出現不連續的置1位。

·         如果傳送的數據長度在1DW之內時,在First DW BE字段中允許有不連續的置1位。此時PCIe總線允許在TLP中傳送1個DW的第1,3字節或者第0,2字節。

·         如果傳送的數據長度為2DW之內時,則First DW BE字段和Last DW BE字段允許有不連續的置1位。

值得注意的是,PCIe總線支持一種特殊的讀操作,即“Zero-Length”讀請求。此時Length字段的長度為1DW,而First DW BE字段和LastDW BE字段都為0b0000,即所有字節都不使能。此時與這個存儲器讀請求TLP對應的讀完成TLP中不包含有效數據。再次提醒讀者注意“Zero-Length”讀請求使用的Length字段為1,而不是為0,為0表示需要獲得的數據長度為1024個DW。

“Zero-Length”讀請求的引入是為了實現“讀刷新”操作,該操作的主要目的是為了確保之前使用Posted方式所傳送的數據,到達最終的目的地,與“Zero-Length”讀對應的讀完成報文中不含有負載,從而提高了PCIe鏈路的利用率。在PCIe總線中,使用Posted方式進行存儲器寫時,目標設備不需要向主設備發送回應報文,因此主設備並不知道這個存儲器寫是否已經達到目的地。而主設備可以使用“讀刷新”操作,向目標設備進行讀操作來保證存儲器寫最終到達目的地。

3 RequesterID字段

RequesterID字段包含“生成這個TLP報文”的PCIe設備的總線號(Bus Number)、設備號(Device Number)和功能號(Function Number),其格式如圖5‑9所示。對於存儲器寫請求TLP,Requester ID字段並不是必須的,因為目標設備收到存儲器寫請求TLP后,不需要完成報文作為應答,因此Requester ID字段對於存儲器寫請求TLP並沒有實際意義。

 

TLP中與數據負載相關的參數

在PCIe總線中,有些TLP含有Data Payload,如存儲器寫請求、存儲器讀完成TLP等。在PCIe總線中,TLP含有的Data Payload大小與Max_Payload_Size、Max_Read_Request_Size和RCB參數相關。

Max_Payload_Size參數

PCIe總線規定在TLP報文中,數據有效負載的最大值為4KB,但是PCIe設備並不一定能夠發送這么大的數據報文。PCIe設備含有“Max_Payload_Size”和“Max_Payload_SizeSupported”參數,這兩個參數分別在Device Capability寄存器和Device Control寄存器中定義。

“Max_Payload_SizeSupported”參數存放在一個PCIe設備中,TLP有效負載的最大值,該參數由PCIe設備的硬件邏輯確定,系統軟件不能改寫該參數。而Max_Payload_Size參數存放PCIe設備實際使用的,TLP有效負載的最大值。該參數由PCIe鏈路兩端的設備協商決定,是PCIe設備進行數據傳送時,實際使用的參數。

PCIe設備發送數據報文時,使用Max_Payload_Size參數決定TLP的最大有效負載。當PCIe設備的所傳送的數據大小超過Max_Payload_Size參數時,這段數據將被分割為多個TLP進行發送。當PCIe設備接收TLP時,該TLP的最大有效負載也不能超過Max_Payload_Size參數,如果接收的TLP,其Length字段超過Max_Payload_Size參數,該PCIe設備將認為該TLP非法。

RC或者EP在發送存儲器讀完成TLP時,這個存儲器讀完成TLP的最大Payload也不能超過Max_Payload_Size參數,如果超過該參數,PCIe設備需要發送多個讀完成報文。值得注意的是,這些讀完成報文需要滿足RCB參數的要求,有關RCB參數的詳細說明見下文。

在實際應用中,盡管有些PCIe設備的Max_Payload_Size Supported參數可以為256B、512B、1024B或者更高,但是如果PCIe鏈路的對端設備可以支持的Max_Payload_Size參數為128B時,系統軟件將使用對端設備的Max_Payload_Size Supported參數,初始化該設備的Max_Payload_Size參數,即選用PCIe鏈路兩端最小的Max_Payload_Size Supported參數初始化Max_Payload_Size參數。

在多數x86處理器系統的MCH或者ICH中,Max_Payload_SizeSupported參數為128B。這也意味着在x86處理器中,與MCH或者ICH直接相連的PCIe設備進行DMA讀寫時,數據的有效負載不能超過128B。而在PowerPC處理器系統中,該參數大多為256B。

目前在大多數EP中,Max_Payload_Size Supported參數不大於512B,因為在大多數處理器系統的RC中,Max_Payload_Size Supported參數也不大於512B。因此即便EP支持較大的Max_Payload_SizeSupported參數,並不會提高數據傳送效率。

而Max_Payload_Size參數的大小與PCIe鏈路的傳送效率成正比,該參數越大,PCIe鏈路帶寬的利用率越高,該參數越小,PCIe鏈路帶寬的利用率越低。

PCIe總線規范規定,對於實時性要求較高的PCIe設備,Max_Payload_Size參數不應設置過大,因此這個參數有時會低於PCIe鏈路允許使用的最大值。

Max_Read_Request_Size參數

Max_Read_Request_Size參數由PCIe設備決定,該參數規定了PCIe設備一次能從目標設備讀取多少數據。

Max_Read_Request_Size參數在Device Control寄存器中定義。該參數與存儲器讀請求TLP的Length字段相關,其中Length字段不能大於Max_Read_Request_Size參數。在存儲器讀請求TLP中,Length字段表示需要從目標設備讀取多少數據。

值得注意的是,Max_Read_Request_Size參數與Max_Payload_Size參數間沒有直接聯系,Max_Payload_Size參數僅與存儲器寫請求和存儲器讀完成報文相關。

PCIe總線規定存儲器讀請求,其讀取的數據長度不能超過Max_Read_Request_Size參數,即存儲器讀TLP中的Length字段不能大於這個參數。如果一次存儲器讀操作需要讀取的數據范圍大於Max_Read_Request_Size參數時,該PCIe設備需要向目標設備發送多個存儲器讀請求TLP。

PCIe總線規定Max_Read_Request_Size參數的最大值為4KB,但是系統軟件需要根據硬件特性決定該參數的值。因為PCIe總線規定EP在進行存儲器讀請求時,需要具有足夠大的緩沖接收來自目標設備的數據。

如果一個EP的Max_Read_Request_Size參數被設置為4KB,而且這個EP每發出一個4KB大小存儲器讀請求時,EP都需要准備一個4KB大小的緩沖[1]。這對於絕大多數EP,這都是一個相當苛刻的條件。為此在實際設計中,一個EP會對Max_Read_Request_Size參數的大小進行限制。

RCB參數

RCB位在Link Control寄存器中定義。RCB位決定了RCB參數的值,在PCIe總線中,RCB參數的大小為64B或者128B,如果一個PCIe設備沒有設置RCB的大小[2],則RC的RCB參數缺省值為64B,而其他PCIe設備的RCB參數的缺省值為128B。PCIe總線規定RC的RCB參數的值為64B或者128B,其他PCIe設備的RCB參數為128B。

在PCIe總線中,一個存儲器讀請求TLP可能收到目標設備發出的多個完成報文后,才能完成一次存儲器讀操作。因為在PCIe總線中,一個存儲器讀請求最多可以請求4KB大小的數據報文,而目標設備可能會使用多個存儲器讀完成TLP才能將數據傳遞完畢。

當一個EP向RC或者其他EP讀取數據時,這個EP首先向RC或者其他EP發送存儲器讀請求TLP;之后由RC或者其他EP發送存儲器讀完成TLP,將數據傳遞給這個EP。

如果存儲器讀完成報文所傳遞數據的地址范圍沒有跨越RCB參數的邊界,那么數據發送端只能使用一個存儲器完成報文將數據傳遞給請求方,否則可以使用多個存儲器讀完成TLP。

假定一個EP向地址范圍為0xFFFF-0000~0xFFFF-0010這段區域進行DMA讀操作,RC收到這個存儲器讀請求TLP后,將組織存儲器讀完成TLP,由於這段區域並沒有跨越RCB邊界,因此RC只能使用一個存儲器讀完成TLP完成數據傳遞。

如果存儲器讀完成報文所傳遞數據的地址范圍跨越了RCB邊界,那么數據發送端(目標設備)可以使用一個或者多個完成報文進行數據傳遞。數據發送端使用多個存儲器讀完成報文完成數據傳遞時,需要遵循以下原則。

·         第一個完成報文所傳送的數據,其起始地址與要求的起始地址相同。其結束地址或者為要求的結束地址(使用一個完成報文傳遞所有數據),或者為RCB參數的整數倍(使用多個完成報文傳遞數據)。

·         最后一個完成報文的起始地址或者為要求的起始地址(使用一個完成報文傳遞所有數據),或者為RCB參數的整數倍(使用多個完成報文傳遞數據)。其結束地址必須為要求的結束地址。

·         中間的完成報文的起始地址和結束地址必須為RCB參數的整數倍。

中斷

在PCI總線中,所有需要提交中斷請求的設備,必須能夠通過INTx引腳提交中斷請求,而MSI機制是一個可選機制。而在PCIe總線中,PCIe設備必須支持MSI或者MSI-X中斷請求機制,而可以不支持INTx中斷消息。在PCIe總線中,MSI和MSI-X中斷機制使用存儲器寫請求TLP向處理器提交中斷請求。

不同的處理器對PCIe設備發出的MSI報文的解釋並不相同。但是PCIe設備在提交MSI中斷請求時,都是向MSI/MSI-X Capability結構中的Message Address的地址寫Message Data數據,從而組成一個存儲器寫TLP,向處理器提交中斷請求。

有些PCIe設備還可以支持Legacy中斷方式,通過發送Assert_INTx和Deassert_INTx消息報文進行中斷請求,即虛擬中斷線方式。。但是PCIe總線並不鼓勵其設備使用Legacy中斷方式,在絕大多數情況下,PCIe設備使用MSI或者MSI/X方式進行中斷請求。

PCIe總線提供Legacy中斷方式的主要原因是,在PCIe體系結構中,存在許多PCI設備,而這些設備通過PCIe橋連接到PCIe總線中。這些PCI設備可能並不支持MSI/MSI-X中斷機制,因此必須使用INTx信號進行中斷請求。

當PCIe橋收到PCI設備的INTx信號后,並不能將其直接轉換為MSI/MSI-X中斷報文,因為PCI設備使用INTx信號進行中斷請求的機制與電平觸發方式類似,而MSI/MSI-X中斷機制與邊沿觸發方式類似。這兩種中斷觸發方式不能直接進行轉換。因此當PCI設備的INTx信號有效時,PCIe橋將該信號轉換為Assert_INTx報文,當這些INTx信號無效時,PCIe橋將該信號轉換為Deassert_INTx報文。

與Legacy中斷方式相比,PCIe設備使用MSI或者MSI-X中斷機制,可以消除INTx這個邊帶信號,而且可以更加合理地處理PCIe總線的“序”。目前絕大多數PCIe設備使用MSI或者MSI-X中斷機制提交中斷請求。

FPGA設計

博主主要在Xilinx的FPGA上進行設計,好在X家提供了PCIe的IP和,支持到事務層,就不想SATA那么麻煩需要自己從物理層開始寫邏輯。本文講述了從IP核的建立、系統仿真環境的搭建、參考案例的講解到BMD控制器中各個模塊的設計方法。

IP核建立

這里V6使用ISE開發,K7使用Vivado開發。核的參數主要是Device Type、Lane Width、Link Speed、Interface Frequency和BAR選項。這個BAR建立BAR0必須要,地址大宇2KB就可以。其他的BAR是不是需要看您喜好。另外,各個ID和驅動安裝有關,只要對應上就可以。

 
 

XAPP1052說明

xapp1052是xilinx官方給出的一個有關DMA數據傳輸的樣例,用於PC端和FPGA端之間的DMA數據傳輸。首先需要說的是,xapp1052並不是一個完整的DMA數據傳輸的終端硬件設計,這個稍后會有說明。

         最新的XAPP1052參考例程為3.3版本,手冊在這里,設計文件在這里,下載需要Xilinx賬號,我打包放在CSDN上了,可以到這里下載,算是給我貢獻一點下載積分吧。

XAPP1052的3.3版本里,對於K7系列以及有一個基於KC705開發板的工程,是在vivado下建立的,使用vivado打開可以直接綜合、仿真、測試。

里面包含的BMD的Example和V6系列的稍有差別。包括設計源碼、約束和仿真源碼。其中,仿真部分的EP是直接例化得設計源碼的頂層。

 

對於使用V6的情況,可以使用XAPP1052 \dma_performance_demo\fpga\implement中的implement_dma.pl腳本自動生成ML605開發板對應的工程。

或者自己搭建工程

..\v6_pcie_v1_7\source全部

..\v6_pcie_v1_7\example_design中的xilinx_pcie_2_0_ep_v6_04_lane_gen2_xc6vlx240t-ff1156-1_ML605.ucf和xilinx_pcie_2_0_ep_v6.v

..\xapp1052\dma_performance_demo\fpga\BMD\common全部

..\xapp1052\dma_performance_demo\fpga\BMD中的BMD_64_RX_ENGINE.v和BMD_64_TX_ENGINE.v和v6_pci_exp_64b_app.v

由於工程中使用了全局宏定義,在ISE上支持的不是特別好,在不對的情況下可以使用手動編譯順序

然后編譯,由於要先編譯BMD_PCIE_20.v,所以右鍵選擇manual compile order,選中所有.v和.ucf文件。

XAPP1052結構

XAPP1052實現了在事務層的總線主控的DMA傳輸樣例,雖然只是提供了收發固定數據的功能,但是為整個事務層的BMD實現提供的良好的參考。

該參考設計的結構如下

     pci_exp_64b_app

     |

     |__BMD

           |

           |__BMD_EP

           |     |

           |    |__BMD_EP_MEM_ACCESS

           |     |    |_BMD_EP_MEM

           |     |

           |     |__BMD_RX_ENGINE

           |     |__BMD_TX_ENGINE

           |     |    |__BMD_INTR_CTRL

           |     |

           |     |__BMD_GEN2

           |    |__BMD_RD_THROTTLE

           |

           |__BMD_TO_CTRL

           |__BMD_CFG_CTRL

 

其中,

1、TX_ENGINE.v:是產生TLP包的邏輯,包含讀TLP請求用於DMA讀;寫TLP請求用於DMA寫;CPLD用於BAR空間讀。
2、RX_ENGINE.v:是解析TLP包的邏輯,包含讀TLP解析用於BAR空間讀、寫TLP解析用於BAR空間寫、CPLD解析用於DMA讀。

 

這里先簡單介紹一下DMA過程:

 

RT->EPDMA(存儲器讀):

1、驅動程序向操作系統申請一片物理連續的內存;

2、主機向該地址寫入數據;

3、主機將這個內存的物理地址告訴FPGA;

4、FPGA向主機發起讀TLP請求—連續發出多個讀請求;

5、主機向FPGA返回CPLD包—連續返回多個CPLD;

6、FPGA取出CPLD包中的有效數據;

7、FPGA發送完數據后通過中斷等形式通知主機DMA完成;

EP->RTDMA(存儲器寫):

1、驅動程序向操作系統申請一片物理連續的內存;

2、主機將這個內存的物理地址告訴FPGA;

3、FPGA向主機發起寫TLP請求,並將數據放入TLP包中—連續發出多個寫請求;

4、FPGA發送完數據后通過中斷等形式通知主機DMA完成;

5、主機從內存中獲取數據;

 

系統仿真環境

系統的仿真需要搭建如下一個系統,包括RT(模擬主機端)和我們自己的EP(FPGA)端,並提供相應的時鍾和測試激勵,系統結構大概如下圖所示,只是其中的PIO APP被替換為了咱們的BMD APP

在Vivado下,設置使用Modelsim為仿真工具后,直接使用Vivado的仿真腳本生成功能直接生成的腳本就可以了。

 

在ISE下,最好是自己書寫仿真腳本,注意BMD_PCIE_20.v必須放在最前面編譯,完整的仿真腳本可以聯系我獲取(jackxu8#163.com)。

最后,打開Modelsim軟件,更改目錄至. . \simulation\functional,輸入腳本dosimulate_mti.do 仿真結果如下,

 
 

但是XAPP1052沒有提供對應的針對DMA的仿真測試腳本,需要我們根據DMA的配置稍后自行書寫,詳細仿真腳本可以聯系我獲取(jackxu8#163.com)。

BMD APP設計

萬事具備,有了這個環境,我們就可以開始着手修改官方提供的BMD的example,首先這個Example只是用於傳輸一個固定的pattern,並不能正在的傳輸用戶自己的數據。其次,也不能將接收的數據傳輸給用戶。另外,沒有提供用戶寄存器和內存傳輸空間。用戶也無法發送自定義的中斷。等等這些限制,導致了這個Example只是一個能夠用來測試PCIe的DMA傳輸速度的Example,而不具有實用價值。但是!但是!但是!它提供了很好的基於事務層設計DMA傳輸的范例,因此,在此基礎上修改一個實用的DMA傳輸控制器將不是什么難事,so, flollow me and make it。

PIO傳輸

PIO傳輸就是Programmable IO,通常用於配置寄存器等小數據的傳輸,一次傳輸32bit數據。首先需要明確一點,PIO傳輸都用主機(也就是PC電腦RT端)主動發起,進行一次PIO傳輸主要需要地址和數據兩個參數(TLP包格式參考前面的基礎知識)。

那么我們來看一下,這兩個參數在BMD的設計中是怎么傳遞的。

BMD_64_RX_ENGINE.trn_rd[63:0] –> BMD_64_RX_ENGINE.addr_o[10:0]–> BMD_EP.req_addr[10:0] ->BMD_EP_MEM_ACCESS.addr_i[6:0] -> 用於索引寄存器

從這條路徑可以看出,BMD接收到RT發過來的地址參數后,將TLP包中解析得到的地址一路送給了BMD_EP_MEM_ACCESS,用來索引對應的寄存器。我們按圖索驥,找到BMD_RX_ENGINE.v和BMD_EP_MEM.v中對應的部分,可以發現,不管是存儲器寫請求(BMD_MEM_WR32_FMT_TYPE)還是存儲器讀請求(BMD_MEM_RD32_FMT_TYPE)都是RT先發送一個地址下來,索引這個寄存器。

 
 

按照這個路子,也就很容易找到數據的傳輸路徑和相應的控制信號了,如上位機寫寄存器的數據傳輸路徑如下

BMD_RX_ENGINE. wr_data_o[31:0] -> BMD_EP_MEM_ACCESS.wr_data_i[31:0]

下面是靈魂畫手繪制的容易理解圖

 

存儲器讀請求

首先我們來看一下存儲器讀請求

時序

 

 

分析

 

RX_ENGINE

EP_RX模塊根據接收trn_rd[62:56]解析包類型


 

解析包頭信息,並跳轉到對應的包處理過程


 

包處理

給出存儲器讀地址

addr_o -> BMD_EP-> BME_EP_MEM_ACCESS.addr_i -> BMD_EP_MEM. a_i

讀出寄存器值

BMD_EP_MEM.rd_d_o->BMD_EP_MEM_ACCESS.mem_rd_data->rd_data_o ->EP_TX. rd_data_i


 

等待CPLD請求響應完成

TX_ENGINE

 

收到CPLD發送請求,根據RX模塊發過來的包信息組包頭

然后到包體形成狀態

 

將存儲器寄存器值組成包的第二個DW

 

給出存儲器讀地址

addr_o -> BMD_EP-> BME_EP_MEM_ACCESS.addr_i -> BMD_EP_MEM. a_i

讀出寄存器值

BMD_EP_MEM.rd_d_o->BMD_EP_MEM_ACCESS.mem_rd_data->rd_data_o ->EP_TX. rd_data_i

 

存儲器寫請求

時序
分析
RX_ENGINE

解析包頭,並跳轉到包體解析狀態

解析包體,獲得存儲器數據

並產生寫使能

EP_RX.wr_en_o -> EP_MEM. wr_en_i

等待存儲器忙結束

 

BMD_EP_MEM_ACCESS

存儲器控制遵循讀-修改-寫過程

時序

 

分析

收到寫使能,進入寫前讀狀態

拉高存儲器忙標志(表示存儲器在操作中,非RST狀態均在操作中)


讀出存儲器當前值,預存

BMD_EP_MEM.rd_d_o -> mem_rd_data

轉到修改狀態

 

根據be信號(Byte Enable)按Byte修改存儲器值

並產生寫使能信號

mem_write_en -> BME_EP_MEM.wr_en_i

mem_wr_data -> BME_EP_MEM.wr_d_i

w_wr_data_bx是wr_data_i的Byte划分

w_pre_wr_data_bx是pre_wr_data的Byte划分

 

存儲器過程完成,跳轉到RST狀態,撤銷存儲器忙信號

 

DMA傳輸

雜項

trn_tdst_dsc_n
 
 
cfg_bus_mstr_enable
配置信息流程
 
配置空間

配置空間總覽

PCIe Spec 2.0 v2_0 page 415

 

Xilinx PCIe core對配置空間寄存器的映射

通過Xilinx core配置端口訪問配置空間

ug517page162( Accessing Registersthrough the Configuration Port)

地址

使用DW地址

將配置空間地址/4得到cfg端口地址

配置空間地址是byte地址

時序

BMD_CFG_CTRL.v

 

PCIe能力結構體

對應着配置空間的60H~98H

 

Device Capabilities Register (Offset 04h)

有CORE配置決定

這個值表示這設備的傳輸能力,由IP核配置

 

測試

 

Device Control Register (Offset 08h)

這個寄存器都是可讀可寫的,用來通過上位機設置硬件設備上的工作能力

 
 
 

測試

 

Device Control Register寄存器對應着cfg_dcommand總線

 
 

最終傳輸到40h寄存器

 

BMD_EP_MEM.v 40H 寄存器

 
 
 

 

cfg_max_rd_req_size          Device Control Register [14:12]

cfg_prg_max_payload_size     Device Control Register [7:5]

cfg_cap_max_payload_size     Device Capabilities Register [2:0]

 

幀結構

dsport關於配置空間的task

自身配置空間

 

對方配置空間


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM