最近在老师的指点下学习了PCIE相关的知识点,同时做一个测试练练手,在这个过程中学习了axis总线,linux使用等,收获颇丰,在此总结一下。
开发设备:装载win10的PC一台
装载ubuntu18的PC一台(PCIE接口)
FPGA开发板(PCIE接口)
vivado18.3
tera term 用于在win上登录ubuntu PC
实验任务:在win上搭建fpga工程上板。编写XDMA的驱动文件达到对数据的读写,至于fpga工程,使用的是之前的读写测试模块,添加了一个CRC的处理模块。关于XDMA的相关知识请查看链接内容https://blog.csdn.net/Njustxiaobai/article/details/132874083。
需要解决的问题:① 将自己写的CRC计算模块做成AXI Stream接口
② XDMA 用128位 crc模块是8位,转换位宽
以下是CRC8_D8模块的代码
module crc8_D8 #(
parameter DATA_WIDTH = 8 // 数据宽度
)(
input wire aclk, // 时钟
input wire aresetn, // 复位信号,低电平有效
input wire [DATA_WIDTH-1:0] s_axis_tdata, // 接收数据
input wire s_axis_tlast, // 标识数据包的最后一个字节
input wire s_axis_tvalid, // 有效信号,表明数据和用户信号是有效的
output wire s_axis_tready, // 准备就绪信号,表明模块可以接收数据
output wire [DATA_WIDTH-1:0] m_axis_tdata, // 发送数据
output wire m_axis_tlast, // 标识数据包的最后一个字节
output wire m_axis_tvalid, // 有效信号,表明数据是有效的
input wire m_axis_tready // 准备就绪信号,表明接收模块可以接收数据
);
// 内部信号定义
reg [DATA_WIDTH-1:0] data_reg;
reg last_reg;
reg valid_reg;
reg ready_reg;
reg [DATA_WIDTH-1:0] lfsr_c ;
reg [DATA_WIDTH-1:0] lfsr_q ;
// 状态机,处理AXI Stream数据流
always @(posedge aclk) begin
if (!aresetn) begin
valid_reg <= 1'b0;
ready_reg <= 1'b1;
end else begin
if (s_axis_tvalid & ready_reg) begin
data_reg <= lfsr_q;
last_reg <= s_axis_tlast;
valid_reg <= 1'b1;
end else if (m_axis_tready & valid_reg) begin
valid_reg <= 1'b0;
end
ready_reg <= m_axis_tready;
end
end
always @(posedge aclk or negedge aresetn) begin
if (!aresetn) begin
lfsr_q <= {DATA_WIDTH{1'b1}}; // 异步复位
end else begin
lfsr_q <= lfsr_c; // 每个时钟周期更新lfsr_q
end
end
// 根据状态机输出信号
assign m_axis_tdata = data_reg;
assign m_axis_tlast = last_reg;
assign m_axis_tvalid = valid_reg;
assign s_axis_tready = ready_reg;
always @(*) begin
lfsr_c[0] = lfsr_q[0] ^ lfsr_q[1] ^ lfsr_q[2] ^ lfsr_q[3] ^ lfsr_q[4] ^ lfsr_q[5] ^ lfsr_q[6] ^ s_axis_tdata[0] ^ s_axis_tdata[1] ^ s_axis_tdata[2] ^ s_axis_tdata[3] ^ s_axis_tdata[4] ^ s_axis_tdata[5] ^ s_axis_tdata[6];
lfsr_c[1] = lfsr_q[0] ^ lfsr_q[7] ^ s_axis_tdata[0] ^ s_axis_tdata[7];
lfsr_c[2] = lfsr_q[1] ^ s_axis_tdata[1];
lfsr_c[3] = lfsr_q[2] ^ s_axis_tdata[2];
lfsr_c[4] = lfsr_q[3] ^ s_axis_tdata[3];
lfsr_c[5] = lfsr_q[4] ^ s_axis_tdata[4];
lfsr_c[6] = lfsr_q[5] ^ s_axis_tdata[5];
lfsr_c[7] = lfsr_q[0] ^ lfsr_q[1] ^ lfsr_q[2] ^ lfsr_q[3] ^ lfsr_q[4] ^ lfsr_q[5] ^ s_axis_tdata[0] ^ s_axis_tdata[1] ^ s_axis_tdata[2] ^ s_axis_tdata[3] ^ s_axis_tdata[4] ^ s_axis_tdata[5];
end
// always
// always @(posedge clk, negedge rst_n) begin
// if(!rst_n) begin
// lfsr_q <= {8{1'b1}};
// end
// else begin
// lfsr_q <= crc_en ? lfsr_c : lfsr_q;
// end
// end // always
endmodule // crc
在自行设计axis接口时有一个小技巧,可以去tools里找到自定义/封装ip功能,创建一个axis接口的ip,创建成功后,vivado会自动生成v文件,可以打开看看,作为参考,另外正点原子也有关于自定义IP的教程,可以看一下。注意一点,设计此类接口不能只创建接口而不管接口逻辑,如果没有的话在后续生成bit流文件可能会有问题。当时笔者设计的时候不小心把tready接口弄反了,导致综合的时候报错某一个LUT没有数据,最后打开网表图一层一层找到LUT才发现的问题,所以一开始设计就要规划好。
接下来就是整个设计的block design图
图中可以看到,从H2C接口出来的信号先进入一个axis fifo,然后进入了 axis_dwidth_converter这个IP,这个ip的作用是将128位的数据截成8位交给crc模块处理,所以在后面同样有一个axis_converter,用于8位合并成128位的数据到C2H接口。
由于这次的实验没办法像之前的实验那样写一个tb文件去做仿真,所以在老师的指点下学习了debug的方法,本工程内运用的是ila,教程移步链接:https://blog.csdn.net/unique_ZRF/article/details/127715565
需要注意的是,每个slot,都有不同的模板,在本实验中由于所有的接口基本上都是axis的,所以选用axis rtl模板,否则无法链接接口。
由于这是之前的工程,所以综合排错以后就不需要做管脚约束和时序约束,直生成bit流文件上版。接下来移步ubuntu。
linux端:
使用tera term在win上对linux远程操作,不需要GUI界面。注意通过网络连接要求两台设备同一IP域中。
登录后输入lspci查看pcie设备,发现并没有xilinx设备。了解资料后发现需要reboot一下。
重启后成功识别设备。
安装驱动,过程如下。
检查xdma设备,至此,我们的设备成功安装了驱动并且检测到了pcie设备。
关于驱动:
简单概括,data文件夹存放测试用的数据和测试完的输出数据文件;
dma_memory_mapped_test.sh是地址映射脚本
dma_streaming_test.sh是流传输脚本
load_driver.sh是加载驱动脚本
perform_hwcount.sh是对性能的评估脚本
run_test.sh为测试脚本
主要的关注点就是测试脚本和流传输脚本,打开测试脚本我们可以了解驱动的工作原理:首先会根据用户编写的fpga项目来判断是流传输模式还是地址映射模式,根据不同的模式选择不同的测试方案,而测试方案将data中的数据文件用于测试,最后输出文件至文件夹,并给出性能评估。对于本次实验,笔者用的是stream传输模式,因此只需要修改dma_streaming_test.sh即可。
修改好以后,打包dma_streaming_test.sh和数据文件通过tera term的scp传输给linux。
linux,将文件替换进XDMA下的tests文件。至此,就可以开始测试了。
运行run_test即可完成测试。
回到正文,装好驱动以后,通过ila去debug工程。
这是一个漫长的工作过程,但是方法只有一个,就是一个接口一个接口的反复去抓信号,确定哪个接口的数据出了问题,然后修改,再抓,调试完毕后,通过scp输出rx和tx文件做对比,如下(二进制文件用notepad++打开)
收获与反思:在这次实验里,接触了PCIE,axis总线协议,在线debug等许多知识,让我离独立开发项目又近了一步,但是与此同时,我发现此次实验大多还是基于以前的工程,如管脚约束和时序约束,尤其是时序约束,没有亲手去操作一下。