本文介绍一个PL读取DDR3 的实用程序,查表程序。这个可能也可叫RAM 程序吧,把数据表格放置在固定的一段RAM 中, PL就可以查表,用于计算。其实也可以放置代码在这段空间里,你的ip 就可以像cpu一样执行代码。
这样的结果是sdk 运行程序,把查表数据放在 Ram空间里,PL 就可以访问你给的数据。
PL读写DDR3 实现PS和PL间的数据交互 为基础,修改而成。也可参考PL读写DDR3 实现PS和PL间的数据交互 代码分析
MasterIP 代码修改
1: 添加端口
//input original
input wire [15 : 0] IN_ADDR,
//out results
output wire [15 : 0] OUT_DATA,
//output cycle for search
output wire [7 : 0] OUT_TIM,
2 读地址段:
//Read Addresses
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0)
begin
axi_araddr <= 0;
end
// Signals a new write address/ write data is
// available by user logic
else
begin
axi_araddr <= IN_ADDR;
end
end
3:状态机代码,只有读,控制read_issued, start_single_read
//implement master command interface state machine only read
always @ ( posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 1'b0)
begin
// reset condition
start_single_write <= 1'b0;
write_issued <= 1'b0;
start_single_read <= 1'b0;
read_issued <= 1'b0;
end
else
if (~axi_arvalid && ~M_AXI_RVALID && ~last_read && ~start_single_read && ~read_issued)
begin
start_single_read <= 1'b1;
read_issued <= 1'b1;
end
else if (axi_rready)
begin
read_issued <= 1'b0;
end
else
begin
start_single_read <= 1'b0; //Negate to generate a pulse
end
end //MASTER_EXECUTION_PROC
4 last read
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
last_read <= 1'b0;
//The last read should be associated with a read address ready response
else if ((read_index == C_M_TRANSACTIONS_NUM) && (M_AXI_ARREADY) )
last_read <= 1'b1;
else
last_read <= last_read;
end
5 读取数据放在输出上:
//Data read and put on outport
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0)
out_data<=0;
//The read data when available (on axi_rready)
else if (M_AXI_RVALID && axi_rready)
out_data<=M_AXI_RDATA;
else
out_data<=out_data;
end
6 评估读一次的时间周期数:
//escapecycle
always @(posedge M_AXI_ACLK)
begin
if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1)
escapecycle <= 1'b0;
//The last read should be associated with a read address ready response
else if ((~last_read) &&(escapecycle<100000))
escapecycle <= escapecycle+1'b1;
else if (escapecycle == 100000)
escapecycle[15]<=1;
else
escapecycle <= escapecycle;
end
输出查表数据,除了查表数据,还统计查表时间,15位是溢出位,14位为查表结束标志。
assign OUT_DATA=out_data;
assign OUT_TIM[13:0]=escapecycle[13:0];
assign OUT_TIM[15]=escapecycle[15];
assign OUT_TIM[14]=last_read;
验证接口ip
可以用gpio 连接相应接口,完成测试。但我直接加axi_gpio 做测试,连接不上,自定义一个。方法见:zynq 7000 自定义IP 实验
这个gpio axi ip 的主要内容是提供地址,启动,2路输出, 获取输出的数据,和状态信息,2路输入。
在2个文件中都添加io
// Users to add ports here
output wire [31:0]out_addr,
output wire start,
input wire [31:0]in_data,
input wire [15:0]in_cycles_last,
// User ports ends
在接口代码中添加函数调用的接口:
.out_addr(out_addr),
.start(start),
.in_data(in_data),
.in_cycles_last(in_cycles_last),
实现代码中添加
读取部分:16‘h0003是32位的高位,可以为任何数据,实际没用,但保证全32位。
2'h2 : reg_data_out <= in_data;
2'h3 : reg_data_out <= { 16'h0003, in_cycles_last };
写保持不变,但用户逻辑修改如下:
// Add user logic here
assign out_addr=slv_reg0;
assign start=slv_reg1[0];
// User logic ends
验证工程
验证工程可以以PL读写DDR3 实现PS和PL间的数据交互 为基础,注意我们的测试ip 是Slave Axi IP, 原理图如下:
除了基本的axi 总线连接外,
axi_gpio的 start =>SearchTable 的 init_axi_txn
out_addr=>in_addr
in_data <=out_data
in_cycles_last <=out_tim
这个是数据控制的基本流程,axi_gpio 输出地址,发出启动信号,然后接收in_cycles_last数据,完成了就接收in_data
在这里处理器设置是这样的,注意这里的2个勾选:
产生比特流,输出,然后启动SDK
验证程序
验证代码的流程是先写入数据,这里有3种方式写入数据:test_ram(), setram(),setram2(),实际情况你只要选取一种,我开始总是不对一样,所以想了3种写入的方法。
searchi(addr) 则是查数据,验证。
这里程序有点问题,test_ram() 写的数据比较多,几乎所有的数据都写了,先运行这个,然后setram()或setram2(), 写的数据量不多的时候,比如2000, 结果是pl 读取的数据还是原来的,没有被覆盖,验证读取的数据是更新了的。后面写的数据很多的时候,比如200000,pl读取的数据就是更新了的。是不是ddr3 有缓存问题。
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xil_io.h"
#define MY_IP 0x44A00000
#define DDRAM 0x04000000
#define MAXLENTH 512000000
unsigned char lasercmd[MAXLENTH];
u32 ok=0;
//int ip_data;
void test_ram(void)
{
int i;
int* ip=(int*)lasercmd;
printf("ip=%x\n",(int)ip);
for(i=0;i<512000000/4;i++)
{
ip[i]=i;
}
for(i=0;i<51;i++)
{
//printf("%d=%d\n",i,ip[i*1000000]);
}
}
void setram(u32 count)
{
u32* comp_addr=DDRAM;
u32 i;
for(i=0;i<count;i++)
{
*(comp_addr+i)=i*2;
}
}
void setram2(u32 count)
{
u32 comp_addr=DDRAM;
u32 i;
for(i=0;i<count;i+=4)
{
Xil_Out32(comp_addr+i,i);
}
}
void searchi(u32 addr)
{
u16 state=0;
u32 result,result2;
int delay=0;
unsigned int comp;
unsigned char* comp_addr=addr+DDRAM;
int* ip=(int*)lasercmd;
comp=*((u32*)comp_addr);
Xil_Out32(MY_IP+4,0);
Xil_Out32(MY_IP,addr);
Xil_Out32(MY_IP+4,1);
state=Xil_In32(MY_IP+12);
while((state&0x4000)==0)
{
delay++;
if(delay>1000)
break;
state=Xil_In32(MY_IP+12);
}
result=Xil_In32(MY_IP+8);
result2=ip[result];
if(result!=comp)
printf("addr=%d:result=%x,ccomp=%x cycles=%x delay=%d \n",addr,result,comp,state&0xfff,delay);
else { ok++;printf("------addr=%d rusult=%x\n",addr,result);}
}
int main()
{
u32 i;
u32 counts;
u32 setNo;
init_platform();
counts=800;
setNo=60000;
test_ram();
setram2(setNo);
ok=0;
for(i=0;i<counts;i+=4)
searchi(i);
printf("counts=%d,ok=%d,setNo=%d\n",(int)counts,(int)ok,(int)setNo);
cleanup_platform();
return 0;
}
运行结果
下面是更改参数运行2次的结果。SetNo =70000, pl读取的数据没有更新,而SetNo =80000的时候pl 读取的数据更新了。
ip=114324
addr=0:result=fbaf37,ccomp=0 cycles=4f delay=3
addr=4:result=fbaf38,ccomp=4 cycles=51 delay=4
addr=8:result=fbaf39,ccomp=8 cycles=4a delay=3
addr=12:result=fbaf3a,ccomp=c cycles=4b delay=3
addr=16:result=fbaf3b,ccomp=10 cycles=49 delay=3
...
addr=788:result=fbaffc,ccomp=314 cycles=4a delay=3
addr=792:result=fbaffd,ccomp=318 cycles=4b delay=3
addr=796:result=fbaffe,ccomp=31c cycles=4b delay=3
counts=800,ok=0,setNo=60000
ip=114324
------addr=0 rusult=0
------addr=4 rusult=4
------addr=8 rusult=8
------addr=12 rusult=c
------addr=16 rusult=10
...
------addr=788 rusult=314
------addr=792 rusult=318
------addr=796 rusult=31c
counts=800,ok=200,setNo=70000
问题和改进
上面pl 读取的数据和直接ram 读取的数据不一样的原因是内存缓存的原因。
下面是我在xilinx 上询问的结果。
在初始化后添加
Xil_DCacheDisable();
问题得到解决。2者不在不一样了。
int main()
{
u32 i;
u32 counts;
u32 setNo;
init_platform();
Xil_DCacheDisable();
counts=800;
setNo=300;
test_ram();
setram2(setNo);
ok=0;nook=0;
for(i=0;i<counts;i+=4)
searchi(i);
printf("counts=%d,ok=%d,setNo=%d\n",(int)counts,(int)ok,(int)setNo);
cleanup_platform();
return 0;
}