在线咨询
eetop公众号 创芯大讲堂 创芯人才网
切换到宽版

EETOP 创芯网论坛 (原名:电子顶级开发网)

手机号码,快捷登录

手机号码,快捷登录

找回密码

  登录   注册  

快捷导航
搜帖子
查看: 20206|回复: 5

[转贴] 【转载】Xilinx Zedboard ZYNQ AXI-Stream IP调试日记

[复制链接]
发表于 2013-5-24 01:12:31 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?注册

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


【参赛手记】AXI-Stream接口开发详细流程
2013-05-18 23:32

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

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

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

这里要和大家说明白一点,就是AXI总线和接口的区别。总线是一种标准化接口,由数据线、地址线、控制线等构成,具有一定的强制性。接口是其物理实现,即在硬件上的分配。在ZYNQ中,支持AXI-Lite,AXI4和AXI-Stream三种总线,但PS与PL之间的接口却只支持前两种,AXI-Stream只能在PL中实现,不能直接和PS相连,必须通过AXI-Lite或AXI4转接。PS与PL之间的物理接口有9个,包括4个AXI-GP接口和4个AXI-HP接口、1个AXI-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模块,配置界面如下图所示

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image001.jpg

其余参数默认。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到系统中,连接图见下。

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image002.jpg

my_stream_ip实现了先接受8个字的数据,求和,然后将和发送回去(发送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,像下图这样:

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image003.jpg
在高版本ISE14.5中,这个bug已经修复,不需要改。

之后就是AXI-DMA和my_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
PORTARESETN = 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
PORTM_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 THISLINE ////////////////////
  // 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 streamNUMBER_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 anypoint 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);
assignM_AXIS_TKEEP = 4'b1111;
   always @(posedge ACLK)
   begin  // process The_SW_accelerator
      if(!ARESETN)              // Synchronous reset (active low)
        begin
           // CAUTION: makesure your reset polarity is consistent with the
           // system resetpolarity
          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->RescanUser Repositories,实现用户配置更新。

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

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image004.jpg

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

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image005.jpg

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

8.点Project->Design Rule Check;没错时,点Hardware->GenerateNetlist,完成后关闭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*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);
}
printf("\r\n");
}
void show_sendbuffer()
{
int i;
printf("Send contents are:\n");
for(i=0;i<100;i++)
{
  printf("%d\t",sendram);
}
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,打开串口终端,等待输出。

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image006.jpg

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

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

前面的文章对XPS痛批了一顿,现在回过头来,发现其实如果没有前面的那些尝试,也能解决问题,只不过对Stream接口的认识没有特别深入而已。Chipscope调试是和AXi总线协议的一次亲密接触,让我知道了问题所在,从而在后面的问题解决中提供更好的方式。否定之否定原则再一次发挥作用了。



【参赛手记】AXI-StreamIP调试日记(一)
2013-05-10 23:27

前言:项目中用到Stream类型总线,便于处理连续数据流。本来以为AXIStream总线很简单,应该不会有问题,但恍惚一月过去了,中间遇到许多坎坷,绝大部分都是软件问题。

环境:Windows XP 32bit;ISE14.2;超级终端;

使用IP:AXI-Stream-FIFO,自定义Stream类型IP

BSP:裸机

正文:

首先按照常规开发流程做了个最简单的实验,即
(1)建立PlanAhead工程
(2)添加Embeded IP
(3)添加AXI-Stream-FIFO IP
(4)生成自定义IP,选择类型为Stream,收发寄存器都是8个
(5)添加自定义StreamIP,将对应总线连接上,Stream对Stream接口,Lite对GP接口
(6)关闭XPS,综合,实现,出bit
(7)导出到SDK
(8)根据system.mcs里面的例子,写一简单的测试程序
(9)下载bit,下载程序,运行
(10)观察输出。

测试代码很简单,如下所示。

#include

#include "platform.h"
#include "xllfifo.h"   //
包含AXI-FIFO-Stream控制接口头文件
#define r1 t1           //收发都是一个模块完成的,所以。。。
XLlFifot1;                //AXI-FIFO Stream控制模块
int sendram[8] = {1,2,3,4,5,6,7,8};   //发送缓冲区
int recvram[8] = {0,0,0,0,0,0,0,0};    //接收缓冲区
void print(char *str);

#define AXI_FIFO_BASE 0x74200000    //AXI-FIFO模块内存映射地址

//下面都是寄存器偏移量(按字计,不是字节,因为这里使用unsigned int指针)
#define ISR 0
#define IER 1
#define TDFR 2
#define TDFV 3
#define TDFD 4
#define TLF 5
#define RDFR 6
#define RDFO 7
#define RDFD 8
#define RLF 9
#define LLR 10

//用于调试,打印关键寄存器的值
void debug_register(unsigned int * p)
{
printf("ISR = 0x%x\n",*(p+ISR));
if(*(p+ISR))
{
  unsigned int t = *(p+ISR);
  *(p+ISR)=t&0xffffffff;
}
printf("ISR = 0x%x\n",*(p+ISR));
printf("IER = 0x%x\n",*(p+IER));
printf("TDFR = 0x%x\n",*(p+TDFR));
printf("TDFV = 0x%x\n",*(p+TDFV));
printf("TDFD = 0x%x\n",*(p+TDFD));
printf("TLF = 0x%x\n",*(p+TLF));
printf("RDFR = 0x%x\n",*(p+RDFR));
printf("RDFO = 0x%x\n",*(p+RDFO));
// printf("RDFD = 0x%x\n",*(p+RDFD));
// printf("RLF = 0x%x\n",*(p+RLF));   //千万别轻易读这个,会复位的!
printf("LLR = 0x%x\n",*(p+LLR));
}
int main()
{

int status=0;

int rxlen;   //接收字节数
    init_platform();

    printf("Hello World\n\r");

//    debug_register((unsigned int*)AXI_FIFO_BASE);
    XLlFifo_Initialize(&t1,AXI_FIFO_BASE);//
初始化AXI-FIFO模块
//   XLlFifo_Initialize(&r1,0x74200000);//由于收发一体,故只需初始化一次
    XLlFifo_Write(&t1,sendram,8*4);//写发送缓冲区的内容到发送FIFO
    XLlFifo_TxSetLen(&r1,8*4);//启动发送过程

    print("Transmit begin!\n\r");
//    debug_register((unsigned int *)AXI_FIFO_BASE);
    if(XLlFifo_RxOccupancy(&r1))   //
如果接收FIFO中有内容
    {
     rxlen=XLlFifo_RxGetLen(&r1);//先获取其长度
     printf("Rcv Length:%d\n",rxlen);
     XLlFifo_Read(&r1, recvram,rxlen);//读取接收内容
     int sum=0,i;
     for(i = 0;i<8;i++)
     {
      if(recvram!=sendram)//如果接收不等于发送
      {
       printf("Error in index%d\n",i);//那么就报错,并报告接收内容
       sum++;//错误计数
      }
     }
     if(sum==0)
     {
      printf("Success!\n");//无错,则成功
     }
    }
    print("Transmit done!\n\r");

    cleanup_platform();

    return 0;
}

的确很简单的代码,但是输出却表示收到任何东西,直接跳出了if(XLlFifo_RxOccupancy(&r1))  {},然后结束。

不理解为什么,于是又做了如下尝试:XPS中去掉自定义IP,而是直接将AXI-FIFO的Stream端回环,发送连到接收,这样再关闭XPS,综合,实现,出bit,导出SDK,下载bit,下载elf,运行,居然运行成功!输出"Success!".


【参赛手记】AXI-StreamIP调试日记(二)
2013-05-11 00:08

前言:项目中用到Stream类型总线,便于处理连续数据流。本来以为AXIStream总线很简单,应该不会有问题,但恍惚一月过去了,中间遇到许多坎坷,绝大部分都是软件问题(个人很鄙视用GUI操作,但还没有找到一种完全不用GUI的开发方法。如果有,吾愿毅然卸掉XPS!)

环境:Windows XP 32bit;ISE14.2;超级终端;

使用IP:AXI-Stream-FIFO,自定义Stream类型IP

BSP:裸机

正文:上回书说到,一旦去掉自定义Stream IP,就能正常使用AXI-FIFO-Stream模块了。

停止前进,扎营御敌。

通过Xilinx Forum搜索相关问题,看到有人提到是Stream 接口不一致造成的。回去找到XXX\ISE_DS\EDK\hw\XilinxProcessorIPLib\pcores\axi_fifo_mm_s_v2_01_a\data\
axi_fifo_mm_s_v2_1_0.mpd 文件,其中有接口内容如下:

PORT AXI_STR_TXC_ACLK = ACLK, DIR = I, BUS = AXI_STR_TXC, SIGIS= CLK, INITIALVAL = VCC
PORT mm2s_cntrl_reset_out_n = ARESETN, DIR = O, BUS = AXI_STR_TXC, SIGIS = RST
PORT AXI_STR_TXC_TVALID = TVALID, DIR = O, BUS = AXI_STR_TXC, INITIALVAL = GND
PORT AXI_STR_TXC_TREADY = TREADY, DIR = I, BUS = AXI_STR_TXC
PORT AXI_STR_TXC_TLAST = TLAST, DIR = O, BUS = AXI_STR_TXC, INITIALVAL = GND
PORTAXI_STR_TXC_TKEEP
= TKEEP, DIR = O, BUS = AXI_STR_TXC,INITIALVAL = VCC, VEC = [((C_S_AXI_DATA_WIDTH/8)-1):0], ENDIAN = LITTLE
PORT AXI_STR_TXC_TDATA = TDATA, DIR = O, BUS = AXI_STR_TXC, INITIALVAL = GND,VEC = [(C_S_AXI_DATA_WIDTH-1):0], ENDIAN = LITTLE
PORT AXI_STR_TXD_ACLK = ACLK, DIR = I, BUS = AXI_STR_TXD, SIGIS = CLK,INITIALVAL = VCC
PORT mm2s_prmry_reset_out_n = ARESETN, DIR = O, BUS = AXI_STR_TXD, SIGIS = RST
PORT AXI_STR_TXD_TVALID = TVALID, DIR = O, BUS = AXI_STR_TXD, INITIALVAL = GND
PORT AXI_STR_TXD_TREADY = TREADY, DIR = I, BUS = AXI_STR_TXD
PORT AXI_STR_TXD_TLAST = TLAST, DIR = O, BUS = AXI_STR_TXD, INITIALVAL = GND
PORTAXI_STR_TXD_TKEEP
= TKEEP, DIR = O, BUS =AXI_STR_TXD, INITIALVAL = VCC, VEC = [((C_S_AXI_DATA_WIDTH/8)-1):0], ENDIAN =LITTLE
PORTAXI_STR_TXD_TDEST
= TDEST, DIR = O, BUS =AXI_STR_TXD, INITIALVAL = VCC, VEC = [3:0], ENDIAN = LITTLE
PORT AXI_STR_TXD_TDATA = TDATA, DIR = O, BUS = AXI_STR_TXD, INITIALVAL = GND,VEC = [(C_S_AXI_DATA_WIDTH-1):0], ENDIAN = LITTLE
PORT AXI_STR_RXD_ACLK = ACLK, DIR = I, BUS = AXI_STR_RXD, SIGIS = CLK,INITIALVAL = VCC
PORT s2mm_prmry_reset_out_n = ARESETN, DIR = O, BUS = AXI_STR_RXD, SIGIS = RST
PORT AXI_STR_RXD_TVALID = TVALID, DIR = I, BUS = AXI_STR_RXD
PORT AXI_STR_RXD_TREADY = TREADY, DIR = O, BUS = AXI_STR_RXD, INITIALVAL = GND
PORT AXI_STR_RXD_TLAST = TLAST, DIR = I, BUS = AXI_STR_RXD
PORTAXI_STR_RXD_TKEEP
= TKEEP, DIR = I, BUS =AXI_STR_RXD, VEC = [((C_S_AXI_DATA_WIDTH/8)-1):0], ENDIAN = LITTLE
PORTAXI_STR_RXD_TDEST
= TDEST, DIR = I, BUS =AXI_STR_RXD, VEC = [3:0], ENDIAN = LITTLE
PORT AXI_STR_RXD_TDATA = TDATA, DIR = I, BUS = AXI_STR_RXD, VEC =[(C_S_AXI_DATA_WIDTH-1):0], ENDIAN = LITTLE

这个是AXI-FIFO-Stream 这个IP核的引脚描述,看到有TKEEP和TDEST这两个接口,在数据位宽32bit情况下这两个接口都是4bit的。

再看我们利用大名鼎鼎的XPS自定义IP向导生成的Stream 类型IP核,是怎么定义对外接口的。

module my_stream_fir

(
  // 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
  // DO NOT EDIT ABOVE THIS LINE ////////////////////
);

哈,直接忽略掉了TDEST和TKEEP,好轻松随意!

莫非就是因为这两个引脚造成的无法通信?

其实这时,最应该做的一件事,就是搞清楚TKEEP和TDEST到底是什么意思。ARM官方文档上有说明,这里先卖个关子,暂时不表。我们先看看这两个信号到底是谁引起无法通信的。

Q1:在XPS里,想把某总线的一部分信号线断开,一部分信号线连上,这个功能可以用GUI实现吗?

我没有用XPS做这件事。

我觉得XPS简直就是鸡肋,灵活性极差,很多功能都不支持,不同的IP核接口的各种不匹配!我本来想在XPS中添加一个AXI-Stream总线监视器Chipscope IP,但是一直报错,说TDEST位宽不一致,查了一下chipscope_axi_monitor_v2_1_0.mpd这个档案,发现有一句PARAMETER C_MON_AXI_S_TDEST_WIDTH = 1,而AXI-FIFO-Stream的TDEST宽度固定为4bit,导致不能连接。XPS中不能直接修改自带IP的MPD!论坛上有人修改MHS文件来实现匹配,觉得不够直接。于是,用了一计瞒天过海,不让XPS知道连接方式,而是将AXI-FIFO-Stream的TXD和RXD引脚全部设为External,我们可以轻松绕开XPS这个傻×,做自己想做的事。

关掉XPS,在PlanAhead里面生成顶层模块,代码如下:

//-----------------------------------------------------------------------------
// module_1_stub.v
//-----------------------------------------------------------------------------

module module_1_stub
  (
    processing_system7_0_MIO,
    processing_system7_0_PS_SRSTB,
    processing_system7_0_PS_CLK,
    processing_system7_0_PS_PORB,
    processing_system7_0_DDR_Clk,
    processing_system7_0_DDR_Clk_n,
    processing_system7_0_DDR_CKE,
    processing_system7_0_DDR_CS_n,
    processing_system7_0_DDR_RAS_n,
    processing_system7_0_DDR_CAS_n,
    processing_system7_0_DDR_WEB_pin,
    processing_system7_0_DDR_BankAddr,
    processing_system7_0_DDR_Addr,
    processing_system7_0_DDR_ODT,
    processing_system7_0_DDR_DRSTB,
    processing_system7_0_DDR_DQ,
    processing_system7_0_DDR_DM,
    processing_system7_0_DDR_DQS,
    processing_system7_0_DDR_DQS_n,
    processing_system7_0_DDR_VRN,
    processing_system7_0_DDR_VRP

//删除和Stream IP相关的引脚,我们并不是真的连接到物理引脚,而是在顶层模块里实现连接
  );
  inout [53:0] processing_system7_0_MIO;
  input processing_system7_0_PS_SRSTB;
  input processing_system7_0_PS_CLK;
  input processing_system7_0_PS_PORB;
  inout processing_system7_0_DDR_Clk;
  inout processing_system7_0_DDR_Clk_n;
  inout processing_system7_0_DDR_CKE;
  inout processing_system7_0_DDR_CS_n;
  inout processing_system7_0_DDR_RAS_n;
  inout processing_system7_0_DDR_CAS_n;
  output processing_system7_0_DDR_WEB_pin;
  inout [2:0] processing_system7_0_DDR_BankAddr;
  inout [14:0] processing_system7_0_DDR_Addr;
  inout processing_system7_0_DDR_ODT;
  inout processing_system7_0_DDR_DRSTB;
  inout [31:0] processing_system7_0_DDR_DQ;
  inout [3:0] processing_system7_0_DDR_DM;
  inout [3:0] processing_system7_0_DDR_DQS;
  inout [3:0] processing_system7_0_DDR_DQS_n;
  inout processing_system7_0_DDR_VRN;
  inout processing_system7_0_DDR_VRP;




  //下面这一堆本来是input或output,这里都改成wire类型
  wire axi_fifo_mm_s_0_mm2s_prmry_reset_out_n_pin;
  wire axi_fifo_mm_s_0_AXI_STR_TXD_TVALID_pin;
  wire axi_fifo_mm_s_0_AXI_STR_TXD_TREADY_pin;
  wire axi_fifo_mm_s_0_AXI_STR_TXD_TLAST_pin;
  wire [3:0] axi_fifo_mm_s_0_AXI_STR_TXD_TKEEP_pin;
  wire [3:0] axi_fifo_mm_s_0_AXI_STR_TXD_TDEST_pin;
  wire [31:0] axi_fifo_mm_s_0_AXI_STR_TXD_TDATA_pin;
  wire axi_fifo_mm_s_0_s2mm_prmry_reset_out_n_pin;
  wire axi_fifo_mm_s_0_AXI_STR_RXD_TVALID_pin;
  wire axi_fifo_mm_s_0_AXI_STR_RXD_TREADY_pin;
  wire axi_fifo_mm_s_0_AXI_STR_RXD_TLAST_pin;
  wire [3:0] axi_fifo_mm_s_0_AXI_STR_RXD_TKEEP_pin;
  wire [3:0] axi_fifo_mm_s_0_AXI_STR_RXD_TDEST_pin;
  wire [31:0] axi_fifo_mm_s_0_AXI_STR_RXD_TDATA_pin;
  wire processing_system7_0_FCLK_CLK0_pin;  //这个也顺便从XPS里引出,方便做同步时钟

//下面实现手动连接总线引脚,先全都连上

assign axi_fifo_mm_s_0_AXI_STR_RXD_TVALID_pin =axi_fifo_mm_s_0_AXI_STR_TXD_TVALID_pin;
assign axi_fifo_mm_s_0_AXI_STR_TXD_TREADY_pin =axi_fifo_mm_s_0_AXI_STR_RXD_TREADY_pin;
assign axi_fifo_mm_s_0_AXI_STR_RXD_TLAST_pin =axi_fifo_mm_s_0_AXI_STR_TXD_TLAST_pin;
assign axi_fifo_mm_s_0_AXI_STR_RXD_TKEEP_pin = axi_fifo_mm_s_0_AXI_STR_TXD_TKEEP_pin;
assign axi_fifo_mm_s_0_AXI_STR_RXD_TDEST_pin =axi_fifo_mm_s_0_AXI_STR_TXD_TDEST_pin;
assign axi_fifo_mm_s_0_AXI_STR_RXD_TDATA_pin =axi_fifo_mm_s_0_AXI_STR_TXD_TDATA_pin;

  (* BOX_TYPE = "user_black_box" *)
  module_1
    module_1_i (
      .processing_system7_0_MIO (processing_system7_0_MIO ),
      .processing_system7_0_PS_SRSTB (processing_system7_0_PS_SRSTB ),
      .processing_system7_0_PS_CLK (processing_system7_0_PS_CLK ),
      .processing_system7_0_PS_PORB ( processing_system7_0_PS_PORB),
      .processing_system7_0_DDR_Clk (processing_system7_0_DDR_Clk ),
      .processing_system7_0_DDR_Clk_n (processing_system7_0_DDR_Clk_n ),
      .processing_system7_0_DDR_CKE (processing_system7_0_DDR_CKE ),
      .processing_system7_0_DDR_CS_n (processing_system7_0_DDR_CS_n ),
      .processing_system7_0_DDR_RAS_n (processing_system7_0_DDR_RAS_n ),
      .processing_system7_0_DDR_CAS_n (processing_system7_0_DDR_CAS_n ),
      .processing_system7_0_DDR_WEB_pin (processing_system7_0_DDR_WEB_pin ),
      .processing_system7_0_DDR_BankAddr (processing_system7_0_DDR_BankAddr ),
      .processing_system7_0_DDR_Addr (processing_system7_0_DDR_Addr ),
      .processing_system7_0_DDR_ODT (processing_system7_0_DDR_ODT ),
      .processing_system7_0_DDR_DRSTB (processing_system7_0_DDR_DRSTB ),
      .processing_system7_0_DDR_DQ (processing_system7_0_DDR_DQ ),
      .processing_system7_0_DDR_DM (processing_system7_0_DDR_DM ),
      .processing_system7_0_DDR_DQS (processing_system7_0_DDR_DQS ),
      .processing_system7_0_DDR_DQS_n (processing_system7_0_DDR_DQS_n ),
      .processing_system7_0_DDR_VRN (processing_system7_0_DDR_VRN ),
      .processing_system7_0_DDR_VRP (processing_system7_0_DDR_VRP ),
      .axi_fifo_mm_s_0_AXI_STR_TXD_ACLK_pin (axi_fifo_mm_s_0_AXI_STR_TXD_ACLK_pin ),
      .axi_fifo_mm_s_0_mm2s_prmry_reset_out_n_pin (axi_fifo_mm_s_0_mm2s_prmry_reset_out_n_pin ),
      .axi_fifo_mm_s_0_AXI_STR_TXD_TVALID_pin (axi_fifo_mm_s_0_AXI_STR_TXD_TVALID_pin ),
      .axi_fifo_mm_s_0_AXI_STR_TXD_TREADY_pin (axi_fifo_mm_s_0_AXI_STR_TXD_TREADY_pin ),
      .axi_fifo_mm_s_0_AXI_STR_TXD_TLAST_pin (axi_fifo_mm_s_0_AXI_STR_TXD_TLAST_pin ),
      .axi_fifo_mm_s_0_AXI_STR_TXD_TKEEP_pin (axi_fifo_mm_s_0_AXI_STR_TXD_TKEEP_pin ),
      .axi_fifo_mm_s_0_AXI_STR_TXD_TDEST_pin (axi_fifo_mm_s_0_AXI_STR_TXD_TDEST_pin ),
      .axi_fifo_mm_s_0_AXI_STR_TXD_TDATA_pin (axi_fifo_mm_s_0_AXI_STR_TXD_TDATA_pin ),
      .axi_fifo_mm_s_0_AXI_STR_RXD_ACLK_pin (axi_fifo_mm_s_0_AXI_STR_RXD_ACLK_pin ),
      .axi_fifo_mm_s_0_s2mm_prmry_reset_out_n_pin (axi_fifo_mm_s_0_s2mm_prmry_reset_out_n_pin ),
      .axi_fifo_mm_s_0_AXI_STR_RXD_TVALID_pin (axi_fifo_mm_s_0_AXI_STR_RXD_TVALID_pin ),
      .axi_fifo_mm_s_0_AXI_STR_RXD_TREADY_pin (axi_fifo_mm_s_0_AXI_STR_RXD_TREADY_pin ),
      .axi_fifo_mm_s_0_AXI_STR_RXD_TLAST_pin (axi_fifo_mm_s_0_AXI_STR_RXD_TLAST_pin ),
      .axi_fifo_mm_s_0_AXI_STR_RXD_TKEEP_pin (axi_fifo_mm_s_0_AXI_STR_RXD_TKEEP_pin ),
      .axi_fifo_mm_s_0_AXI_STR_RXD_TDEST_pin (axi_fifo_mm_s_0_AXI_STR_RXD_TDEST_pin ),
      .axi_fifo_mm_s_0_AXI_STR_RXD_TDATA_pin (axi_fifo_mm_s_0_AXI_STR_RXD_TDATA_pin ),
      .processing_system7_0_FCLK_CLK3_pin (processing_system7_0_FCLK_CLK3_pin )
    );
   

//为了便于观察,添加了Chipscope模块,一会可以看波形的!
    wire [35:0] mycontrol;
        chipscope_icon_v1_06_a_0 myicon (
        .CONTROL0(mycontrol) // INOUT BUS[35:0]
    );
    chipscope_ila_v1_05_a_0 myila (
        .CONTROL(mycontrol), // INOUT BUS[35:0]
        .CLK(processing_system7_0_FCLK_CLK0_pin),// IN
       .TRIG0({axi_fifo_mm_s_0_AXI_STR_RXD_TVALID_pin,
axi_fifo_mm_s_0_AXI_STR_TXD_TREADY_pin,
axi_fifo_mm_s_0_AXI_STR_RXD_TLAST_pin,
axi_fifo_mm_s_0_AXI_STR_RXD_TKEEP_pin,
axi_fifo_mm_s_0_AXI_STR_RXD_TDEST_pin,
axi_fifo_mm_s_0_AXI_STR_RXD_TDATA_pin}) // IN BUS [42:0]
    );
endmodule

好了,这回再综合,实现,出bit,导出SDK,下载bit,下载elf,运行。结果正常!

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image007.jpg

先讲到这里,下次再做对照试验。


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

 
2013-05-18 23:33


前言:项目中用到Stream类型总线,便于处理连续数据流。本来以为AXIStream总线很简单,应该不会有问题,但恍惚一月过去了,中间遇到许多坎坷,绝大部分都是软件问题(个人很鄙视用GUI操作,但还没有找到一种完全不用GUI的开发方法。如果有,吾愿毅然卸掉XPS!)

环境:Windows XP 32bit;ISE 14.2;超级终端;

使用IP:AXI-Stream-FIFO,自定义Stream类型IP

BSP:裸机

正文:书接上回。为了和前面的实验形成对照,我们需要保证每次只有一个量是不同的。

首先,为了便于Chipscope调试,我们将软件代码稍微修改,改成一个死循环,不至于运行一次就清场,Chipscope不好捕捉。再说一句Xilinx的坏话吧,SDK和Chipscope协调真是费劲,如果想先下载、运行elf,再用chipscope捕捉信号,程序运行快得让你措手不及。如果先让Chipscope一直处于捕捉状态,后下载代码,你妹呀,SDK直接挂掉,说JTAG电缆没连!看来这俩软件是互斥的,不能同时占用JTAG,所以必须修改代码,让代码运行起来之后就不再需要SDK的支持,而是我们手动通过串口来控制软件的执行。

软件代码如下:

#include

#include"platform.h"

#include"xllfifo.h"

#define r1 t1

XLlFifo t1;

int sendram[8] ={1,2,3,4,5,6,7,8};

int recvram[8] ={0,0,0,0,0,0,0,0};

void print(char*str);

#defineAXI_FIFO_BASE 0x74200000

#define ISR 0

#define IER 1

#define TDFR 2

#define TDFV 3

#define TDFD 4

#define TLF 5

#define RDFR 6

#define RDFO 7

#define RDFD 8

#define RLF 9

#define LLR 10

voiddebug_register(unsigned int * p)

{

      printf("ISR = 0x%x\n",*(p+ISR));

      if(*(p+ISR))

      {

             unsigned int t = *(p+ISR);

             *(p+ISR)=t&0xffffffff;

      }

      printf("ISR = 0x%x\n",*(p+ISR));

      printf("IER = 0x%x\n",*(p+IER));

      printf("TDFR = 0x%x\n",*(p+TDFR));

      printf("TDFV = 0x%x\n",*(p+TDFV));

      printf("TDFD = 0x%x\n",*(p+TDFD));

      printf("TLF = 0x%x\n",*(p+TLF));

      printf("RDFR = 0x%x\n",*(p+RDFR));

      printf("RDFO = 0x%x\n",*(p+RDFO));

//    printf("RDFD = 0x%x\n",*(p+RDFD));

//    printf("RLF = 0x%x\n",*(p+RLF));

      printf("LLR = 0x%x\n",*(p+LLR));

}

int main()

{

      int status=0;

      int rxlen;

   init_platform();

   printf("Hello World\n\r");

   while(1)                     //死循环,进去出不来了…………………………

   {

//   debug_register((unsigned int *)AXI_FIFO_BASE);

   XLlFifo_Initialize(&t1,AXI_FIFO_BASE);

//  XLlFifo_Initialize(&r1,0x74200000);

   XLlFifo_Write(&t1,sendram,8*4);

   XLlFifo_TxSetLen(&r1,8*4);

   print("Transmit begin!\n\r");

//   debug_register((unsigned int *)AXI_FIFO_BASE);

   if(XLlFifo_RxOccupancy(&r1))

   {

          rxlen=XLlFifo_RxGetLen(&r1);

          printf("Rcv Length:%d\n",rxlen);

          XLlFifo_Read(&r1, recvram,rxlen);

          int sum=0,i;

          for(i = 0;i<8;i++)

          {

                if(recvram!=sendram)

                {

                       printf("Error in index %d,value = %d\n",i,recvram);

                       sum++;

                }

          }

          if(sum==0)

          {

                printf("Success!\n");

          }

   }

   print("Transmit done!\n\r");

   scanf("%d",&status);        //串口输入一个数,然后放行。这样可以手动控制软件运行

   }

   cleanup_platform();

   return 0;

}

这样,打开Chipscope,先连接设备,设置一下总线名称以利于阅读,捕获得到波形如下图所示

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image008.jpgv:shapes="imgddiv290550">

可见TDATA,TLAST,TVALID波形完全正确,按照顺序发送走了1,2,3,4,5,6,7,8.。TDEST一直等于4’b0000,TKEEP一直等于4’b1111。

好了,对照试验要进行了。

实验1:乐不思蜀
将顶层模块中TDEST连线注释掉。//assign

axi_fifo_mm_s_0_AXI_STR_RXD_TDEST_pin =axi_fifo_mm_s_0_AXI_STR_TXD_TDEST_pin;

经过试验发现,不影响试验的正确性!

实验2:兵粮寸断

将顶层模块中TKEEP连线注释掉。//assign
axi_fifo_mm_s_0_AXI_STR_RXD_TKEEP_pin =axi_fifo_mm_s_0_AXI_STR_TXD_TKEEP_pin;  同时将实验1中的注释打开。

经过实验发现,接收数据字节数为4,与发送字节数32相比大相径庭。

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image009.jpg

看一下Chipscope波形:

file:///C:/DOCUME~1/ADMINI~1/LOCALS~1/Temp/msohtml1/01/clip_image010.jpg

可见,当TKEEP信号为4‘b0000时,数据完全不能通行(即所谓“兵粮寸断”)。

找到了数据不通的原因,我们就可以解释TDEST和TKEEP信号的含义了。

TKEEP信号位宽数=TDATA字节数

TKEEP某一bit负责监视TDATA相应字节。如果该字节有效,则对应bit=1;否则,bit=0时,认为该字节为NULL字节,空字节,无效字节,接收端直接丢弃。

TDEST在多机通信中用于指定接收端地址。由于本实验不涉及多机通信,只是点对点通信,所以该信号不起作用。

有了上面的基础,我又做了一个尝试,将TDEST和TKEEP都断开,RXD端TKEEP直接赋值为4'b1111,RXD端TDEST直接赋值为4'b0000.实验结果表明完全正确!


【参赛手记】AXI总线学习  2013-04-15 22:50

自从拿到ZED板卡,就开始接触ARM+FPGA这个神奇的架构。

AXI,是这两者之间最佳的通信手段。

xps中用户自定义IP核可以拥有AXI-Lite,AXI4,AXI-Stream,PLB和FSL这些接口。

其中AXI-Lite具有轻量级,结构简单的特点,适合小批量数据、简单控制场合。AXI4和AXI-Lite差不多,只是增加了一项功能就是突发传输,可以连续对一片地址进行一次性读写。上面两种均采用内存映射控制方式,即ARM将用户自定义IP编入某一地址进行访问,读写时就像在读写自己的片内RAM,编程也很方便,开发难度较低。代价就是资源占用过多,需要额外的读地址线、写地址线、读数据线、写数据线、写应答线这些信号线,而且传输速度受限(主要是因为采用AXI-GP物理接口,带宽很低)。

另外一种AXI接口就是AXI-Stream,这是一种连续传输的接口技术,速度能达到很高,而且不需要地址线(有点像FIFO,你就一直读或一直写就行)。这类IP不能通过上面的内存映射方式控制,必须有一个转换装置,例如AXI-Interconnector,AXI-DMA模块就能实现内存映射到流式接口的转换,但编程较复杂,调试起来没有内存映射方式直观,必须要通过芯片内部调试接口(Chipscope)来观察。

AXI-Stream适用的场合有很多:视频流处理;通信协议转换;数字信号处理;无线通信等。

其本质都是针对数值流构建的数据通路,从信源(例如ARM内存、DMA、无线接收前端等)到信宿(例如HDMI显示器、音频输出等)构建起连续的数据流。这种接口适合做实时信号处理。当然,实际处理中也有分块和不分块的情况,典型分块情况就是计算FFT。

后面两种貌似在ZED上面用处不大,都是Microblaze的接口。不过应该也有桥接IP。没有研究。

我们项目中属于典型的流式数据,从射频前端、ADC采集到信号传输到DDR2内存,组织为时分复用或并行通路来传输数值数据到自定义IP,并携带额外信息(当前帧均衡器的系数),利用控制流通道传输过去。处理结果仍传回DDR2中,交给主机显示或存储为文件。和DDR2的通信需要借助AXI-HP物理接口,PL部分为master,负责数据搬移。

通过以上论述,应该比较清楚的看到整个数据走向了。具体实施细节还需要进一步研究。


【参赛手记】System Generator与XPS连接的方法  2013-03-17 15:07

【项目信息】
基于ZED-Board实现的宽带实时自适应均衡系统

由于项目需要用到DSP算法实现,考虑用SystemGenerator辅助设计算法,但在参赛赠送的书里没有相关知识,需要自己动手摸索。

还好在Matlab Simulink 内包含库XilinxBlocksets里右键发现了帮助文档,进入之后了解了怎么用Xilinx器件来完成算法设计,并生成网表。最重要的器件应该是Gatewayin和gatewayout,将matlab内的模块与Xilinx模块隔离,实现数位精度变换。这两个元件相当于input和output,在生成实例的时候可以看到。

看了帮助文档后,做个实验,只用PL部分,不用PS部分,用Project Navigator新建一个工程,添加一个SysGen模块,里面什么都不用做,直接将Gatewayin连接到Gatewayout,位数设为8bit,无小数部分。然后新建顶层verilog模块,生成一个SysGen的实例(有模板可用),将输入通过UCF约束到电路板的SW,输出约束到LED,生成.bit,下载到PL上,很容易就成功实现了。

在此基础上,再次进入matlab,在Gatewayin和Gatewayout之间加入一个移位器模块,设为左移,重新生成.bit下载,也成功了。

单独的DSP模块显然功能很有限,必须要和双核ARM9连接起来才有价值。

新建planahead工程,参考懒兔子的自定义IP部分,生成mygpio模块,然后连接到AXI总线,这时不要连接到外部引脚,而是连接到顶层模块的内部信号线。

在planahead里面,新建SysGen模块,搭建自己的算法,生成模块,回到planahead。

之前的内部信号线连接到SysGen模块的输入。

还是像懒兔子一样导出软件工程,开发方法没有差别。下载.bit文件配置PL部分,然后运行程序。

这时就可以发现,你通过串口写到slave寄存器里的值,通过SysGen模块的处理,就能迅速反馈给你。这就说明,通过planahead顶层模块可以实现SysGen和XPS的互联,属于系统互联,这里涉及到两个系统,一个是DSP子系统,一个是PS子系统,二者都在planahead顶层模块被实例化,然后用信号线互联。

这是最近两天的摸索,算法实现总算有了点眉目。

另外看到在SysGen里面支持AXI4总线,而且可以导出为XPS工程,还有待进一步研究,找到更紧密的连接方式,这样可以进一步提高数据在DSP模块和XPS之间的传输效率。如果大家有什么好的建议,请不要吝啬及时告诉我一声~~~~

时间真是快,还有很多值得去研究的内容,包括GAL均衡算法优化,多种算法(LMS,RLS,基于小波的。。。)性能对比,盲均衡技术实现(CMA,DF)等。。。除了算法实现外还有信号类型分析(DRM,DTV,GSM,4G),时不我与,不再多说,回去继续埋头学习。。。


发表于 2017-9-4 10:33:15 | 显示全部楼层
你好,我们恰好需要在自作的IP通过VDMA与DDR数据交互,但我们是在petalinux系统上来做的,现在不知道怎么让ddr把数据发到IP中,不知你是否知道?
发表于 2017-11-20 21:43:03 | 显示全部楼层
图片挂了
发表于 2018-11-28 16:57:12 | 显示全部楼层
学习中。。。。
发表于 2018-11-29 10:22:23 | 显示全部楼层
谢谢分享
发表于 2019-2-21 08:52:46 | 显示全部楼层
图片为啥看不了呢,怎样才能看得到图片?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

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


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

GMT+8, 2024-11-22 08:54 , Processed in 0.024398 second(s), 10 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
快速回复 返回顶部 返回列表