欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Xilinx ZYNQ学习笔记(2)——PS端读写单口BRAM

程序员文章站 2024-02-23 16:13:58
...

PS读写单端BRAM

Block RAM是PL部分的存储器阵列,为了与DRAM(分布式RAM)区分开,所以叫Block RAM。
ZYNQ的每一个BRAM大小为36KB,7020的BRAM有140个(4.9M),7030有265个(9.3M),7045有545个(19.2M)。每一个BRAM都有两个共享数据的独立端口,当然是可以配置的,可用于片内数据缓存、FIFO缓冲。
Xilinx ZYNQ学习笔记(2)——PS端读写单口BRAM
使用BRAM进行PS-PL或者反向进行数据传输,是PS与PL进行互联的一种方法,实现较为简单。但是在实现双端通信之前,我们首先需要熟悉每个不同部分对应的通信方式。
今天我们主要研究的是PS端写入、读取PL端的BRAM。

在本例中,我们使用下图的第二种连接方式,虽然BLOCK RAM为双口RAM,但是我们仅使用其中一端。在图中可以看到,BRAM模块的端口B为OPEN状态。
Xilinx ZYNQ学习笔记(2)——PS端读写单口BRAM

为了使用PS端调用PL端的BRAM,我们需要使用几个重要的IP核。

  1. Block RAM Controller
  2. Block Memory Generator

其block design如下。
Xilinx ZYNQ学习笔记(2)——PS端读写单口BRAM
PS核心方面打开UART1作为debug Serial。
Xilinx ZYNQ学习笔记(2)——PS端读写单口BRAM
在Block Memory Generator方面,将Memory类型设置为单口RAM。在other options中关闭Safety Circuit
Xilinx ZYNQ学习笔记(2)——PS端读写单口BRAM
Xilinx ZYNQ学习笔记(2)——PS端读写单口BRAM

在Block RAM Controller方面,设置如下:
将BRAM interface数量调整为1。因为我们使用的是一个单口RAM。
Xilinx ZYNQ学习笔记(2)——PS端读写单口BRAM
所有IP添加完成后,执行自动连接,并重新生成layout,即可得到一个清爽简洁的BD。
打开Address Editor,可以在这里对BRAM的起始地址、偏移、名称、范围等做手动修改。
Xilinx ZYNQ学习笔记(2)——PS端读写单口BRAM
接下来综合,并生成比特流文件,导出hardware,启动SDK。
我的代码如下:

/******************************************************************************
*
* Copyright (C) 2018 - 2020 TerayTech, Inc.  All rights reserved.
*
******************************************************************************/

/*
 * helloworld.c: simple test application
 *
 * This application configures UART 16550 to baud rate 9600.
 * PS7 UART (Zynq) is not initialized by this application, since
 * bootrom/bsp configures it to baud rate 115200
 *
 * ------------------------------------------------
 * | UART TYPE   BAUD RATE                        |
 * ------------------------------------------------
 *   uartns550   9600
 *   uartlite    Configurable only in HW design
 *   ps7_uart    115200 (configured by bootrom/bsp)
 */

#include <stdio.h>
#include "xil_io.h"
#include "platform.h"
#include "xil_printf.h"


int main(void)
{

	unsigned int chara;
	chara = 0;

	printf("Welcome To TerayTech Enterprise 2020 XILINX FPGA BRAM Test DEMO\r\n");

	//Write 200 words with Xil_Out8
	for(int i = 0;i < 200; i++){
		Xil_Out8(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + i, chara);
		chara++;
	}

	//read 100 words with Xil_In16
	for(int i = 0;i < 100; i++){
		chara = Xil_In16(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + (i*2));
		int ad = XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + (i*2);
		printf("Address:0x%08x", ad);
		printf("  Word%d = 0x%04x\n\r", i, chara);
	}

	return(0);
}

void tt_rw_test(){
	//Write Progress

	int word1,word2,word3,word4;

	Xil_Out8(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 0, 0xAB);//Write first word, length 4 byte
	Xil_Out8(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 1, 0xFF);
	Xil_Out8(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 2, 0x34);
	Xil_Out8(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 3, 0x8C);

	Xil_Out8(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 4, 0xEF);//第二个字,4字节
	Xil_Out8(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 5, 0xBE);
	Xil_Out8(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 6, 0xAD);
	Xil_Out8(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 7, 0xDE);

	Xil_Out16(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 0x10, 0x1209);//第三个字,4字节
	Xil_Out16(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 0x12, 0xFE31);

	Xil_Out16(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 0x14, 0x6587);//第四个字,4字节
	Xil_Out16(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 0x16, 0xAAAA);


	Xil_Out32(0xE000A244, 0x0AAA);  //第5个字,4字节


	word1 = Xil_In32(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR);
	word2 = Xil_In32(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 8);
	word3 = Xil_In32(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR + 0x10);
	word4 = Xil_In32(0xE000A244);

	printf("Word1 = 0x%08x\n\r", word1);
	printf("Word2 = 0x%08x\n\r", word2);
	printf("Word3 = 0x%08x\n\r", word3);
	printf("Word4 = 0x%08x\n\r", word4);
}


其运行结果如下:

Welcome To TerayTech Enterprise 2020 Xilinx FPGA BRAM Test DEMO
Address:0x40000000  Word0 = 0x0100
Address:0x40000002  Word1 = 0x0302
Address:0x40000004  Word2 = 0x0504
Address:0x40000006  Word3 = 0x0706
Address:0x40000008  Word4 = 0x0908
Address:0x4000000a  Word5 = 0x0b0a
Address:0x4000000c  Word6 = 0x0d0c
Address:0x4000000e  Word7 = 0x0f0e
Address:0x40000010  Word8 = 0x1110
Address:0x40000012  Word9 = 0x1312
Address:0x40000014  Word10 = 0x1514
Address:0x40000016  Word11 = 0x1716
Address:0x40000018  Word12 = 0x1918
Address:0x4000001a  Word13 = 0x1b1a
Address:0x4000001c  Word14 = 0x1d1c
Address:0x4000001e  Word15 = 0x1f1e
Address:0x40000020  Word16 = 0x2120
......

可以观察到,其通过从指定地址读取并返回从该地址读取的16位值,该值为我们刚才分两次写入的数据。
00地址的数据为00,01地址的数据为01,故一次性读取16位即为00-01地址的数据,随后地址偏移为2。

打开Xil_io.h,可以看到相关函数,注释清晰较容易理解含义。

/*****************************************************************************/
/**
*
* @brief    Performs an input operation for a memory location by reading
*           from the specified address and returning the 8 bit Value read from
*            that address.
*
* @param	Addr: contains the address to perform the input operation
*
* @return	The 8 bit Value read from the specified input address.

*
******************************************************************************/
static INLINE u8 Xil_In8(UINTPTR Addr)
{
	return *(volatile u8 *) Addr;
}

/*****************************************************************************/
/**
*
* @brief    Performs an input operation for a memory location by reading from
*           the specified address and returning the 16 bit Value read from that
*           address.
*
* @param	Addr: contains the address to perform the input operation
*
* @return	The 16 bit Value read from the specified input address.
*
******************************************************************************/
static INLINE u16 Xil_In16(UINTPTR Addr)
{
	return *(volatile u16 *) Addr;
}

至此,对于PS读写PL端BRAM的测试就完成了。