ZYNQ学习之路4.ZYNQ通过GP口读取PL内部RAM数据

实验环境:window 7 64 bit, vivado 2017.1, ZTURN board.

参考手册:Xilinx Distributed Memory Generator

        在ZYNQ开发中,经常需要PS与PL进行数据交互。当数据量比较大时往往需要先缓存一部分然后批量传输到Linux系统,否则中断响应时间无法满足要求,使用双端口RAM或许是一种不错的方法。本文详细描述PS端读写PL端片的双端口RAM数据的实验过程。本次实验使用FPGA内部的Block Memory,PS端通过Master GP0端口向RAM写数据,通过Master GP1端口读出数据,本次实验涉及到AXI BRAM Controller和Block Memory Generator等IP核。

一. SRAM介绍

1.1 双端口RAM结构

        Xilinx的ZYNQ7Z010内部FPGA是virtex7系列,内部有32KB的Block Memory,可以用它作为ROM,Single-Port RAM, Dual-Port RAM或Simple Dual-Port RAM, RAM的区别在于读写数据线与地址总线的数量的区别,根据自己的需求进行选择。

RAM类型

功能

数据总线数量

地址总线数量

Single-Port

通过一个端口进行数据的读写操作

1

1

Dual-Port

通过两个端口读写数据

2

2

Simple Dual-Port

一个端口进行写操作,另一个端口进行读操作

1

2

        双端口RAM示意图如下:

图1-1.双端口RAM示意图题

        双端口RAM相比其他几种RAM更复杂,所以本文只介绍双端口RAM的使用。

1.2 双端口RAM控制信号

        上文中的示意图中只列出了基本且常用的信号,其他特殊信号在次不做介绍。常用控制信号功能定义如下:

信号

方向

描述

din[31:0]

Input

数据总线输入,32位宽

addr[31:0]

Input

地址总线输入,32位宽,地址从0开始,有效至容量大小

clk

Input

时钟信号输入,1位,写同步时钟

en

Input

使能读,写,复位选项

rst

Input

复位或置位读出锁存寄存器,如果要读出需要置置

wea[3:0]

Input

写使能

dout[31:0]

Output

数据输出,32位宽

        双端口RAM的工作模式有三种,写优先、读优先和无变化模式。

写优先模式:数据同时写入到内存并输出到数据端口,时序如图1-2所示:

图1-2: Write First Mode Example

读优先模式:当要写入内存数据时,先前存储的数据在写地址有效前存储到数据输出总线上。时序图如图1-3所示:

图1-3:Read First Mode Example

无变化模式: 输出锁存器在写入内存期间不改变值。时序图如图1-4所示:

图1-4:No Change Mode Example

1.3 字节写操作

在使用32为数据总线时,数据按32位同时变化,如果只写入8,16,24位的数据或者间隔一个字节写入两个不连续的字节,这是可以使用WE信号,首先看看图1-5时序图。

图1-5: Byte-write Example标题

图中第一个上升沿到来之时,数据输入0xFFEEDD,WEA=0x011,写入内容变为0x00FFDD,只有后面两个字节写入成功,可以发现WEA的某个bit位置1时对应数据输入的字节将被写入,其他字节屏蔽写操作,32为数据总线时WEA为4位,从低到高依次对应数据总线低到高的四个字节的掩码,置位可写,复位屏蔽,也就是说写32位数据时WEA[3:0]=b1111.

二. PL端硬件设计

2.1 新建一个vivado工程,选择开发板所对应的芯片型号。

2.2 Add IP, 添加ZYNQ,双击设置属性,保留uart1,选择M AXI GP0 interface和M AXI GP1 interface

图2-1: ZYNQ IP核GP端口设置

2.3 Add IP, 添加一个Block memory Generator,选择true dual Port RAM类型。

图2-2: Block Ram设置

2.4 Add IP,添加两个AXI BRAM Controller,把number of BRAM interfaces改成1。

图2-3: AXI BRAM Controler设置

2.5 点击run connection automation,把axi_bram_ctl_0的Master选择为GP0, axi_bram_ctl_1的Master选择为GP1,interconnect ip都选择为 new AXI Interconnect。

图2-4: 设置AXI连接

2.6 点击重新布局,最后得到如下结构。

图2-5: 双端口RAM系统结构

2.7 点击Address Editor,可以看到GP0和GP1的地址已经自动分配好。

图2-6: RAM地址分配

2.8 在sources下选择block design右击,点击Create HDL wrapper,生成系统的顶层模块,在system_wrapper上右击,选择generate output products...。

2.9 编译综合工程,最后generate bitstream,导出硬件(包含bitstream),launch SDK。

 

三. PS端软件的编写与测试

3.1 新建一个以helloworld为模板的工程,SDK自动创建了硬件的bsp,设置终端串口为uart1。

 

3.2 在main函数中编写代码如下:

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

int main()
{
    int a[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
    int b[16] = {0};
    init_platform();

    print("------The test is start...------\n\r");
    xil_printf( "Write data:\n\r");
    memcpy((void *)0x40000000, a, 16*4);
    for(int i = 0;i<16;i++)
    {
    xil_printf( "%x  ",a[i]);
    }
    xil_printf( "\n\r");
    sleep(1);
    xil_printf( "Read data:\n\r");
    memcpy(b, (void*)0x80000000, 4*16);
    for(int i = 0;i<16;i++)
    {
    	xil_printf( "%x  ",b[i]);
    }

    xil_printf( "\n\r");

    xil_printf("------The test is end!------\n\r");
    cleanup_platform();
    return 0;
}

程序开始向地址0x40000000处复制数组a的内容,并打印数组a,然后将地址0x80000000开始的64字节(数组a大小)内容复制到数组b中,然后打印数组b。如果不复制到数组b,显然数组b是全零数组,上电启动初始状态内存中全部为为0。0x40000000和0x80000000分别是双端口RAM的两个端口的起始地址,如果写入与读取的内容一致,则说明双端口RAM的两套读写地址操作的是同一个存储空间,说明实验是成功的。

3.3 连接好仿真器和串口终端,先下载FPGA程序,然后运行PS的软件,在终端显示如下:

可以看出读取的与写入的数据完全一样,然而读取的地址不一样,说明实验是成功的。

说明:由于BRAM是连接在GP0和GP1接口上通过AXI总线进行读写操作,在地址空间上寻址可以直接使用指针操作,因此memcpy这样的C基本库函数可以直接对GP0和GP1地址空间进行操作,在访问速度上比使用循环指针操作要快很多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章