XDMA核的使用
一、 XDMA相關知識
絕對地址就是物理地址=段地址*16+偏移地址,也就是段地址<<4+偏移地址
主機host通過PCIe接口訪問DMA,DMA即外部設備不通過CPU而直接與系統內存(DDR)交換數據。
PIO模式下硬盤和內存之間的數據傳輸是通過CPU來控制的,而在DMA模式下,CPU只需向DMA控制下達命令,讓DMA來控制數據的發送,數據傳送完畢后再把數據反饋給CPU,這樣很大程度上減輕了 CPU的資源占有率。
DMA和PIO模式的區別就在於,DMA模式不過分依賴CPU,可以大大的節省系統資源,二者在傳輸速度上的差異並不明顯。
PIO模式:CPU通過執行端口IO指令來進行數據讀寫的交換方式。CPU占有率高。
在例程中數據傳輸使用XDMA方式,與DMA相同,CPU通過向DMA發送指令完成數據的讀寫。
讀寫部分分為兩種,一種是數據的讀寫,另一種數配置數據的讀寫,在數據讀寫部分,DMA通過MIG控制DDR完成數據讀寫。配置數據讀寫通過與BRAM通過AXI-lite總線連接完成,XDMA將PCIe配置信息存在BRAM,在進行配置信息讀寫時,將傳入主機映射到用戶邏輯的地址,然后與偏移地址處理(物理地址=段地址<<4+偏移地址),所以在bram設置時需要將其偏移地址設置的與主機地址映射的偏移地址相同。
對於DDR則不必,設置的話還減少了可使用的內存空間,只是一個袋子,寫在哪里就從哪里讀取即可,必須設置為0。
XDMA ip core的配置:
1、 Basic
Functional mode:功能模式,即DMA模式。
Mode:模式,選擇basic即可,basic與advanced的區別在於advanced模式開放更多的可選選項與功能,basic的話為默認。
Device/Port Type:選擇設備與端口類型,為端點設備。
PCIe Block Location:從可用的集成塊中選擇,以啟用生成特定位置的約束文件和輸出,產品能夠pg054datasheet截取的位置說明P249。
Lane width:通道寬度,根據接口進行選擇。
AXI Address width:AXI地址寬度選擇,只支持64bit(用戶手冊73頁)。
AXI Data Width:AXI總線上傳輸的數據寬度,可以是64bit、128bit、256bit、512bit(這個只有ultrascale+可以滿足,一分錢一分貨),根據datasheet進行配置即可P249,見下圖。
DMA Interface option:DMA的接口類型用於數據傳輸,有兩種AXI MM(memory mapped)與AXI ST(stream),二選一。
PCIe ID:
見之前推送。
PCIe BARs:
PCIe to AXI Lite Master Interface:使能,這樣可以在主機一側通過PCIe來訪問用戶邏輯側寄存器或者其他AXI-Lite總線設備。
此處將配置信息存儲到BRAM,通過AXI-lite總線讀寫Bram。
(1)、BAR為32bit,不使能64bit,prefetchable表示預讀取,不使能。
(2)、映射空間選擇1M,大小隨意。
(3)、PCIe to AXI Translation:主機側BAR地址與用戶邏輯側地址不同,通過設置轉換地址實現BAR地址到AXI地址的轉換。比如主機一側BAR地址為0,則主機訪問BAR地址0轉換到AXI-Lite總線就是0x8000_0000.
PCIe to DMA Interface:數據傳輸寬度64bit,DMA控制器一般只支持數據8字節對齊的情況。
當數據從上位機通過PCIe接口發送到端點設備,XDMA內部自行解包對將數據與指令進行分析,得到讀寫操作的指令地址,並對DDR進行讀寫操作。操作的結果通過AXI接口返回XDMA,XDMA對數據進行組包,之后通過物理層發出,實現數據的DMA控制。
二、上位機設計
2.1 h2c_transfer函數
該函數的作用是向DDR中寫入數據,寫入大小固定的數據,根據寫入時間計算傳輸的速度(MB/s)。
1、變量定義
Double型變量bd,用於寄存傳輸速度。
Double型變量time_sec,用於保存傳輸定量數據所需時間。
LARGE_INTEGER型變量變量start和stop用於保存頻率計數值。還有freq,用於保存機器內部計時器的時鍾頻率。
2、函數操作
2.1 獲取傳輸所需時間
獲取傳輸所需時間,則需要三個量:機器時鍾頻率,傳輸開始前后計數器的計數值。
獲取機器內部計時器的時鍾
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start);
…… //數據傳輸
QueryPerformanceCounter(&stop);
頭文件為<Windows.h>,函數原型為:
BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency);
BOOL QueryPerformanceCounter (LARGE_INTEGER *lpCount);
在定時前先調用QueryPerformanceFrequency函數獲得機器內部計時器的時鍾頻率,然后在嚴格計時的時間發生前后調用QueryPerformanceCounter函數,獲得兩次計數的差值經過轉換得到事件耗費的精准時間。
LARGE_INTEGER為一個typedef定義的變量,其原型為:
typeef union _ LARGE_INTEGER
{
struct
{
DWORD LowPart;
LONG HighPart;
};
LONGLONG QuadPart;
} LARGE_INTEGER;
2.2 數據寫入操作
使用的是自定義的write_device()函數,操作語句為:write_device(h2c0_device,FPGA_DDR_START_ADDR+0x20000000,size,h2c_align_mem_tmp);四個參數分別為:設備句柄,傳輸目的地址,傳輸的數據大小(單位為字節),寫入數據的緩存區地址。
在write_device函數中,重點是變量與函數語句:
變量:
DWORD wr_size:用於保存寫入的字節數
unsigned int transfers;傳輸次數,每次傳輸的最大字節數為0x800000,也就是8MB,根據要傳輸的字節數計算所需傳輸的次數。
函數語句:
數據的寫入通過WriteFile()函數完成,操作語句為WriteFile(device, (void *)(buffer+i*MAX_BYTES_PER_TRANSFER), MAX_BYTES_PER_TRANSFER, &wr_size, NULL),傳入參數分別為:系統文件句柄,寫入數據的存儲地址,每次傳輸的字節數,成功寫入的字節數。
WriteFile()函數的返回值為bool類型,操作成功為true,操作失敗返回-1,實際寫入的字節數與設定的寫入字節數不同時返回-2。
在數據寫入之前,需要先設置一個文件在設備的對應地址中的操作位置,FILE_BEGIN表示在文件的起始位置接收傳輸的數據。
unsigned int h2c_transfer(unsigned int size)
{
double bd=0;
double time_sec;
LARGE_INTEGER start;
LARGE_INTEGER stop;
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start); write_device(h2c0_device,FPGA_DDR_START_ADDR+0x20000000,size,h2c_align_mem_tmp);
QueryPerformanceCounter(&stop);
time_sec = (unsigned long long)(stop.QuadPart - start.QuadPart) / (double)freq.QuadPart;
bd = ((double)size)/(double)(time_sec)/1024.0/1024.0;
return (unsigned int)bd;
}
2.3 數據讀取操作
與數據寫入類似,讀取的起始地址為寫入時的起始地址。
unsigned int c2h_transfer(unsigned int size)
{
double bd=0;
double time_sec;
LARGE_INTEGER start;
LARGE_INTEGER stop;
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&start);
read_device(c2h0_device,FPGA_DDR_START_ADDR+0x10000000,size,c2h_align_mem_tmp);
QueryPerformanceCounter(&stop);
time_sec = (unsigned long long)(stop.QuadPart - start.QuadPart) / (double)freq.QuadPart;
bd = ((double)size)/(double)(time_sec)/1024.0/1024.0;
return (unsigned int)bd;
}
三、基於PCIe的下位機設計
3.1 簡介
首先是選用一個AD轉換器進行電壓數據的采集,之后自定義一個AXI—Full類型的IP封裝,調用XDMA ip core連接AXI封裝,AXI封裝將連接一個fifo模塊,從fifo中讀取出來的數據通過AXI-Full總線送至XDMA的AXI主接口,XDMA會將數據進行自動封裝,封裝成TLP包,等到上位機發出數據讀取請求時候,將數據發送到上位機進行波形顯示。
PCIe設計模塊:
分析其信號:
1.1 輸入信號
pkg_rdy: package ready信號,表示數據部分准備好進行讀取。
pkg_data[63:0]: 64bit的數據輸入。
pcie_ref:XDMA的驅動時鍾輸入,雙極性,經過一個緩沖之后接入到XDMA ip core。
pcie_rst_n: XDMA復位控制。
1.2 輸出信號
pkg_rd_en: 由AXI Slave core發出的讀取fifo使能信號。
pcie_mgt: PCIe電氣接口信號,TX與RX,7030為PCIe2.0,lane width: x2。
axi_aclk:AXI總線驅動時鍾,該時用於fifo的數據讀取。
Clk_50M:通過時鍾功能核clocking Wizard生成,作用是驅動AD7606的驅動。
Rst_n:復位信號,控制AD驅動與fifo的復位。
3.2 項目設計
1.1 AD驅動設計
AD采集芯片為AD7606,按照數據與例程設計。
1.2 fifo使用
將AD采集得到的數據存儲到fifo中,fifo的接口類型為本地native類型,fifo的實現方式為獨立時鍾的塊ram,即數據的讀取與寫入使用獨立的時鍾,寫入的數據寬度為64bit,是AD0~AD3的16bit數據拼接得到的64bit寫入數據,寫入深度為4096,數據讀取同。引出寫入數據的計數器,用於通知AXI-Salve core數據讀取允許。
通過AD采集模塊的數據采集完成標志作為fifo數據寫入的使能信號,寫入的數據個數通過Write Data Count來指示,以下為摘取自fifo datasheet中關於該信號的介紹:
Write Data Count:
Write Data Count: This bus indicates the number of words written into the FIFO. The count is guaranteed to never under-report the number of words in the FIFO, to ensure you never overflow the FIFO. The exception to this behavior is when a write operation occurs at the rising edge of wr_clk/clk, that write operation will only be reflected on wr_data_count at the next rising clock edge. If D is less than log2(FIFO depth)-1, the bus is truncated by removing the least-significant bits.
Note: wr_data_count is also available for UltraScale devices using a common clock Block RAM-based FIFO when the Asymmetric Port Width option is enabled.
注:datasheet真是個好東西,問啥告訴啥!
使用fifo的半滿信號作為AXI Slave ip core的包准備好信號。
AXI Slave ip core在輸入的包讀取使能信號(pkg_rdy)之后,將自身的valid信號置一,等待AXI總線可以進行數據讀取,輸入數據為fifo的內部數據。
四、上位機設計
上位機顯示使用了QWT環境,QWT環境的設置可以參考博文:https://www.cnblogs.com/luxinshuo/p/12316037.html。
QT中的程序最重要的一點為設置一個定時器,定時器定時滿后關聯數據更新槽函數。