AXI-Stream接口开发详细流程

http://blog.csdn.net/kkk584520/article/details/9290069原文地址



下面讲一个例子,来加深对上面介绍内容的理解。笔者使用的软件版本为ISE 14.2。

1.建立PlanAhead工程,一直到进入XPS,具体流程见官方文档CTT[1]

2.在XPS中,添加一个AXI-DMA模块,配置界面如图1所示。

图1 AXI-DMA模块配置

其余参数默认。SG模块如果选上,那么后面软件控制会相对复杂一些。这里不选,采用Simple模式,实现较为简单的传输。

3.选菜单Hardware->Createor Import Peripheral…,设计自定义IP。名称起为my_stream_ip,自动版本为1.00a。遇到Bus Interface选择AXI4-Stream类型,一直点下一步到最后结束。该类型IP的生成过程比AXI4-Lite和AXI4都要简单。

4.添加一个my_stream_ip到系统中,连接见图2。

图2  AXI Stream IP硬件连接

由XPS自动生成的my_stream_ip实现了先接收8个32bit字,然后求和,再将结果发送回去(连续发送8次)。上图连接方式说明是AXI-DMA模块发送数据给my_stream_ip,然后my_stream_ip又将结果发回AXI-DMA。同时看到AXI-DMA和PS的数据流连接是通过HP0传输,而控制流通过GP0传输。

5.上面连接在不做任何改动的情况下有问题(主要是XPS的bug),需要一项项手动修改。首先是HP0的地址区间报错,可以先点Zynq标签,然后单击HP0绿线,在弹出的配置对话框中将HP0的地址区间改为我们ZED Board 上DDR2区间0x00000000~0x1FFFFFFF,像图3一样。

图3  修正bug1

在较高版本软件ISE14.5中,这个bug已经修复,不需要改。

第二个bug就是AXI-DMA和my_stream_ip的连线问题。本来都是Stream 接口,按理说是标准接口,不应该有差异。但事实就是这样,XPS界面掩饰的问题层出不穷。我们右击my_stream_ip,选择View MPD,将内容改为:

[plain]  view plain copy print ?
  1. BEGIN my_stream_ip  
  2.   
  3. ## Peripheral Options  
  4. OPTION IPTYPE = PERIPHERAL  
  5. OPTION IMP_NETLIST = TRUE  
  6. OPTION HDL = VERILOG  
  7. ## Bus Interfaces  
  8. BUS_INTERFACE BUS=M_AXIS, BUS_STD=AXIS, BUS_TYPE=INITIATOR  
  9. BUS_INTERFACE BUS=S_AXIS, BUS_STD=AXIS, BUS_TYPE=TARGET  
  10.   
  11. ## Parameters  
  12. PARAMETER C_S_AXIS_PROTOCOL = GENERIC, DT = string, TYPE = NON_HDL, ASSIGNMENT= CONSTANT, BUS = S_AXIS  
  13. PARAMETER C_S_AXIS_TDATA_WIDTH = 32, DT = integer, TYPE = NON_HDL, ASSIGNMENT =CONSTANT, BUS = S_AXIS  
  14. PARAMETER C_M_AXIS_PROTOCOL = GENERIC, DT = string, TYPE = NON_HDL, ASSIGNMENT= CONSTANT, BUS = M_AXIS  
  15. PARAMETER C_M_AXIS_TDATA_WIDTH = 32, DT = integer, TYPE = NON_HDL, ASSIGNMENT =CONSTANT, BUS = M_AXIS  
  16. ## Peripheral ports  
  17. PORT ACLK = "", DIR=I, SIGIS=CLK, BUS=M_AXIS:S_AXIS  
  18. PORT ARESETN = ARESETN, DIR=I, INITIALVAL = VCC  
  19. PORT S_AXIS_TREADY = TREADY, DIR=O, BUS=S_AXIS  
  20. PORT S_AXIS_TDATA = TDATA, DIR=I, VEC=[31:0], BUS=S_AXIS  
  21. PORT S_AXIS_TLAST = TLAST, DIR=I, BUS=S_AXIS  
  22. PORT S_AXIS_TVALID = TVALID, DIR=I, BUS=S_AXIS  
  23. PORT M_AXIS_TVALID = TVALID, DIR=O, BUS=M_AXIS  
  24. PORT M_AXIS_TDATA = TDATA, DIR=O, VEC=[31:0], BUS=M_AXIS  
  25. PORT M_AXIS_TLAST = TLAST, DIR=O, BUS=M_AXIS  
  26. PORT M_AXIS_TREADY = TREADY, DIR=I, BUS=M_AXIS  
  27. PORT M_AXIS_TKEEP = TKEEP, DIR=O, VEC=[3:0], BUS=M_AXIS  
  28. END  


这里存在两个问题:一个是ARESETN,在连接时AXI-DMA上没有合适的引脚与之相连,默认接地。这里显式声明接VCC。另一个问题是TKEEP信号,在我的博客文章《AXI-Stream调试日记(三)》里说过了,这里加上这个引脚,才能准确地将数据发回AXI-DMA。

保存MPD文件,关闭。再次右击my_stream_ip,选择Browse HDL Sources,打开my_stream_ip.v(或my_stream_ip.vhd),添加TKEEP信号并设置TLAST信号。

[plain]  view plain copy print ?
  1. module my_stream_ip  
  2.  (  
  3.   ACLK,  
  4.   ARESETN,  
  5.   S_AXIS_TREADY,  
  6.   S_AXIS_TDATA,  
  7.   S_AXIS_TLAST,  
  8.   S_AXIS_TVALID,  
  9.   M_AXIS_TVALID,  
  10.   M_AXIS_TDATA,  
  11.   M_AXIS_TLAST,  
  12.   M_AXIS_TREADY,  
  13.   M_AXIS_TKEEP  
  14.  );  
  15.   
  16. input                                    ACLK;  
  17. input                                    ARESETN;  
  18. output                                   S_AXIS_TREADY;  
  19. input      [31 :0]                      S_AXIS_TDATA;  
  20. input                                    S_AXIS_TLAST;  
  21. input                                    S_AXIS_TVALID;  
  22. output                                   M_AXIS_TVALID;  
  23. output     [31 :0]                      M_AXIS_TDATA;  
  24. output                                   M_AXIS_TLAST;  
  25. input                                    M_AXIS_TREADY;  
  26. output    [3:0]         M_AXIS_TKEEP;  
  27.   
  28.    localparamNUMBER_OF_INPUT_WORDS  = 8;  
  29.   
  30.    localparamNUMBER_OF_OUTPUT_WORDS = 8;  
  31.   
  32.    localparam Idle  =3'b100;  
  33.    localparam Read_Inputs = 3'b010;  
  34.    localparam Write_Outputs  = 3'b001;  
  35.   
  36.    reg [2:0] state;  
  37.   
  38.    reg [31:0] sum;  
  39.   
  40.    reg [NUMBER_OF_INPUT_WORDS -1:0] nr_of_reads;  
  41.    reg [NUMBER_OF_OUTPUT_WORDS - 1:0] nr_of_writes;  
  42.   
  43.    assign S_AXIS_TREADY  =(state == Read_Inputs);  
  44.    assign M_AXIS_TVALID = (state == Write_Outputs);  
  45.   
  46.    assign M_AXIS_TDATA = sum;  
  47.    assign M_AXIS_TLAST = (nr_of_writes == 1);  
  48.  assign M_AXIS_TKEEP = 4'b1111;  
  49.    always @(posedge ACLK)  
  50.    begin  // process The_SW_accelerator  
  51.       if(!ARESETN)              // Synchronous reset (active low)  
  52.         begin  
  53.           state        <= Idle;  
  54.            nr_of_reads <= 0;  
  55.            nr_of_writes <=0;  
  56.           sum          <= 0;  
  57.         end  
  58.       else  
  59.         case (state)  
  60.           Idle:  
  61.             if (S_AXIS_TVALID== 1)  
  62.             begin  
  63.              state       <= Read_Inputs;  
  64.              nr_of_reads <= NUMBER_OF_INPUT_WORDS - 1;  
  65.              sum         <= 0;  
  66.             end  
  67.   
  68.          Read_Inputs:  
  69.             if(S_AXIS_TVALID == 1)  
  70.             begin  
  71.              sum         <= sum + S_AXIS_TDATA;  
  72.              if (nr_of_reads == 0)  
  73.                begin  
  74.                  state        <= Write_Outputs;  
  75.                  nr_of_writes <= NUMBER_OF_OUTPUT_WORDS - 1;  
  76.                end  
  77.              else  
  78.                nr_of_reads <= nr_of_reads - 1;  
  79.             end  
  80.   
  81.          Write_Outputs:  
  82.             if(M_AXIS_TREADY == 1)  
  83.             begin  
  84.              if (nr_of_writes == 0)  
  85.                 state <= Idle;  
  86.               else  
  87.                 nr_of_writes <= nr_of_writes - 1;  
  88.             end  
  89.         endcase  
  90.    end  
  91.   
  92. endmodule  


到这里修正了已知的所有bug。VHDL代码见我的博客文章http://www.eeforum.com/附件,或通过邮件联系我获取。完成上述更改后,点XPS菜单Project->Rescan User Repositories,实现用户配置更新。

6.点Port标签,引脚连接。这里重点是将所有带CLK字样的都连接到PS7_FCLK_CLK0.如图4所示。

图4 PORT标签信号线连接

7.点击Addresses标签,看看AXI-DMA是否分配了控制端口地址

图5 地址分配

注意,如果你的axi_dma_0的地址和图中不一样,那么在后面软件编写时一定要修改成你的地址。

8.点Project->DesignRule Check;没错时,点Hardware->Generate Netlist,完成后关闭XPS。

9.在PlanAhead中完成综合、实现、生成Bit等步骤[12]。其实上一步已经完成了综合,所以这一步速度就会非常快。

10 导出SDK工程。建立Helloworld工程。将Helloworld.c里面的内容改为如下代码。 

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include "platform.h"  
  4. #include "xil_cache.h"          //必须包含该头文件,实现cache操作  
  5.   
  6. #define sendram ((int *)0x10000000)          //发送缓冲区  
  7. #define recvram ((int *)0x10001000)          //接收缓冲区  
  8. #define sizeofbuffer 32  
  9.   
  10. void print(char *str);  
  11. #define WITH_SG 0  
  12. #define AXI_DMA_BASE 0x40400000  
  13.   
  14. #define MM2S_DMACR 0  
  15. #define MM2S_DMASR 1  
  16. #if WITH_SG  
  17. #define MM2S_CURDESC 2  
  18. #define MM2S_TAILDESC 4  
  19. #else  
  20. #define MM2S_SA 6  
  21. #define MM2S_LENGTH 10  
  22. #endif  
  23. #define S2MM_DMACR 12  
  24. #define S2MM_DMASR 13  
  25. #if WITH_SG  
  26. #define S2MM_CURDESC14  
  27. #define S2MM_TAILDESC16  
  28. #else  
  29. #define S2MM_DA 18  
  30. #define S2MM_LENGTH 22  
  31. #endif  
  32.   
  33. void debug_axi_dma_register(unsigned int *p)  
  34. {  
  35.  printf("MM2S_DMACR = 0x%x\n",*(p+MM2S_DMACR));  
  36.  printf("MM2S_DMASR = 0x%x\n",*(p+MM2S_DMASR));  
  37. #if WITH_SG  
  38.  printf("MM2S_CURDESC = 0x%x\n",*(p+MM2S_CURDESC));  
  39.  printf("MM2S_TAILDESC = 0x%x\n",*(p+MM2S_TAILDESC));  
  40. #else  
  41.  printf("MM2S_SA = 0x%x\n",*(p+MM2S_SA));  
  42.  printf("MM2S_LENGTH = 0x%x\n",*(p+MM2S_LENGTH));  
  43. #endif  
  44.  printf("S2MM_DMACR =0x%x\n",*(p+S2MM_DMACR));  
  45.  printf("S2MM_DMACSR =0x%x\n",*(p+S2MM_DMASR));  
  46. #if WITH_SG  
  47.  printf("S2MM_CURDESC =0x%x\n",*(p+S2MM_CURDESC));  
  48.  printf("S2MM_TAILDESC= 0x%x\n",*(p+S2MM_TAILDESC));  
  49. #else  
  50.  printf("S2MM_DA =0x%x\n",*(p+S2MM_DA));  
  51.  printf("S2MM_LENGTH =0x%x\n",*(p+S2MM_LENGTH));  
  52. #endif  
  53. }  
  54. void init_axi_dma_simple(unsigned int * p)  
  55. {  
  56.  *(p+MM2S_DMACR) = 0x04;  //reset send axi dma  
  57.  while(*(p+MM2S_DMACR)&0x04);  
  58.  *(p+S2MM_DMACR) =0x04;  //reset send axi dma  
  59.  while(*(p+S2MM_DMACR)&0x04);  
  60.  *(p+MM2S_DMACR)=1;  
  61.  while((*(p+MM2S_DMASR)&0x01));  
  62.  *(p+S2MM_DMACR)=1;  
  63.  while((*(p+S2MM_DMASR)&0x01));  
  64.  *(p+MM2S_SA) = (unsigned int )sendram;  
  65.  *(p+S2MM_DA) =(unsigned int )recvram;  
  66.  Xil_DCacheFlushRange((u32)sendram,sizeofbuffer); //将cache内容同步到DDR2  
  67.  *(p+S2MM_LENGTH) =sizeofbuffer;//sizeof(recvram);  
  68.  *(p+MM2S_LENGTH) = sizeofbuffer;//sizeof(sendram);  
  69.  while(!(*(p+MM2S_DMASR)&0x1000)); //wait for send ok  
  70.   
  71. }  
  72. void init_sendbuffer()  
  73. {  
  74.  int i;  
  75.  for(i=0;i< sizeofbuffer/4;i++)  
  76.  {  
  77.   sendram[i]=i*2;  
  78.  }  
  79. }  
  80. void show_recvbuffer()  
  81. {  
  82.  int i;  
  83.  printf("Recv contents are:\n");  
  84.  for(i=0;i< sizeofbuffer/4;i++)  
  85.  {  
  86.   printf("%d\t",recvram[i]);  
  87.  }  
  88.  printf("\r\n");  
  89. }  
  90. void show_sendbuffer()  
  91. {  
  92.  int i;  
  93.  printf("Send contents are:\n");  
  94.  for(i=0;i< sizeofbuffer/4;i++)  
  95.  {  
  96.   printf("%d\t",sendram[i]);  
  97.  }  
  98.  printf("\r\n");  
  99. }  
  100. int main()  
  101. {  
  102.  unsigned int status=0;  
  103.   
  104.  int rxlen;  
  105.     init_platform();  
  106.     init_sendbuffer();  
  107.   
  108. init_axi_dma_simple((unsignedint *)AXI_DMA_BASE);  
  109.     printf("Hello World\n\rPlease input data:");  
  110.     while(1)  
  111.     {  
  112.      scanf("%x",&status);  
  113.      printf("Got 0x%x\n",status);  
  114.      debug_axi_dma_register((unsigned int *)AXI_DMA_BASE);  
  115.      if(status==0)  
  116.      {  
  117.       break;  
  118.      }  
  119.     }  
  120.     show_sendbuffer();  
  121.   
  122. Xil_DCacheInvalidateRange((u32)recvram,sizeofbuffer);      //将DDR2内容同步到cache  
  123.   
  124.     show_recvbuffer();  
  125.     cleanup_platform();  
  126.   
  127. return 0;  
  128. }   


保存,等待生成elf。然后连接板子,下载bit文件,Run App,打开串口终端,等待输出。由图6可见结果正确。

图6  程序输出

最终实现的my_stream_ip对外接口如下图所示。其中“M_AXIS”开头的信号线表示为AXI_Stream主机信号线,而“S_AXIS”开头的信号线表示为AXI_Stream从机信号线。自动生成的代码中没有M_AXIS_TKEEP信号,根据AXI4_Stream协议,这会导致该模块作为主机时发送的数据一直处于无效状态,影响数据传输。我们在my_stream_ip中添加了该信号,并使之有效,从而能够获得正确的处理数据。

图7  my_stream_ip对外接口

其中 Xil_DCacheFlushRange()和Xil_DCacheInvalidateRange()两个函数均在"xil_cache.h"中声明,用于将cache内容同步到DDR2或相反的操作。之前由于不了解cache,导致程序一直得不到正确的结果,总是怀疑硬件问题,后来通过forums.xilinx.com看到了相关的帖子才明白这一点,在此感谢论坛上国内外的技术大牛为社区提供的支持。


  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值