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; }