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

 下面讲一个例子,来加深对上面介绍内容的理解。笔者使用的软件版本为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,将内容改为:

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),添加TKEEP信号并设置TLAST信号。

module my_stream_ip
 (
  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
 );

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;

   localparamNUMBER_OF_INPUT_WORDS  = 8;

   localparamNUMBER_OF_OUTPUT_WORDS = 8;

   localparam Idle  =3'b100;
   localparam Read_Inputs = 3'b010;
   localparam Write_Outputs  = 3'b001;

   reg [2:0] state;

   reg [31:0] sum;

   reg [NUMBER_OF_INPUT_WORDS -1:0] nr_of_reads;
   reg [NUMBER_OF_OUTPUT_WORDS - 1:0] nr_of_writes;

   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
          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
             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代码见我的博客文章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里面的内容改为如下代码。 

#include <stdio.h>
#include <stdlib.h>
#include "platform.h"
#include "xil_cache.h"          //必须包含该头文件,实现cache操作

#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_CURDESC14
#define S2MM_TAILDESC16
#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); //将cache内容同步到DDR2
 *(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< sizeofbuffer/4;i++)
 {
  sendram[i]=i*2;
 }
}
void show_recvbuffer()
{
 int i;
 printf("Recv contents are:\n");
 for(i=0;i< sizeofbuffer/4;i++)
 {
  printf("%d\t",recvram[i]);
 }
 printf("\r\n");
}
void show_sendbuffer()
{
 int i;
 printf("Send contents are:\n");
 for(i=0;i< sizeofbuffer/4;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((unsignedint *)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();

Xil_DCacheInvalidateRange((u32)recvram,sizeofbuffer);      //将DDR2内容同步到cache

    show_recvbuffer();
    cleanup_platform();

return 0;
} 

保存,等待生成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看到了相关的帖子才明白这一点,在此感谢论坛上国内外的技术大牛为社区提供的支持。

时间: 2024-09-12 07:59:55

【OpenHW参赛手记】AXI-Stream接口开发详细流程的相关文章

【OpenHW参赛手记】AXI-Stream接口介绍

AXI4-Stream协议是一种用来连接需要交换数据的两个部件的标准接口,它可以用于连接一个产生数据的主机和一个接受数据的从机.当然它也可以用于连接多个主机和从机.该协议支持多种数据流使用相同共享总线集合,允许构建类似于路由.宽窄总线.窄宽总线等更为普遍的互联.AXI4-Stream接口的信号线定义如图1所示[1].比较重要的信号线有: ACLK为时钟线,所有信号都在ACLK上升沿被采样: ARESETn为复位线,低电平有效: TVALID为主机数据同步线,为高表示主机准备好发送数据: TREA

【OpenHW参赛手记】AXI初体验

去年报名参加了OpenHW2012开源硬件大赛,入围后收到了Xilinx赞助的ZED-Board. 自从今年3月拿到ZED板卡,就开始体验ARM+FPGA这个神奇的架构. AXI,是PS与PL之间最佳的通信手段. Xilinx XPS中用户自定义IP核可以拥有AXI-Lite,AXI4,AXI-Stream,PLB和FSL这些接口实现和PS通信. 其中AXI-Lite具有轻量级,结构简单的特点,适合小批量数据.简单控制场合.AXI4和AXI-Lite差不多,只是增加了一项功能就是突发传输,可以连

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

由于项目需要用到DSP算法实现,考虑用System Generator辅助设计算法,但在参赛赠送的书里没有相关知识,需要自己动手摸索. 还好在Matlab Simulink 内包含库XilinxBlocksets里右键发现了帮助文档,进入之后了解了怎么用Xilinx器件来完成算法设计,并生成网表.最重要的器件应该是Gatewayin和gatewayout,将matlab内的模块与Xilinx模块隔离,实现数位精度变换.这两个元件相当于input和output,在生成实例的时候可以看到.   看了

PHP使用Face++接口开发微信公众平台人脸识别系统的方法_php实例

本文实例讲述了PHP使用Face++接口开发微信公众平台人脸识别系统的方法.分享给大家供大家参考.具体如下: 效果图如下: 具体步骤如下: 首先,先登录Face++的官网注册账号:官网链接 注册之后会获取到api_secret和api_key,这些在调用接口的时候需要用到. 然后接下来的就是使用PHP脚本调用API了. 在使用PHP开发微信公共平台的时候,推荐使用Github上的一款不错的框架:wechat-php-sdk 对于微信的常用接口做了一些封装,核心文件wechat.class.php

php支付宝在线支付接口开发教程_php实例

1.什么是第三方支付 所谓第三方支付,就是一些和各大银行签约.并具备一定实力和信誉保障的第三方独立机构提供的交易支持平台.在通过第三方支付平台的交易中,买方选购商品后,使用第三方平台提供的账户进行货款支付,由第三方通知卖家货款到达. 目前提供第三方支付的机构很多,常见的有支付宝.财付通.快钱.网银在线.易宝支付.云网等各大支付平台.网站如果需要实现第三方支付首先应该向第三方支付平台申请一个账号并签署协议,协议生效后第三方支付平台将为其开通在线支付功能,通过程序将接口集成到网站中. 为什么要使用第

C#微信公众号与订阅号接口开发示例代码_C#教程

本文实例讲述了C#微信公众号与订阅号接口开发示例代码.分享给大家供大家参考,具体如下: using System; using System.Web; using System.IO; using System.Text; using System.Web.Security; using weixin_api; public class wxgz_api : IHttpHandler { public void ProcessRequest(HttpContext context) { cont

微信公众平台消息接口开发(32)空气质量指数查询

原文:微信公众平台消息接口开发(32)空气质量指数查询 微信公众平台开发 微信公众平台开发者 微信公众平台开发模式 空气质量指数 PM2.5 作者:方倍工作室 原文:http://www.cnblogs.com/txw1958/archive/2013/05/30/weixin-if32-air-quality.html   一.获取原版数据 在中国环境监测总站http://www.cnemc.cn/ 可以找到全国城市空气质量实时发布平台,其地址为 http://113.108.142.147:

php支付宝在线支付接口开发教程

1.什么是第三方支付 所谓第三方支付,就是一些和各大银行签约.并具备一定实力和信誉保障的第三方独立机构提供的交易支持平台.在通过第三方支付平台的交易中,买方选购商品后,使用第三方平台提供的账户进行货款支付,由第三方通知卖家货款到达. 目前提供第三方支付的机构很多,常见的有支付宝.财付通.快钱.网银在线.易宝支付.云网等各大支付平台.网站如果需要实现第三方支付首先应该向第三方支付平台申请一个账号并签署协议,协议生效后第三方支付平台将为其开通在线支付功能,通过程序将接口集成到网站中. 为什么要使用第

集成代码生成器 java 微信 自定义菜单 java微信接口开发 公众平台 SSM redis shiro 多数据源

获取[下载地址]  QQ: 313596790A 调用摄像头拍照,自定义裁剪编辑头像,头像图片色度调节B 集成代码生成器 [正反双向](单表.主表.明细表.树形表,快速开发利器)+快速表单构建器 freemaker模版技术 ,0个代码不用写,生成完整的一个模块,带页面.建表sql脚本,处理类,service等完整模块C 集成阿里巴巴数据库连接池druid  数据库连接池  阿里巴巴的 druid.Druid在监控.可扩展性.稳定性和性能方面都有明显的优势D 集成安全权限框架shiro  Shir