增加xdma相关学习

main
阳光少年 8 months ago
parent 0e392272f9
commit 0bf2017e9e

@ -0,0 +1,177 @@
module axi_bram #(
parameter AXI_IDWIDTH = 4, // AXI的id宽度
parameter AXI_AWIDTH = 64, // AXI的 地址总线宽度
parameter AXI_DWIDTH = 256, // AXI的数据总线宽度 一次读取的数据位数
parameter MEM_AWIDTH = 12 // BRAM size = MEM_AWIDTH*C_M_AXI_DATA_WIDTH (bits) = MEM_AWIDTH*C_M_AXI_DATA_WIDTH/8 (bytes)
) (
input wire rstn,
input wire clk,
// AXI-MM AW interface ----------------------------------------------------
output wire s_axi_awready, // 从写地址准备好
input wire s_axi_awvalid, // 主准备好
input wire [ AXI_AWIDTH-1:0] s_axi_awaddr,
input wire [ 7:0] s_axi_awlen,
input wire [ AXI_IDWIDTH-1:0] s_axi_awid,
// AXI-MM W interface ----------------------------------------------------
output wire s_axi_wready,
input wire s_axi_wvalid,
input wire s_axi_wlast,
input wire [ AXI_DWIDTH-1:0] s_axi_wdata,
input wire [(AXI_DWIDTH/8)-1:0] s_axi_wstrb,
// AXI-MM B interface ----------------------------------------------------
input wire s_axi_bready,
output wire s_axi_bvalid,
output wire [ AXI_IDWIDTH-1:0] s_axi_bid,
output wire [ 1:0] s_axi_bresp,
// AXI-MM AR interface ----------------------------------------------------
output wire s_axi_arready, // 表示S 已经可以接受 读地址信号
input wire s_axi_arvalid, // 表示M 地址已经准备就绪
input wire [ AXI_AWIDTH-1:0] s_axi_araddr, // M 发送的地址
input wire [ 7:0] s_axi_arlen, // 突发长度
input wire [ AXI_IDWIDTH-1:0] s_axi_arid,
// AXI-MM R interface ----------------------------------------------------
input wire s_axi_rready, // 只有当M ready的时候, S才可以发数据
output wire s_axi_rvalid, // S的数据已经准备好, 可以发送到M
output wire s_axi_rlast,
output reg [ AXI_DWIDTH-1:0] s_axi_rdata,
output wire [ AXI_IDWIDTH-1:0] s_axi_rid,
output wire [ 1:0] s_axi_rresp
);
function automatic int log2 (input int x);
int xtmp = x, y = 0;
while (xtmp != 0) begin
y ++;
xtmp >>= 1;
end
return (y == 0) ? 0 : (y - 1);
endfunction
// ---------------------------------------------------------------------------------------
// AXI READ state machine
// ---------------------------------------------------------------------------------------
enum reg [0:0] {R_IDLE, R_BUSY} rstate = R_IDLE;
reg [AXI_IDWIDTH-1:0] rid = '0; // 从 读地址 传过来的id
reg [ 7:0] rcount = '0; // 从 读地址 传过来的读取次数
reg [ MEM_AWIDTH-1:0] mem_raddr, mem_raddr_last; // 当前操作的地址, 和上次操作的地址
assign s_axi_arready = (rstate == R_IDLE); // 读地址 可以进行 接收
assign s_axi_rvalid = (rstate == R_BUSY); // 读数据 以准备就绪
assign s_axi_rlast = (rstate == R_BUSY) && (rcount == 8'd0); // 是否是最后一个数据, 直接连线
assign s_axi_rid = rid; // 直接连线
assign s_axi_rresp = '0;
always @ (posedge clk or negedge rstn)
if (~rstn) begin
rstate <= R_IDLE;
rid <= '0;
rcount <= '0;
end
else begin
case (rstate)
R_IDLE :
if (s_axi_arvalid) begin // M发起 读地址 请求
rstate <= R_BUSY; // S切换到忙碌
rid <= s_axi_arid; // 记录本次 M读地址 ID
rcount <= s_axi_arlen; // 记录本次 M的读长度
end
R_BUSY :
if (s_axi_rready) begin
if (rcount == 8'd0) // 如果是 忙碌状态, 且 是最后一个数据
rstate <= R_IDLE; // 则切换 S状态 为 空闲, 状态结束
rcount <= rcount - 8'd1; // 每次忙碌状态 rcount 都会减1
end
endcase
end
// 组合逻辑块,根据当前状态计算内存读地址
always_comb
if (rstate == R_IDLE && s_axi_arvalid) // 如果空闲状态 且 M已经准备好地址
mem_raddr = (MEM_AWIDTH)'(s_axi_araddr >> log2(AXI_DWIDTH/8)); // 计算地址,
// 因为每次数据都会 传256位数据, 这里除以8, 得到需要传的次数, 每次1字节传输, 256/8就是32次
// 看能整除多少次2, 然后 s_axi_araddr 进行右移, 移动到传过来的地址的 AXI_DWIDTH的字节边界 start起始的位置
else if (rstate == R_BUSY && s_axi_rready) // 如果繁忙, 且 M 已经准备好 接收 S发来的数据
mem_raddr = mem_raddr_last + (MEM_AWIDTH)'(1); // 计算下一个内存地址, 加一即可, 这是一个二维数组
else
mem_raddr = mem_raddr_last; // 保持地址不变
// 在每个时钟上升沿,更新上一次的读地址
always @ (posedge clk)
mem_raddr_last <= mem_raddr;
// ---------------------------------------------------------------------------------------
// AXI WRITE state machine
// ---------------------------------------------------------------------------------------
enum reg [1:0] {W_IDLE, W_BUSY, W_RESP} wstate = W_IDLE;
reg [AXI_IDWIDTH-1:0] wid = '0;
reg [ 7:0] wcount = '0;
reg [ MEM_AWIDTH-1:0] mem_waddr = '0;
assign s_axi_awready = (wstate == W_IDLE);
assign s_axi_wready = (wstate == W_BUSY);
assign s_axi_bvalid = (wstate == W_RESP);
assign s_axi_bid = wid;
assign s_axi_bresp = '0;
always @ (posedge clk or negedge rstn)
if (~rstn) begin
wstate <= W_IDLE;
wid <= '0;
wcount <= '0;
mem_waddr <= '0;
end else begin
case (wstate)
W_IDLE :
if (s_axi_awvalid) begin
wstate <= W_BUSY;
wid <= s_axi_awid;
wcount <= s_axi_awlen;
mem_waddr <= (MEM_AWIDTH)'(s_axi_awaddr >> log2(AXI_DWIDTH/8));
end
W_BUSY :
if (s_axi_wvalid) begin
if (wcount == 8'd0 || s_axi_wlast)
wstate <= W_RESP;
wcount <= wcount - 8'd1;
mem_waddr <= mem_waddr + (MEM_AWIDTH)'(1);
end
W_RESP :
if (s_axi_bready)
wstate <= W_IDLE;
default :
wstate <= W_IDLE;
endcase
end
// ---------------------------------------------------------------------------------------
// a block RAM
// ---------------------------------------------------------------------------------------
reg [AXI_DWIDTH-1:0] mem [ 1<<MEM_AWIDTH ]; // 这是一个二维数组
always @ (posedge clk)
s_axi_rdata <= mem[mem_raddr]; // 一次读出 AXI_DWIDTH 位的数据
always @ (posedge clk)
if (s_axi_wvalid & s_axi_wready)
for (int i=0; i<(AXI_DWIDTH/8); i++)
if (s_axi_wstrb[i])
mem[mem_waddr][i*8+:8] <= s_axi_wdata[i*8+:8];
endmodule

@ -0,0 +1,220 @@
module fpga_top (
output wire o_led0,
input wire i_pcie_rstn,
input wire i_pcie_refclkp, i_pcie_refclkn,
input wire [0:0] i_pcie_rxp, i_pcie_rxn,
output wire [0:0] o_pcie_txp, o_pcie_txn
);
localparam AXI_IDWIDTH = 4;
localparam AXI_AWIDTH = 64;
localparam AXI_DWIDTH = 64;
wire pcie_rstn;
wire pcie_refclk;
wire rstn;
wire clk;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PCIe XDMA's AXI interface
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
wire axi_awready;
wire axi_awvalid;
wire [ AXI_AWIDTH-1:0] axi_awaddr;
wire [ 7:0] axi_awlen;
wire [ AXI_IDWIDTH-1:0] axi_awid;
// AXI Master Write Data Channel
wire axi_wready;
wire axi_wvalid;
wire axi_wlast;
wire [ AXI_DWIDTH-1:0] axi_wdata;
wire [(AXI_DWIDTH/8)-1:0] axi_wstrb;
// AXI Master Write Response Channel
wire axi_bready;
wire axi_bvalid;
wire [ AXI_IDWIDTH-1:0] axi_bid;
wire [ 1:0] axi_bresp;
// AXI Master Read Address Channel
wire axi_arready;
wire axi_arvalid;
wire [ AXI_AWIDTH-1:0] axi_araddr;
wire [ 7:0] axi_arlen;
wire [ AXI_IDWIDTH-1:0] axi_arid;
// AXI Master Read Data Channel
wire axi_rready;
wire axi_rvalid;
wire axi_rlast;
wire [ AXI_DWIDTH-1:0] axi_rdata;
wire [ AXI_IDWIDTH-1:0] axi_rid;
wire [ 1:0] axi_rresp;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Ref clock input buffer
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
IBUFDS_GTE2 refclk_ibuf (
.CEB ( 1'b0 ),
.I ( i_pcie_refclkp ),
.IB ( i_pcie_refclkn ),
.O ( pcie_refclk ),
.ODIV2 ( )
);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Reset input buffer
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
IBUF sys_reset_n_ibuf (
.I ( i_pcie_rstn ),
.O ( pcie_rstn )
);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PCIe XDMA core
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
xdma_0 xdma_0_i (
// PCI Express (PCIe) Interface : connect to the pins of FPGA chip
.sys_rst_n ( pcie_rstn ),
.sys_clk ( pcie_refclk ),
.pci_exp_txn ( o_pcie_txn ),
.pci_exp_txp ( o_pcie_txp ),
.pci_exp_rxn ( i_pcie_rxn ),
.pci_exp_rxp ( i_pcie_rxp ),
// PCIe link up
.user_lnk_up ( o_led0 ),
// interrupts
.usr_irq_req ( 16'h0 ),
.usr_irq_ack ( ),
//
.msix_enable ( ),
// clock/reset for user (for AXI)
.axi_aclk ( clk ),
.axi_aresetn ( rstn ),
// AXI Interface
.m_axi_awready ( axi_awready ),
.m_axi_awvalid ( axi_awvalid ),
.m_axi_awaddr ( axi_awaddr ),
.m_axi_awlen ( axi_awlen ),
.m_axi_awid ( axi_awid ),
.m_axi_awsize ( ),
.m_axi_awburst ( ),
.m_axi_awprot ( ),
.m_axi_awlock ( ),
.m_axi_awcache ( ),
.m_axi_wready ( axi_wready ),
.m_axi_wvalid ( axi_wvalid ),
.m_axi_wdata ( axi_wdata ),
.m_axi_wstrb ( axi_wstrb ),
.m_axi_wlast ( axi_wlast ),
.m_axi_bready ( axi_bready ),
.m_axi_bvalid ( axi_bvalid ),
.m_axi_bid ( axi_bid ),
.m_axi_bresp ( axi_bresp ),
.m_axi_arready ( axi_arready ),
.m_axi_arvalid ( axi_arvalid ),
.m_axi_araddr ( axi_araddr ),
.m_axi_arlen ( axi_arlen ),
.m_axi_arid ( axi_arid ),
.m_axi_arsize ( ),
.m_axi_arburst ( ),
.m_axi_arprot ( ),
.m_axi_arlock ( ),
.m_axi_arcache ( ),
.m_axi_rready ( axi_rready ),
.m_axi_rvalid ( axi_rvalid ),
.m_axi_rlast ( axi_rlast ),
.m_axi_rdata ( axi_rdata ),
.m_axi_rid ( axi_rid ),
.m_axi_rresp ( axi_rresp ),
// AXI bypass interface
.m_axib_awready ( '0 ),
.m_axib_awvalid ( ),
.m_axib_awaddr ( ),
.m_axib_awlen ( ),
.m_axib_awid ( ),
.m_axib_awsize ( ),
.m_axib_awburst ( ),
.m_axib_awprot ( ),
.m_axib_awlock ( ),
.m_axib_awcache ( ),
.m_axib_wready ( '0 ),
.m_axib_wvalid ( ),
.m_axib_wlast ( ),
.m_axib_wdata ( ),
.m_axib_wstrb ( ),
.m_axib_bready ( ),
.m_axib_bvalid ( '0 ),
.m_axib_bid ( '0 ),
.m_axib_bresp ( '0 ),
.m_axib_arready ( '0 ),
.m_axib_arvalid ( ),
.m_axib_araddr ( ),
.m_axib_arlen ( ),
.m_axib_arid ( ),
.m_axib_arsize ( ),
.m_axib_arburst ( ),
.m_axib_arprot ( ),
.m_axib_arlock ( ),
.m_axib_arcache ( ),
.m_axib_rready ( ),
.m_axib_rvalid ( '0 ),
.m_axib_rlast ( '0 ),
.m_axib_rdata ( '0 ),
.m_axib_rid ( '0 ),
.m_axib_rresp ( '0 )
);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AXI BRAM connected to PCIe XDMA's AXI interface
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
axi_bram #(
.AXI_IDWIDTH ( AXI_IDWIDTH ),
.AXI_AWIDTH ( AXI_AWIDTH ),
.AXI_DWIDTH ( AXI_DWIDTH ),
.MEM_AWIDTH ( 14 ) // BRAM size = MEM_AWIDTH*AXI_DWIDTH (bits) = MEM_AWIDTH*AXI_DWIDTH/8 (bytes)
) axi_bram_i (
.rstn ( rstn ),
.clk ( clk ),
// AXI Memory Mapped interface
.s_axi_awready ( axi_awready ),
.s_axi_awvalid ( axi_awvalid ),
.s_axi_awaddr ( axi_awaddr ),
.s_axi_awlen ( axi_awlen ),
.s_axi_awid ( axi_awid ),
.s_axi_wready ( axi_wready ),
.s_axi_wvalid ( axi_wvalid ),
.s_axi_wlast ( axi_wlast ),
.s_axi_wdata ( axi_wdata ),
.s_axi_wstrb ( axi_wstrb ),
.s_axi_bready ( axi_bready ),
.s_axi_bvalid ( axi_bvalid ),
.s_axi_bid ( axi_bid ),
.s_axi_bresp ( axi_bresp ),
.s_axi_arready ( axi_arready ),
.s_axi_arvalid ( axi_arvalid ),
.s_axi_araddr ( axi_araddr ),
.s_axi_arlen ( axi_arlen ),
.s_axi_arid ( axi_arid ),
.s_axi_rready ( axi_rready ),
.s_axi_rvalid ( axi_rvalid ),
.s_axi_rlast ( axi_rlast ),
.s_axi_rdata ( axi_rdata ),
.s_axi_rid ( axi_rid ),
.s_axi_rresp ( axi_rresp )
);
endmodule
// 1000_0000_0000_0000_0000

@ -0,0 +1,216 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/timeb.h>
// 函数:从设备读取数据到本地内存(缓冲区)
// 功能:实现设备到主机的数据传输
// 参数:
// dev_fd : 设备实例句柄
// addr : 设备中的源地址
// buffer : 缓冲区基指针
// size : 数据大小
// 返回值:
// 成功0失败-1
int dev_read (int dev_fd, uint64_t addr, void *buffer, uint64_t size) {
if ( addr != lseek(dev_fd, addr, SEEK_SET) ) // 调整文件偏移到指定地址
return -1; // 偏移失败
if ( size != read(dev_fd, buffer, size) ) // 从设备读取数据到缓冲区
return -1; // 读取失败
return 0;
}
// 函数:从本地内存(缓冲区)写数据到设备
// 功能:实现主机到设备的数据传输
// 参数:
// dev_fd : 设备实例句柄
// addr : 设备中的目标地址
// buffer : 缓冲区基指针
// size : 数据大小
// 返回值:
// 成功0失败-1
int dev_write (int dev_fd, uint64_t addr, void *buffer, uint64_t size) {
if ( addr != lseek(dev_fd, addr, SEEK_SET) )
return -1;
if ( size != write(dev_fd, buffer, size) )
return -1;
return 0;
}
// 函数:获取毫秒级别的时间
// 功能:返回当前时间的毫秒数
static uint64_t get_millisecond () {
struct timeb tb;
ftime(&tb);
return (uint64_t)tb.millitm + (uint64_t)tb.time * 1000UL;
// tb.time is the number of seconds since 00:00:00 January 1, 1970 UTC time;
// tb.millitm is the number of milliseconds in a second
}
// 函数:解析无符号整数
// 功能从字符串中解析出十六进制或十进制的无符号64位整数
// 参数:
// string : 输入的字符串
// pvalue : 解析后的值的指针
// 返回值:
// 成功解析的项数失败则为0
int parse_uint (char *string, uint64_t *pvalue) {
if ( string[0] == '0' && string[1] == 'x' ) // 十六进制格式"0xXXXXXXXX"
return sscanf( &(string[2]), "%lx", pvalue);
else // 十进制格式
return sscanf( string , "%lu", pvalue);
}
#define DMA_MAX_SIZE 0x10000000UL // DMA操作的最大尺寸定义 256kb
char USAGE[] =
"Usage: \n"
"\n"
" 写入设备(主机到设备):\n"
" %s <文件名> to <设备名> <设备内地址> <大小>\n"
" 示例:\n"
" %s data.bin to /dev/xdma0_h2c_0 0x100000 0x10000\n"
"\n"
" 读取设备(设备到主机):\n"
" %s <文件名> from <设备名> <设备内地址> <大小>\n"
" 示例:\n"
" %s data.bin from /dev/xdma0_c2h_0 0x100000 0x10000\n";
int main (int argc, char *argv[]) {
int ret = -1;
uint64_t millisecond;
char usage_string [1024];
char *dev_name, *file_name;
char direction;
uint64_t address, size;
int dev_fd = -1;
FILE *file_p = NULL;
void *buffer = NULL;
sprintf(usage_string, USAGE, argv[0], argv[0], argv[0], argv[0] );
if (argc < 6) { // not enough argument
puts(usage_string);
return -1;
}
file_name = argv[1];
direction = argv[2][0];
dev_name = argv[3];
if ( 0 == parse_uint(argv[4], &address) ) { // parse the address in the device
puts(usage_string);
return -1;
}
if ( 0 == parse_uint(argv[5], &size ) ) { // parse the size in the device
puts(usage_string);
return -1;
}
// print information:
if (direction == 't') { // to (write device, host-to-device)
printf("from : %s\n" , file_name);
printf("to : %s addr=0x%lx\n" , dev_name, address);
printf("size : 0x%lx\n\n" , size);
} else if (direction == 'f') { // from (read device, device-to-host)
printf("from : %s addr=0x%lx\n" , dev_name, address);
printf("to : %s\n" , file_name);
printf("size : 0x%lx\n\n" , size);
} else {
puts(usage_string);
return -1;
}
if (size > DMA_MAX_SIZE || size == 0) {
printf("*** ERROR: DMA size must larger than 0 and NOT be larger than %lu\n", DMA_MAX_SIZE);
return -1;
}
buffer = malloc(size); // allocate local memory (buffer)
if (buffer == NULL) {
printf("*** ERROR: failed to allocate buffer\n");
goto close_and_clear;
}
dev_fd = open(dev_name, O_RDWR); // open device
if (dev_fd < 0) {
printf("*** ERROR: failed to open device %s\n", dev_name);
goto close_and_clear;
}
file_p = fopen(file_name, direction == 't' ? "rb" : "wb"); // open file for read/write
if (file_p == NULL) {
printf("*** ERROR: failed to open file %s\n", file_name);
goto close_and_clear;
}
millisecond = get_millisecond(); // start time
if (direction == 't') {
if ( size != fread(buffer, 1, size, file_p) ) { // local file -> local buffer
printf("*** ERROR: failed to read %s\n", file_name);
goto close_and_clear;
}
if ( dev_write(dev_fd, address, buffer, size) ) { // local buffer -> device
printf("*** ERROR: failed to write %s\n", dev_name);
goto close_and_clear;
}
} else {
if ( dev_read(dev_fd, address, buffer, size) ) { // device -> local buffer
printf("*** ERROR: failed to read %s\n", dev_name);
goto close_and_clear;
}
if ( size != fwrite(buffer, 1, size, file_p) ) { // local buffer -> local file
printf("*** ERROR: failed to write %s\n", file_name);
goto close_and_clear;
}
}
millisecond = get_millisecond() - millisecond; // get time consumption
millisecond = (millisecond > 0) ? millisecond : 1; // avoid divide-by-zero
printf("time=%lu ms data rate=%.1lf KBps\n", millisecond, (double)size / millisecond );
ret = 0;
close_and_clear:
if (buffer != NULL)
free(buffer);
if (dev_fd >= 0)
close(dev_fd);
if (file_p != NULL)
fclose(file_p);
return ret;
}
Loading…
Cancel
Save