马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?注册
x
Xilinx AXI-Stream IP调试日记
来源 :http://www.eefocus.com/zhaoyongke/blog/
【参赛手记】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),时不我与,不再多说,回去继续埋头学习。。。
|