ebullience的个人空间 https://blog.eetop.cn/52794 [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

Xilinx AXI-Stream IP调试日记

已有 1116 次阅读| 2013-5-24 01:42

Xilinx AXI-Stream IP调试日记
来源 http://www.eefocus.com/zhaoyongke/blog/
目录

【参赛手记】AXI-Stream接口开发详细流程... 1

【参赛手记】AXI-Stream IP调试日记(一)... 12

【参赛手记】AXI-Stream IP调试日记(二)... 15

【参赛手记】AXI-Stream IP调试日记(三)... 20

【参赛手记】AXI总线学习... 25

【参赛手记】System Generator 与XPS连接的方法... 25

 

【参赛手记】AXI-Stream接口开发详细流程  

2013-05-18 23:32

本文是AXI-Stream IP调试日记的终结篇。

看到这里,可能大家都还对Stream没有一个直观的认识。其实Stream并不陌生,在我们学c++编程时,一定会包含,这样就可以完成控制终端对程序的输入输出了。如果还是不够直观,想象一下水流,是连续不断的,向某一方向,以固定的速度输送的接口。以我们看视频为例,视频文件本来是保存在硬盘里的,怎么播放呢,不能一下子把整个文件都显示到屏幕上,而是以一定速度,连续不断地输出到屏幕上(每秒若干帧),这个过程就是流Stream接口完成的。

Xilinx提供的流式IP核有很多用途,可以实现音频流、视频流、数据流到内存或者相反方向的传输。有人问了,内存是PS控制的,怎么才能把PSDDR2的内容以Stream形式发出去呢(例如以固定速度送往DA,完成信号发生器的设计)?答案就是利用AXI总线做转换。ZYNQPS部分是arm Cortex A9系列,支持AXI4AXI-Lite总线。PL部分也有相应AXI总线接口,这样就能完成PSPL的互联。仅仅这样还不够,需要PL部分实现流式转换,即AXI-Stream接口实现。Xilinx提供的从AXIAXI-Stream转换的IP核有:AXI-DMAAXI-DatamoverAXI-fifo-MM2S以及AXI-vDMA等。这些IP核可以在XPS中看到。

这里要和大家说明白一点,就是AXI总线和接口的区别。总线是一种标准化接口,由数据线、地址线、控制线等构成,具有一定的强制性。接口是其物理实现,即在硬件上的分配。在ZYNQ中,支持AXI-LiteAXI4AXI-Stream三种总线,但PSPL之间的接口却只支持前两种,AXI-Stream只能在PL中实现,不能直接和PS相连,必须通过AXI-LiteAXI4转接。PSPL之间的物理接口有9个,包括4AXI-GP接口和4AXI-HP接口、1AXI-ACP接口。

AXI-DMA:实现从PS内存到PL高速传输高速通道AXI-HP<---->AXI-Stream的转换

AXI-FIFO-MM2S:实现从PS内存到PL通用传输通道AXI-GP<----->AXI-Stream的转换

AXI-Datamover:实现从PS内存到PL高速传输高速通道AXI-HP<---->AXI-Stream的转换,只不过这次是完全由PL控制的,PS是完全被动的。

AXI-VDMA:实现从PS内存到PL高速传输高速通道AXI-HP<---->AXI-Stream的转换,只不过是专门针对视频、图像等二维数据的。

除了上面的还有一个AXI-CDMA IP核,这个是由PL完成的将数据从内存的一个位置搬移到另一个位置,无需cpu来插手。这个和我们这里用的Stream没有关系,所以不表。

上面的IP是完成总线协议转换,如果需要做某些处理(如变换、迭代、训练……),则需要生成一个自定义Stream类型IP,与上面的Stream接口连接起来,实现数据输入输出。用户的功能在自定义Stream类型IP中实现。

 

下面讲一个例子,来加深对上面介绍内容的理解。

软件:ISE 14.2

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

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

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

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

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

my_stream_ip实现了先接受8个字的数据,求和,然后将和发送回去(发送8次)。上图连接方式说明是AXI-DMA发送给my_stream_ip,然后my_stream_ip又发回AXI-DMA。同时看到AXI-DMAPS连接是通过HP0传输数据,由GP0控制其传输的进行。

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


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

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

##############################################################################
## Filename:          E:\work\FPGA\ZynqProjects\axi_dma_final\axi_dma_final.srcs\sources_1\edk\system\pcores/my_stream_ip_v1_00_a/data/my_stream_ip_v2_1_0.mpd
## Description:       Microprocessor Peripheral Description
## Date:              Sat May 11 19:51:06 2013 (by Create and Import Peripheral Wizard)
##############################################################################

BEGIN my_stream_ip

## Peripheral Options
OPTION IPTYPE = PERIPHERAL
OPTION IMP_NETLIST = TRUE
OPTION hdl = verilog
## Bus Interfaces
BUS_INTERFACE BUS=M_AXIS, BUS_STD=AXIS, BUS_TYPE=INITIATOR
BUS_INTERFACE BUS=S_AXIS, BUS_STD=AXIS, BUS_TYPE=TARGET

## Parameters
PARAMETER C_S_AXIS_PROTOCOL = GENERIC, DT = string, TYPE = NON_HDL, ASSIGNMENT = CONSTANT, BUS = S_AXIS
PARAMETER C_S_AXIS_TDATA_WIDTH = 32, DT = integer, TYPE = NON_HDL, ASSIGNMENT = CONSTANT, BUS = S_AXIS
PARAMETER C_M_AXIS_PROTOCOL = GENERIC, DT = string, TYPE = NON_HDL, ASSIGNMENT = CONSTANT, BUS = M_AXIS
PARAMETER C_M_AXIS_TDATA_WIDTH = 32, DT = integer, TYPE = NON_HDL, ASSIGNMENT = CONSTANT, BUS = M_AXIS
## Peripheral ports
PORT ACLK = "", DIR=I, SIGIS=CLK, BUS=M_AXIS:S_AXIS
PORT ARESETN = ARESETN, DIR=I, INITIALVAL = VCC
PORT S_AXIS_TREADY = TREADY, DIR=O, BUS=S_AXIS
PORT S_AXIS_TDATA = TDATA, DIR=I, VEC=[31:0], BUS=S_AXIS
PORT S_AXIS_TLAST = TLAST, DIR=I, BUS=S_AXIS
PORT S_AXIS_TVALID = TVALID, DIR=I, BUS=S_AXIS
PORT M_AXIS_TVALID = TVALID, DIR=O, BUS=M_AXIS
PORT M_AXIS_TDATA = TDATA, DIR=O, VEC=[31:0], BUS=M_AXIS
PORT M_AXIS_TLAST = TLAST, DIR=O, BUS=M_AXIS
PORT M_AXIS_TREADY = TREADY, DIR=I, BUS=M_AXIS
PORT M_AXIS_TKEEP = TKEEP, DIR=O, VEC=[3:0], BUS=M_AXIS
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),内容改为:

module my_stream_ip
 (
  // ADD USER PORTS BELOW THIS LINE
  // -- USER ports added here
  // ADD USER PORTS ABOVE THIS LINE

  // DO NOT EDIT BELOW THIS LINE ////////////////////
  // Bus protocol ports, do not add or delete.
  ACLK,
  ARESETN,
  S_AXIS_TREADY,
  S_AXIS_TDATA,
  S_AXIS_TLAST,
  S_AXIS_TVALID,
  M_AXIS_TVALID,
  M_AXIS_TDATA,
  M_AXIS_TLAST,
  M_AXIS_TREADY
,
  M_AXIS_TKEEP

  // DO NOT EDIT ABOVE THIS LINE ////////////////////
 );

// ADD USER PORTS BELOW THIS LINE
// -- USER ports added here
// ADD USER PORTS ABOVE THIS LINE

input                                     ACLK;
input                                     ARESETN;
output                                    S_AXIS_TREADY;
input      [31 : 0]                       S_AXIS_TDATA;
input                                     S_AXIS_TLAST;
input                                     S_AXIS_TVALID;
output                                    M_AXIS_TVALID;
output     [31 : 0]                       M_AXIS_TDATA;
output                                    M_AXIS_TLAST;
input                                     M_AXIS_TREADY;
output     [3:0]         M_AXIS_TKEEP;
// ADD USER PARAMETERS BELOW THIS LINE
// --USER parameters added here
// ADD USER PARAMETERS ABOVE THIS LINE

 

 

//----------------------------------------
// Implementation Section
//----------------------------------------
// In this section, we povide an example implementation of MODULE my_stream_ip
// that does the following:
//
// 1. Read all inputs
// 2. Add each input to the contents of register 'sum' which
//    acts as an accumulator
// 3. After all the inputs have been read, write out the
//    content of 'sum' into the output stream NUMBER_OF_OUTPUT_WORDS times
//
// You will need to modify this example for
// MODULE my_stream_ip to implement your coprocessor

 

   // Total number of input data.
   localparam NUMBER_OF_INPUT_WORDS  = 8;

   // Total number of output data
   localparam NUMBER_OF_OUTPUT_WORDS = 8;

   // Define the states of state machine
   localparam Idle  = 3'b100;
   localparam Read_Inputs = 3'b010;
   localparam Write_Outputs  = 3'b001;

   reg [2:0] state;

   // Accumulator to hold sum of inputs read at any point in time
   reg [31:0] sum;

   // Counters to store the number inputs read & outputs written
   reg [NUMBER_OF_INPUT_WORDS - 1:0] nr_of_reads;
   reg [NUMBER_OF_OUTPUT_WORDS - 1:0] nr_of_writes;

   // CAUTION:
   // The sequence in which data are read in should be
   // consistent with the sequence they are written in the
   // driver's my_stream_ip.c file

   assign S_AXIS_TREADY  = (state == Read_Inputs);
   assign M_AXIS_TVALID = (state == Write_Outputs);

   assign M_AXIS_TDATA = sum;
   assign M_AXIS_TLAST = 
(nr_of_writes == 1);
 
assign M_AXIS_TKEEP = 4'b1111;
   always @(posedge ACLK)
   begin  // process The_SW_accelerator
      if (!ARESETN)               // Synchronous reset (active low)
        begin
           // CAUTION: make sure your reset polarity is consistent with the
           // system reset polarity
           state        <= Idle;
           nr_of_reads  <= 0;
           nr_of_writes <= 0;
           sum          <= 0;
        end
      else
        case (state)
          Idle:
            if (S_AXIS_TVALID == 1)
            begin
              state       <= Read_Inputs;
              nr_of_reads <= NUMBER_OF_INPUT_WORDS - 1;
              sum         <= 0;
            end

          Read_Inputs:
            if (S_AXIS_TVALID == 1)
            begin
              // Coprocessor function (Adding) happens here
              sum         <= sum + S_AXIS_TDATA;
              if (nr_of_reads == 0)
                begin
                  state        <= Write_Outputs;
                  nr_of_writes <= NUMBER_OF_OUTPUT_WORDS - 1;
                end
              else
                nr_of_reads <= nr_of_reads - 1;
            end

          Write_Outputs:
            if (M_AXIS_TREADY == 1)
            begin
              if (nr_of_writes == 0)
                 state <= Idle;
               else
                 nr_of_writes <= nr_of_writes - 1;
            end
        endcase
   end

endmodule
 

 

这里修正了bug

VHDL代码见后面附件1.

 

完成上述更改后,点XPS菜单Project->Rescan User Repositories,实现用户配置更新。

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

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

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

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

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

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

 

#include
#include
#include "platform.h"
#include "xil_cache.h"

 

#define sendram ((int *)0x10000000)
#define recvram ((int *)0x10001000)
#define sizeofbuffer 32

void print(char *str);
#define WITH_SG 0
#define AXI_DMA_BASE 0x40400000

#define MM2S_DMACR 0
#define MM2S_DMASR 1
#if WITH_SG
#define MM2S_CURDESC 2
#define MM2S_TAILDESC 4
#else
#define MM2S_SA 6
#define MM2S_LENGTH 10
#endif
#define S2MM_DMACR 12
#define S2MM_DMASR 13
#if WITH_SG
#define S2MM_CURDESC 14
#define S2MM_TAILDESC 16
#else
#define S2MM_DA 18
#define S2MM_LENGTH 22
#endif

void debug_axi_dma_register(unsigned int * p)
{
 printf("MM2S_DMACR = 0x%x\n",*(p+MM2S_DMACR));
 printf("MM2S_DMASR = 0x%x\n",*(p+MM2S_DMASR));
#if WITH_SG
 printf("MM2S_CURDESC = 0x%x\n",*(p+MM2S_CURDESC));
 printf("MM2S_TAILDESC = 0x%x\n",*(p+MM2S_TAILDESC));
#else
 printf("MM2S_SA = 0x%x\n",*(p+MM2S_SA));
 printf("MM2S_LENGTH = 0x%x\n",*(p+MM2S_LENGTH));
#endif
 printf("S2MM_DMACR = 0x%x\n",*(p+S2MM_DMACR));
 printf("S2MM_DMACSR = 0x%x\n",*(p+S2MM_DMASR));
#if WITH_SG
 printf("S2MM_CURDESC = 0x%x\n",*(p+S2MM_CURDESC));
 printf("S2MM_TAILDESC = 0x%x\n",*(p+S2MM_TAILDESC));
#else
 printf("S2MM_DA = 0x%x\n",*(p+S2MM_DA));
 printf("S2MM_LENGTH = 0x%x\n",*(p+S2MM_LENGTH));
#endif
}
void init_axi_dma_simple(unsigned int * p)
{
 *(p+MM2S_DMACR) = 0x04;  //reset send axi dma
 while(*(p+MM2S_DMACR)&0x04);
 *(p+S2MM_DMACR) = 0x04;  //reset send axi dma
 while(*(p+S2MM_DMACR)&0x04);
 *(p+MM2S_DMACR)=1;
 while((*(p+MM2S_DMASR)&0x01));
 *(p+S2MM_DMACR)=1;
 while((*(p+S2MM_DMASR)&0x01));
 *(p+MM2S_SA) = (unsigned int )sendram;
 *(p+S2MM_DA) = (unsigned int )recvram;
 Xil_DCacheFlushRange((u32)sendram,sizeofbuffer);
 *(p+S2MM_LENGTH) = sizeofbuffer;//sizeof(recvram);
 *(p+MM2S_LENGTH) = sizeofbuffer;//sizeof(sendram);
 while(!(*(p+MM2S_DMASR)&0x1000)); //wait for send ok

}
void init_sendbuffer()
{
 int i;
 for(i=0;i<100;i++)
 {
  sendram[i]=i*2;//50*sinf(2*3.14*i/10);
 }
}
void show_recvbuffer()
{
 int i;
 printf("Recv contents are:\n");
 for(i=0;i<100;i++)
 {
  printf("%d\t",recvram[i]);
 }
 printf("\r\n");
}
void show_sendbuffer()
{
 int i;
 printf("Send contents are:\n");
 for(i=0;i<100;i++)
 {
  printf("%d\t",sendram[i]);
 }
 printf("\r\n");
}
int main()
{
 unsigned int status=0;

 int rxlen;
    init_platform();
    init_sendbuffer();

    init_axi_dma_simple((unsigned int *)AXI_DMA_BASE);
    printf("Hello World\n\rPlease input data:");
    while(1)
    {
     scanf("%x",&status);
     printf("Got 0x%x\n",status);
     debug_axi_dma_register((unsigned int *)AXI_DMA_BASE);
     if(status==0)
     {
      break;
     }
    }
    show_sendbuffer();

    show_recvbuffer();
    cleanup_platform();

    return 0;
}
 

保存,等待生成elf。然后连接板子,下载bit文件,Run App,打开串口终端,等待输出。

发送的内容为连续的偶数0~14,接收到数据为56,只是个数为7个而不是8个。这个是因为我们的my_stream_ipTLAST信号提前到达造成的(是故意这么做的,没问题)。

好了,到这里,Stream接口例子就完成了。希望本文对正在调试stream接口的童鞋们有帮助。


点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 1

    粉丝
  • 0

    好友
  • 0

    获赞
  • 1

    评论
  • 201

    访问数
关闭

站长推荐 上一条 /2 下一条


小黑屋| 手机版| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-12-23 04:10 , Processed in 0.026335 second(s), 13 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部