zynq sdk 开发之通过 BRAM 进行 PL 与 PS 的数据交互

如何在 zynq 中进行 PL 端与 PS 端的数据交互?

在zynq的使用中,高效的进行 BRAM 与 zynq 硬核的数据交换至关重要,当我们需要进行小批量的数据交换时,可以考虑采用 BRAM 作为数据交换的媒介,在 PS 端,将数据通过 AXI 总线写入 PL端的 BRAM 里面去,并且再读出来。整体的设计图如下所示:

在这里插入图片描述

PL 端硬件的设计

在 vivado 2017.4 下,可以通过 block design 快速进行模块的添加与设计。设计出来的架构如图所示:
在这里插入图片描述
注意事项:

  • BRAM 使用真实双端口 RAM ,一个端口连接 axi BRAM controller(用于 PS 端控制 BRAM),一个连接 PL BRAM controller(一个自定义的 PL 端 BRAM 控制器,可以由 PS 端给一些控制信号)。
  • BRAM需要选择 BRAM controller 模式。
  • zynq 端要打开中断

在经过上面的 block design 设计之后,可将硬件导出,然后打开 sdk ide 开始进行 PS 端程序的开发。

程序的整体流程如下:

  1. 在 PS 端输入起始地址和长度
  2. CPU通过 axi BRAM controller 写入BRAM数据
  3. 通知 PL BRAM controller 读取数据
  4. PL 内部读完后向相同的位置写入数据,初始数据有CPU告知
  5. 写完后使能 write_end 信号,出发GPIO 中断
  6. 中断读取 BRAM 数据,打印显示

关于自定义 IP 的设计:
自定义 IP 实现了对 PL 端 BRAM 的读写操作,并且在外部封装的有 AXI 总线的端口,核心的读写逻辑如下:

module ram_read_write
    (
	 input              clk,
	 input              rst_n,
	 //bram port
     input      [31:0]  din,	 
	 output reg [31:0]  dout,
	 output reg         en,
	 output reg [3:0]   we,
	 output             rst,
	 output reg [31:0]  addr,
	 //control signal
	 input              start,       //start to read and write bram
	 input      [31:0]  init_data,   //initial data defined by software
	 output reg         start_clr,   //clear start register
	 input      [31:0]  len,         //data count
	 input      [31:0]  start_addr,   //start bram address
	 //Interrupt
	 input              intr_clr,    //clear interrupt
	 output reg         intr         //interrupt
    );


assign rst = 1'b0 ;
	
localparam IDLE      = 3'd0 ;
localparam READ_RAM  = 3'd1 ;
localparam READ_END  = 3'd2 ;
localparam WRITE_RAM = 3'd3 ;
localparam WRITE_END = 3'd4 ;

reg [2:0] state ;
reg [31:0] len_tmp ;
reg [31:0] start_addr_tmp ;


//Main statement
always @(posedge clk or negedge rst_n)
begin
  if (~rst_n)
  begin
    state      <= IDLE  ;
	dout       <= 32'd0 ;
	en         <= 1'b0  ;
	we         <= 4'd0  ;
	addr       <= 32'd0 ;
	intr       <= 1'b0  ;
	start_clr  <= 1'b0  ;
	len_tmp    <= 32'd0 ;
	start_addr_tmp <= 32'd0 ;
  end
	
  else
  begin
    case(state)
	IDLE            : begin
			            if (start)
						begin
			              state <= READ_RAM     ;
						  addr  <= start_addr   ;
						  start_addr_tmp <= start_addr ;
						  len_tmp <= len ;
						  dout <= init_data ;
						  en    <= 1'b1 ;
						  start_clr <= 1'b1 ;
						end			
						if (intr_clr)
							intr <= 1'b0 ;
			          end

    
    READ_RAM        : begin
	                    if ((addr - start_addr_tmp) == len_tmp - 4)      //read completed
						begin
						  state <= READ_END ;
						  en    <= 1'b0     ;
						end
						else
						begin
						  addr <= addr + 32'd4 ;				  //address is byte based, for 32bit data width, adding 4		  
						end
						start_clr <= 1'b0 ;
					  end
					  
    READ_END        : begin
	                    addr  <= start_addr_tmp ;
	                    en <= 1'b1 ;
                        we <= 4'hf ;
					    state <= WRITE_RAM  ;					    
					  end
    
	WRITE_RAM       : begin
	                    if ((addr - start_addr_tmp) == len_tmp - 4)   //write completed
						begin
						  state <= WRITE_END ;
						  dout  <= 32'd0 ;
						  en    <= 1'b0  ;
						  we    <= 4'd0  ;
						end
						else
						begin
						  addr <= addr + 32'd4 ;
						  dout <= dout + 32'd1 ;						  
						end
					  end
					  
	WRITE_END       : begin
	                    addr <= 32'd0 ;
						intr <= 1'b1 ;
					    state <= IDLE ;					    
					  end	
	default         : state <= IDLE ;
	endcase
  end
end	



	
endmodule

这个自定义 IP 的实现是为了令实例更为完整,方便在逻辑分析仪中进行观察。

PS 端软件的设计

打开 sdk 工具,开始进行软件开发。

代码如下:

/* ------------------------------------------------------------ */
/*				Include File Definitions						*/
/* ------------------------------------------------------------ */

#include "xil_printf.h"
#include "xbram.h"
#include <stdio.h>
#include "pl_bram_ctrl.h"
#include "xscugic.h"

#define BRAM_CTRL_BASE      XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR
#define BRAM_CTRL_HIGH      XPAR_AXI_BRAM_CTRL_0_S_AXI_HIGHADDR
#define PL_RAM_BASE         XPAR_PL_BRAM_CTRL_0_S00_AXI_BASEADDR
#define PL_RAM_CTRL         PL_BRAM_CTRL_S00_AXI_SLV_REG0_OFFSET
#define PL_RAM_INIT_DATA    PL_BRAM_CTRL_S00_AXI_SLV_REG1_OFFSET
#define PL_RAM_LEN          PL_BRAM_CTRL_S00_AXI_SLV_REG2_OFFSET
#define PL_RAM_ST_ADDR      PL_BRAM_CTRL_S00_AXI_SLV_REG3_OFFSET

#define START_MASK   0x00000001
#define INTRCLR_MASK 0x00000002


#define INTC_DEVICE_ID	    XPAR_SCUGIC_SINGLE_DEVICE_ID
#define INTR_ID              XPAR_FABRIC_PL_BRAM_CTRL_0_INTR_INTR

#define TEST_START_VAL      0xC
/*
 * BRAM bytes number
 */
#define BRAM_BYTENUM        4


XScuGic INTCInst;

int Len  ;
int Start_Addr ;
int Intr_flag ;
/*
 * Function declaration
 */
int bram_read_write() ;
int IntrInitFuntion(u16 DeviceId);
void IntrHandler(void *InstancePtr);

int main()
{

	int Status;
	Intr_flag = 1 ;

    IntrInitFuntion(INTC_DEVICE_ID) ;

	while(1)
	{
		if (Intr_flag)
		{
			Intr_flag = 0 ;
			printf("Please provide start address\t\n") ;
			scanf("%d", &Start_Addr) ;
			printf("Start address is %d\t\n", Start_Addr) ;
			printf("Please provide length\t\n") ;
			scanf("%d", &Len) ;
			printf("Length is %d\t\n", Len) ;
			Status = bram_read_write() ;
			if (Status != XST_SUCCESS)
			{
				xil_printf("Bram Test Failed!\r\n") ;
				xil_printf("******************************************\r\n");
				Intr_flag = 1 ;
			}
		}
	}
}

// 对BRAM的读写操作
int bram_read_write()
{

	u32 Write_Data = TEST_START_VAL ; // 要写入的数据
	int i ;

	/*
	 * if exceed BRAM address range, assert error
	 */
	if ((Start_Addr + Len) > (BRAM_CTRL_HIGH - BRAM_CTRL_BASE + 1)/4)
	{
		xil_printf("******************************************\r\n");
		xil_printf("Error! Exceed Bram Control Address Range!\r\n");
		return XST_FAILURE ;
	}
	/*
	 * Write data to BRAM
	 */
	for(i = BRAM_BYTENUM*Start_Addr ; i < BRAM_BYTENUM*(Start_Addr + Len) ; i += BRAM_BYTENUM)
	{
		XBram_WriteReg(XPAR_BRAM_0_BASEADDR, i , Write_Data) ;
		Write_Data += 1 ;
	}
	//Set ram read and write length
	PL_BRAM_CTRL_mWriteReg(PL_RAM_BASE, PL_RAM_LEN , BRAM_BYTENUM*Len) ;
	//Set ram start address
	PL_BRAM_CTRL_mWriteReg(PL_RAM_BASE, PL_RAM_ST_ADDR , BRAM_BYTENUM*Start_Addr) ;
	//Set pl initial data
	PL_BRAM_CTRL_mWriteReg(PL_RAM_BASE, PL_RAM_INIT_DATA , (Start_Addr+1)) ;
	//Set ram start signal
	PL_BRAM_CTRL_mWriteReg(PL_RAM_BASE, PL_RAM_CTRL , START_MASK) ;

	return XST_SUCCESS ;
}



int IntrInitFuntion(u16 DeviceId)
{
	XScuGic_Config *IntcConfig;
	int Status ;


	//check device id
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	//intialization
	Status = XScuGic_CfgInitialize(&INTCInst, IntcConfig, IntcConfig->CpuBaseAddress) ;
	if (Status != XST_SUCCESS)
		return XST_FAILURE ;


	XScuGic_SetPriorityTriggerType(&INTCInst, INTR_ID,
			0xA0, 0x3);

	Status = XScuGic_Connect(&INTCInst, INTR_ID,
			(Xil_ExceptionHandler)IntrHandler,
			(void *)NULL) ;
	if (Status != XST_SUCCESS)
		return XST_FAILURE ;

	XScuGic_Enable(&INTCInst, INTR_ID) ;


	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
			(Xil_ExceptionHandler)XScuGic_InterruptHandler,
			&INTCInst);
	Xil_ExceptionEnable();


	return XST_SUCCESS ;

}


void IntrHandler(void *CallbackRef)
{
	int Read_Data ;

	int i ;
	printf("Enter interrupt\t\n");
	//clear interrupt status
	PL_BRAM_CTRL_mWriteReg(PL_RAM_BASE, PL_RAM_CTRL , INTRCLR_MASK) ;

	for(i = BRAM_BYTENUM*Start_Addr ; i < BRAM_BYTENUM*(Start_Addr + Len) ; i += BRAM_BYTENUM)
	{
		Read_Data = XBram_ReadReg(XPAR_BRAM_0_BASEADDR , i) ;
		printf("Address is %d\t Read data is %d\t\n",  i/BRAM_BYTENUM ,Read_Data) ;
	}
	Intr_flag = 1 ;
}

最终实现效果如下图所示:
在这里插入图片描述
需要完整项目代码的可以留言。

  • 11
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 42
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 42
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wangbowj123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值