软件版本:VIVADO2017.4 操作系统:WIN10 硬件平台: ARTIX-7 系列开发板 米联客(MSXBO)论坛www.osrc.cn答疑解惑专栏开通,欢迎大家给我提供!!! 12.1 概述 上一章我们对DMA进行了回环的测试,本章将继续DMA的学习,本章将结合之前介绍过的定时器中断实验来设计一个DMA的测速历程,以此来分析在MicroBlaze中DMA的性能如何。本章节的硬件电路大体与上一章一致,因此可以在上一章硬件电路的基础上修改,节省开发的时间。 12.2 硬件电路设计 因为本章节的硬件电路与上一章的很相似,因此在上一章的硬件电路上进行修改是一个比较明智的选择。 Step1:复制一份上一章的工程,先拷贝到另外一个路径,然后对其重命名操作。 Step2:打开复制的工程,双击System.xpr打开vivado工程,然后打开BD文件。 Step3:双击AXI4_Stream_data_FIFO,然后如下图所示设置: Step4:删除Data FIFO的M_AXIS连线,然后选中选中该连线,按Ctrl+T重新引出。 Step5:按照上一步的方法,将Data FIFO的m_axis_aresetn、m_axis_aclk也引出来。 Step6:点击IP添加图标,再添加一个Data FIFO,然后修改与另一个Data FIFO配置相同。 Step7:将新添加的Data FIFO的M_AXIS引脚与DMA的S_AXIS_S2MM连接。 Step8:引出axis_data_fifo_1的s_axis_aresetn、s_axis_aclk引脚。 Step9:将axis_data_fifo_1的m_axis_aresetn与DMA的axi_resetn连接,m_axis_aclk与m_axi_s2mm_aclk连接。 Step10:双击axis_data_fifo_1的S_AXIS引出的端口,如下图所示配置: Step11:双击DMA IP图标,如下图所示重新配置IP Step12:选中clk_wiz_1的clk_out1引脚,然后按Ctrl+T引出一个输出端口,并改名为clk_100M. Step13:按照上一步的方法,引出下图中的这个信号。 Step14:点击添加IP图标,添加一个GPIO。 Step15:双击GPIO 图标,如下图所示配置 Step16:单击run connction Automation,然后勾选上GPIO的选项。 Step17:添加AXI Timer IP,点击Run Connection Automation。
Step18:双击xlconcat_0,进行如下修改。并将axi_timer_0与xlconcat_0的In2连接。 Step17: 选中top.bd,右单击然后选择Generate Output Products。 Step18:选中top.bd,右单击然后选择Create HDL Wrapper,在弹出来的窗口中直接点击OK。 Step19:双击top_wrapper,用下面的程序代替: //-------------------------------------------------------------------------------- `timescale 1 ps / 1 ps
module top_wrapper (DDR3_addr, DDR3_ba, DDR3_cas_n, DDR3_ck_n, DDR3_ck_p, DDR3_cke, DDR3_cs_n, DDR3_dm, DDR3_dq, DDR3_dqs_n, DDR3_dqs_p, DDR3_odt, DDR3_ras_n, DDR3_reset_n, DDR3_we_n, clk_in1, reset, uart_rxd, uart_txd); output [14:0]DDR3_addr; output [2:0]DDR3_ba; output DDR3_cas_n; output [0:0]DDR3_ck_n; output [0:0]DDR3_ck_p; output [0:0]DDR3_cke; output [0:0]DDR3_cs_n; output [3:0]DDR3_dm; inout [63:0]DDR3_dq; inout [7:0]DDR3_dqs_n; inout [7:0]DDR3_dqs_p; output [0:0]DDR3_odt; output DDR3_ras_n; output DDR3_reset_n; output DDR3_we_n; input clk_in1; input reset; input uart_rxd; output uart_txd;
wire [14:0]DDR3_addr; wire [2:0]DDR3_ba; wire DDR3_cas_n; wire [0:0]DDR3_ck_n; wire [0:0]DDR3_ck_p; wire [0:0]DDR3_cke; wire [0:0]DDR3_cs_n; wire [7:0]DDR3_dm; wire [63:0]DDR3_dq; wire [7:0]DDR3_dqs_n; wire [7:0]DDR3_dqs_p; wire [0:0]DDR3_odt; wire DDR3_ras_n; wire DDR3_reset_n; wire DDR3_we_n; wire [31:0]M_AXIS_tdata; wire [3:0]M_AXIS_tkeep; wire M_AXIS_tlast; wire M_AXIS_tready; wire M_AXIS_tvalid; reg [31:0]S_AXIS_tdata; wire [3:0]S_AXIS_tkeep; reg S_AXIS_tlast; wire S_AXIS_tready; wire [3:0]S_AXIS_tstrb; reg S_AXIS_tvalid; wire clk_100M; wire clk_in1; wire [0:0]gpio_rtl_tri_i_0; wire [0:0]gpio_rtl_tri_io_0; wire [0:0]gpio_rtl_tri_o_0; wire [0:0]gpio_rtl_tri_t_0; wire m_axis_aclk; wire m_axis_aresetn; wire [0:0]peripheral_aresetn; wire reset; wire s_axis_aclk; wire s_axis_aresetn; wire uart_rxd; wire uart_txd; reg[1:0] state; assign M_AXIS_tready = 1'b1; assign s_axis_aclk = clk_100M; assign m_axis_aclk = clk_100M; assign m_axis_aresetn = peripheral_aresetn; assign s_axis_aresetn = peripheral_aresetn; assign S_AXIS_tkeep = 4'b1111; always@(posedge clk_100M) begin if(!peripheral_aresetn) begin S_AXIS_tvalid <= 1'b0; S_AXIS_tdata <= 32'd0; S_AXIS_tlast <= 1'b0; state <=0; end else begin case(state) 0: begin if(gpio_rtl_tri_o_0&& S_AXIS_tready) begin S_AXIS_tvalid <= 1'b1; state <= 1; end else begin S_AXIS_tvalid <= 1'b0; state <= 0; end end 1:begin if(S_AXIS_tready) begin S_AXIS_tdata <= S_AXIS_tdata + 1'b1; if(S_AXIS_tdata == 32'd1022) begin S_AXIS_tlast <= 1'b1; state <= 2; end else begin S_AXIS_tlast <= 1'b0; state <= 1; end end else begin S_AXIS_tdata <= S_AXIS_tdata; state <= 1; end end 2:begin if(!S_AXIS_tready) begin S_AXIS_tvalid <= 1'b1; S_AXIS_tlast <= 1'b1; S_AXIS_tdata <= S_AXIS_tdata; state <= 2; end else begin S_AXIS_tvalid <= 1'b0; S_AXIS_tlast <= 1'b0; S_AXIS_tdata <= 16'd0; state <= 0; end end default: state <=0; endcase end end
top top_i (.DDR3_addr(DDR3_addr), .DDR3_ba(DDR3_ba), .DDR3_cas_n(DDR3_cas_n), .DDR3_ck_n(DDR3_ck_n), .DDR3_ck_p(DDR3_ck_p), .DDR3_cke(DDR3_cke), .DDR3_cs_n(DDR3_cs_n), .DDR3_dm(DDR3_dm), .DDR3_dq(DDR3_dq), .DDR3_dqs_n(DDR3_dqs_n), .DDR3_dqs_p(DDR3_dqs_p), .DDR3_odt(DDR3_odt), .DDR3_ras_n(DDR3_ras_n), .DDR3_reset_n(DDR3_reset_n), .DDR3_we_n(DDR3_we_n), .M_AXIS_tdata(M_AXIS_tdata), .M_AXIS_tkeep(M_AXIS_tkeep), .M_AXIS_tlast(M_AXIS_tlast), .M_AXIS_tready(M_AXIS_tready), .M_AXIS_tvalid(M_AXIS_tvalid), .S_AXIS_tdata(S_AXIS_tdata), .S_AXIS_tkeep(S_AXIS_tkeep), .S_AXIS_tlast(S_AXIS_tlast), .S_AXIS_tready(S_AXIS_tready), .S_AXIS_tstrb(S_AXIS_tstrb), .S_AXIS_tvalid(S_AXIS_tvalid), .clk_100M(clk_100M), .clk_in1(clk_in1), .gpio_rtl_tri_i(gpio_rtl_tri_i_0), .gpio_rtl_tri_o(gpio_rtl_tri_o_0), .gpio_rtl_tri_t(gpio_rtl_tri_t_0), .m_axis_aclk(m_axis_aclk), .m_axis_aresetn(m_axis_aresetn), .peripheral_aresetn(peripheral_aresetn), .reset(reset), .s_axis_aclk(s_axis_aclk), .s_axis_aresetn(s_axis_aresetn), .uart_rxd(uart_rxd), .uart_txd(uart_txd)); endmodule |
12.3软件设计Step1:新建一个名为DMA_Test的SDK工程。 Step2: 在我们提供的源代码中,找到sdk_src文件夹,然后复制里面的文件到src目录下。 Step3:选中SDK工程文件,右单击选择Debug as-Debug configuration。
Step4:在弹出来的新窗口中,双击下图圈出部分,然后勾选箭头所示参数 Step5:单击APPly,然后单击Debug(进行这一步之前,先给开发板上电)。 Step6:在下图所示区域找到SDK Terminal,然后单击加号图标 Step7:单击加号图标之后,再新弹出来的窗口中设置好对应的端口号和波特率,然后单击OK。
12.4 程序分析 本章的程序是在上一章的基础上添加了定时器中断部分以完成单位时间内DMA的传输速度的测量。DMA部分和定时器中断部分在之前的章节中都已经做过了详细的介绍,在此就仅分析一下DMA测速部分是如何实现的。这一部分的实现是在axi_dma_test子函数完成的,接下来我们来分析一下这个函数: int axi_dma_test() { int Status; TxDone = 0; RxDone = 0; Error = 0;
xil_printf("123"); //print_message( "----DMA Test----",0);//oled print
xil_printf("PKT_LEN=%d\r\n",MAX_PKT_LEN);
//sprintf(oled_str,"PKT_LEN=%d",MAX_PKT_LEN); //print_message(oled_str,1);//oled print
for(Index = 0; Index < MAX_PKT_LEN; Index ++) { TxBufferPtr[Index] = Value;
Value = (Value + 1) & 0xFF; } /* Flush the SrcBuffer before the DMA transfer, in case the Data Cache * is enabled */ Xil_DCacheFlushRange((u32)TxBufferPtr, MAX_PKT_LEN); Timer_start(&Timer,0); while(1) //for(i = 0; i < Tries; i ++) { //RX DMA Transfer
if(RX_ready) { RX_ready=0; Status = XAxiDma_SimpleTransfer(&AxiDma,(u32)RxBufferPtr, (u32)(MAX_PKT_LEN), XAXIDMA_DEVICE_TO_DMA);
if (Status != XST_SUCCESS) {return XST_FAILURE;} }
//TX DMA Transfer if(TX_ready) { TX_ready=0; Status = XAxiDma_SimpleTransfer(&AxiDma,(u32) TxBufferPtr, (u32)(MAX_PKT_LEN), XAXIDMA_DMA_TO_DEVICE);
if (Status != XST_SUCCESS) {return XST_FAILURE;} }
if(RxDone) { RxDone=0; RX_ready=1; RX_success++; }
if(TxDone) { TxDone=0; TX_ready=1; TX_success++; } if(usec==2) {
usec=0; xil_printf("RX_cnt=%d\r\n",RX_success); //xil_printf("%s\r\n",oled_str); //print_message(oled_str,0);
speed_rx = MAX_PKT_LEN*RX_success/1024/1024;
xil_printf("RX_sp=%dMB/S\r\n",speed_rx); //xil_printf("%s\r\n",oled_str); //print_message(oled_str,1);
xil_printf("TX_cnt=%d\r\n",TX_success); // xil_printf("%s\r\n",oled_str); //print_message(oled_str,2);
speed_tx = (MAX_PKT_LEN)*TX_success/1024/1024;
xil_printf("TX_sp=%dMB/S\r\n",speed_tx); // xil_printf("%s\r\n",oled_str); //print_message(oled_str,3);
RX_success=0; TX_success=0;
}
if (Error) { xil_printf("Failed test transmit%s done, " "receive%s done\r\n", TxDone? "":" not", RxDone? "":" not"); goto Done; }
}
/* Disable TX and RX Ring interrupts and return success */ DMA_DisableIntrSystem(&Intc, TX_INTR_ID, RX_INTR_ID); Done: xil_printf("--- Exiting Test --- \r\n"); //print_message("--Exiting Test---",3); return XST_SUCCESS;
} |
首先,可以看到,程序往发送的缓存TxBufferPtr中打入了一些数据,然后开启了定时器中断: 接着判断是否RX_Ready和TX_Ready是否为1(程序开头初始化为1了),启动DMA的传输: 接着判断接收与发送中断回调函数中的全局变量RxDone和TxDone,每检测到一次中断的到来,既进行加1操作: 之后判断定时时间是否到了1S(定时器初值为500ms),如果到了就计算传输的速率: 由此,得到了单位时间内DMA的传输速率。 说明:如下图,axi_data_fifo_0的AXI接口为预留接口,目的是为了留出一个读DMA数据,如果读者感兴趣可以使用ila核读取DMA写出的数据,看看是否与DMA发送数据一致。 12.5 本章小结 本章应用上一章回环测试和定时器中断来设计了一个DMA测速的历程,从实验结果来看并不是很理想,其传输速度低于10MB/S(时钟100M下),对比笔者曾经设计的ZYNQ下实测DMA速度大概150MB/S之下,MicroBlaze的DMA速度是很慢的,这是因为在本章中设置的一帧传输的大小为2MB,笔者试过增加数据的容量,结果速度也相应的变快了,大家感兴趣的可以将数据量变大一点,再进行测试。由此可以推断MicroBlaze的DMA传输速度的大小与单次传输的数据量有关,数据量越大,传输速度就越快! |