PL读写DDR3 实现PS和PL间的数据交互 查表程序

本文介绍一个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, 原理图如下:

PL读写DDR3 实现PS和PL间的数据交互 查表程序_第1张图片

除了基本的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个勾选:

PL读写DDR3 实现PS和PL间的数据交互 查表程序_第2张图片 产生比特流,输出,然后启动SDK

验证程序

验证代码的流程是先写入数据,这里有3种方式写入数据:test_ram(), setram(),setram2(),实际情况你只要选取一种,我开始总是不对一样,所以想了3种写入的方法。

searchi(addr) 则是查数据,验证。

这里程序有点问题,test_ram() 写的数据比较多,几乎所有的数据都写了,先运行这个,然后setram()或setram2(), 写的数据量不多的时候,比如2000, 结果是pl 读取的数据还是原来的,没有被覆盖,验证读取的数据是更新了的。后面写的数据很多的时候,比如200000,pl读取的数据就是更新了的。是不是ddr3 有缓存问题。

#include 
#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;i1000)
			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

运行结果

下面是更改参数运行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 上询问的结果。

https://forums.xilinx.com/t5/Memory-Interfaces-and-NoC/Why-does-my-pl-read-ddr-and-get-old-one-not-new-one/m-p/1086792#M16668

在初始化后添加

 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

 

你可能感兴趣的:(zynq,fpga)