ZYNQ_PLPS_LOOP
摘要:在ZYNQ中设计了自定义的PL端数据处理器,通过DMA连接到AXI总线,完成了PS和该PL端的数据交互等功能。
开发板型号:Zynq-7000 SoC XC7Z305 FPGA
开发平台:Vivado 2019.1; Vivado SDK 2019.1
Github源码:https://github.com/CY0807/Vivado_FIFO_Test.git
1 文件描述(文件见GitHub仓库)
(1)vivado_project存放了vivado和sdk原始工程文件
(2)c_project_demo存放了sdk工程中所用的核心代码
(3)image中存放了项目运行中间过程的重要截图
2 硬件设计
Block Design如图所示:
(1)例化了vivado库的AXI DMA挂在ZYNQ的AXI4 HP总线上,并将DMA的两个AXI4-Stream输入输入出接口引到Block Design的接口上,这两个接口在Block Design的上层模块中与自定义设计的PL端数据处理器连接,实现PS端与自定义PL端的大批量数据流交互。
(2)例化了自定义的AXI-Lite外设(图中为myip)含有4个32位寄存器,可供PS端寻址并进行读写,并将myip中的一个寄存器引出到Block Design的接口上,在上层模块中与PL端连接作为PL端的配置寄存器。此外,在PL端中也设置了中断输出intr可通过myip外设输入到PS中断。
(3)DMA的两路中断输出也连接到了PS的中断输入口,分别在DMA<搬数据从DDR到外设>、<搬数据从外设到DDR>完成时引发中断。
PS端通过配置寄存器控制着DMA的传输方向、坐标和开始,在AXI DMA的两个接口上例化了system ila用于观察波形,得到有PS到PL传输、PL到PS传输的波形图如下所示:
有意思的是,笔者发现无论怎么调试,PL到PS的传输中,即使PS端没有发起请求,DMA也会自动的发送从PL端接收数据的请求,且只接受4 Word的数据量,但这并不影响最终逻辑结果,个人猜测这4 Word存在DMA的buffer中,当PS发起请求时才会从DMA的buffer传输到DDR3中。
PL端外设的核心代码如下
module acc(
input clk,
input wire [31:0]M_AXIS_MM2S_0_tdata,
input wire [3:0]M_AXIS_MM2S_0_tkeep,
input wire M_AXIS_MM2S_0_tlast,
output wire M_AXIS_MM2S_0_tready,
input wire M_AXIS_MM2S_0_tvalid,
output wire [31:0]S_AXIS_S2MM_0_tdata,
output wire [3:0]S_AXIS_S2MM_0_tkeep,
output wire S_AXIS_S2MM_0_tlast,
input wire S_AXIS_S2MM_0_tready,
output wire S_AXIS_S2MM_0_tvalid,
input wire mm2s_prmry_reset_out_n_0,
input wire s2mm_prmry_reset_out_n_0,
input wire [31:0]reg0_0
);
wire empty, full, almost_empty, almost_full, valid, rd_en, wr_en, srst;
wire [31:0] din, dout;
wire [8:0] data_count;
// MM2S
assign S_AXIS_S2MM_0_tdata = dout;
assign S_AXIS_S2MM_0_tkeep = 4'b1111;
assign S_AXIS_S2MM_0_tvalid = valid;
assign S_AXIS_S2MM_0_tlast = almost_empty & (~empty);
// S2MM
assign M_AXIS_MM2S_0_tready = ~almost_full;
// FIFO
assign srst = ~(mm2s_prmry_reset_out_n_0 | s2mm_prmry_reset_out_n_0);
assign wr_en = M_AXIS_MM2S_0_tready & M_AXIS_MM2S_0_tvalid;
assign din = M_AXIS_MM2S_0_tdata;
assign rd_en = S_AXIS_S2MM_0_tready & S_AXIS_S2MM_0_tvalid;
fifo_generator_0 fifo0 (
.clk(clk), // input wire clk
.srst(srst), // input wire srst
.din(din), // input wire [31 : 0] din
.wr_en(wr_en), // input wire wr_en
.rd_en(rd_en), // input wire rd_en
.dout(dout), // output wire [31 : 0] dout
.full(full), // output wire full
.empty(empty), // output wire empty
.almost_empty(almost_empty), // output wire almost_empty
.almost_full(almost_full),
.valid(valid), // output wire valid
.data_count(data_count) // output wire [8 : 0] data_count
);
endmodule
3 软件设计
在Vivado SDK中设计了C语言裸机程序,可以通过配置寄存器来控制DMA的传输长度、坐标、方向,同时也编写了DMA完成传输的中断函数,反映在main函数中为RxDone和TxDone变量,分别表示<外设到DDR>和<DDR到外设>传输的完成,主体部分如下所示。
//config the reg0 in pl processor
//0:~, 1:+1, default:+0
Xil_Out32(XPAR_MYIP_0_S00_AXI_BASEADDR, mode);
// set TX data
for(Index = 0; Index < Len; Index ++) {
TxBufferPtr[Index] = Index;
}
xil_printf("TX data ready!\r\n");
// TX data to DMA
Xil_DCacheFlushRange((u32)TxBufferPtr, Len);
Status = XAxiDma_SimpleTransfer(&AxiDma,(u32) TxBufferPtr,
Len, XAXIDMA_DMA_TO_DEVICE);
if (Status != XST_SUCCESS) {
xil_printf("TX data to DMA Fail!\r\n");
return XST_FAILURE;
}
// Wait util complete
while(TxDone == 0);
xil_printf("TX data to DMA OK!\r\n");
// DMA data to RX
Status = XAxiDma_SimpleTransfer(&AxiDma,(u32) RxBufferPtr,
Len, XAXIDMA_DEVICE_TO_DMA);
if (Status != XST_SUCCESS) {
xil_printf("DMA data to RX Fail!\r\n");
return XST_FAILURE;
}
#ifndef __aarch64__
Xil_DCacheInvalidateRange((u32)RxBufferPtr, Len);
#endif
// Wait util complete
while(RxDone == 0);
xil_printf("DMA data to RX OK!\r\n");
其中在DDR中TxBuffer和RxBuffer的地址分别是0x01100000和0x01300000,在SDK中观察两地址的Memory如下:
(1)传输开始前:这片地址的数据是随机未知的
(2)TX传输后:首先通过PS端对TxBuffer数据进行赋值,再通过DMA传输到PL端
(3)TX传输后:PL端接收到数据后,在PS端的控制下通过DMA将处理的结果传输到RxBuffer,本demo对数据处理的方式是保持不变文章来源:https://www.toymoban.com/news/detail-812454.html
文章来源地址https://www.toymoban.com/news/detail-812454.html
到了这里,关于自定义ZYNQ的PL端数据处理器,通过DMA等进行交互(附GitHub源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!