Zynq 7000 PL和PS通信——使用BRAM

Zynq 7000 PL和PS通信——使用BRAM

  • 介绍
  • 1 准备工作
  • 2 PL搭建BRAM
  • 3 下载PL程序
  • 4 编写Linux应用程序
  • 5 问题
    • 5.1 BRAM的寻址的问题
    • 5.2 PS地址映射

介绍

Xilinx公司的Zynq 7000系列SoC除了有功能强大的FPGA,还集成了带有双核ARM Cortex A9的PS。在协同使用PS和PL时,就不免需要在PS和PL之间进行数据交互。本文主要讲述安装了linux操作系统的PS如何与PL进行数据交互。

1 准备工作

PC环境:win10
vivado版本:2015.4
开发板:ZC706(已安装从SD卡启动的Linux系统)

系统的总体框架如下图所示。利用AXI接口实现PS和PL的通信,PS作为主机,PL作为从机,通过AXI Interconnect分别和两个BRAM的控制器相连。BRAM部分有两个端口,这两个端口都可对这块BRAM进行读写操作。AXI的通信接口由AXI Interconnect和AXI BRAM Controller IP核完成,在PS端,可以将AXI BRAM Controller当做字符设备进行读写操作,在PL端,可以直接对BRAM进行读写操作。其中AXI接口的具体通信过程由IP核完成,在进行应用开发时可以不用太关心。下图这块BRAM的两个端口都连到了PS,没有端口供PL进行操作了,在实际应用中,可以一个端口连到PS,另一个端口由PL控制,然后商量好PS和PL的读写规则即可使用BRAM实现PL和PS的数据传输。
Zynq 7000 PL和PS通信——使用BRAM_第1张图片

2 PL搭建BRAM

打开vivado,新建一个工程,在选择器件时,选择ZC706评估板(如果不是此评估板,应该根据实际芯片型号选择)。
Zynq 7000 PL和PS通信——使用BRAM_第2张图片
建好工程后,新建一个bd文件。
在这里插入图片描述
在Block Design文件中,添加一个ZYNQ7 Processing System IP核。
Zynq 7000 PL和PS通信——使用BRAM_第3张图片
双击这个IP核对其进行配置,由于本文所用是官方提供的开发板,可以直接点击Presets -> ZC706使用默认的配置。如果是其他型号,应该按照实际情况进行配置。
Zynq 7000 PL和PS通信——使用BRAM_第4张图片
然后点击PS-PLConfiguration里,把GP0选择,然后点击ok保存配置。
Zynq 7000 PL和PS通信——使用BRAM_第5张图片
继续添加IP核,选择AXI BRAM Controller。
Zynq 7000 PL和PS通信——使用BRAM_第6张图片
添加AXI BRAM Controller后,双击并对齐进行配置,将接口选择为1个。配置完成后以同样的方法再添加一个AXI BRAM Controller。
Zynq 7000 PL和PS通信——使用BRAM_第7张图片
添加BRAM,并对齐进行配置,将其配置成双端口。
Zynq 7000 PL和PS通信——使用BRAM_第8张图片
Zynq 7000 PL和PS通信——使用BRAM_第9张图片
然后把AXI接口的时钟线连上,并选择自动连线。
Zynq 7000 PL和PS通信——使用BRAM_第10张图片
最终的Block Design如下图所示。
Zynq 7000 PL和PS通信——使用BRAM_第11张图片
接下来右击bd文件,选择Create HDL Wrapper,并选择Let Vivado manage wrapper and auto-update。
Zynq 7000 PL和PS通信——使用BRAM_第12张图片
Zynq 7000 PL和PS通信——使用BRAM_第13张图片
然后点击Address Editor,可以看到两个外设的地址分别为0x40000000和0x42000000。这个地址是给PS使用的,可以按照自己的喜好进行修改(当然地址不可随心所欲的修改,有些地址是不能给它们使用的!)。BRAM的容量大小也可在此处修改,只不过修改之后需要再运行一下Create HDL Wrapper重新进行打包,本文就使用默认的8K大小。
Zynq 7000 PL和PS通信——使用BRAM_第14张图片
然后直接点击Generate Bitstream,等待其一步到位。
Zynq 7000 PL和PS通信——使用BRAM_第15张图片

3 下载PL程序

给开发板上电。前期已经制作了从SD卡启动的linux操作系统,上电后,系统启动,可以从串口进入系统进行命令行操作。连接PL后,点击Program Device下载bitstream文件。
Zynq 7000 PL和PS通信——使用BRAM_第16张图片

4 编写Linux应用程序

下载完成后,在linux系统下编写一个测试读写的C语言程序,测试代码如下所示。

#include
#include
#include
#include
#include
#include

#define BRAM_CTRL_0	0x40000000	//AXI_bram_ctrl_0的物理地址
#define BRAM_CTRL_1	0x42000000	//AXI_bram_ctrl_1的物理地址
#define DATA_LEN		10			//写入和读取的数据长度

int main(){
    unsigned int * map_base0;
	unsigned int * map_base1;
 
    int fd = open("/dev/mem", O_RDWR|O_SYNC);
    if (fd == -1){	
		printf("can't open /dev/mem !\n");
        return (-1);
    }
	printf("/dev/mem is opened !\n");
	
    map_base0 = mmap(NULL, DATA_LEN * 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, BRAM_CTRL_0);
	map_base1 = mmap(NULL, DATA_LEN * 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd, BRAM_CTRL_1);
    if (map_base0 == 0 || map_base1 == 0){
        printf("NULL pointer!\n");
    }
    else{
        printf("mmap successfull!\n");
    }
	
    unsigned long addr;
    unsigned int content;
    int i = 0;
	
	//写数据到BRAM
	printf("\nWrite data to BRAM via AXI_bram_ctrl_0\n");
    for (i= 0; i < DATA_LEN; i++){
        addr = (unsigned long)(map_base0 + i);
        content = i + 2;
		map_base0[i] = content;
        printf("%2dth data, address: 0x%lx data_write: 0x%x\t\t\n", i, addr, content);
    }
	
	//从BRAM读数据
	printf("\nRead data from BRAM via AXI_bram_ctrl_1\n");
    for (i= 0; i < DATA_LEN; i++){
        addr = (unsigned long)(map_base1 + i);
		content = map_base1[i];
        printf("%2dth data, address: 0x%lx data_write: 0x%x\t\t\n", i, addr, content);
    }
    
    close(fd);
    munmap(map_base0, DATA_LEN);
	munmap(map_base1, DATA_LEN);
 
    return 0;
}

在linux下使用GCC进行编译,然后运行,运行结果如下图所示。
Zynq 7000 PL和PS通信——使用BRAM_第17张图片
这里对测试的C语言程序进行简单的分析。首先打开/dev/mem字符设备节点,然后将BRAM的两个控制器AXI_bram_ctrl_0和AXI_bram_ctrl_1的物理地址进行地址映射,这两个控制器在linux看来就相当于两个外设。先向AXI_bram_ctrl_0写入10个数据,然后从AXI_bram_ctrl_1读出10个数据,由于这两个控制器所连接的是同一块BRAM存储空间,所以读出来的数据应该和写入的数据是一致的。如此便实现了PL和PS的数据交互。

5 问题

5.1 BRAM的寻址的问题

如果将BRAM的一端交给PL来操作,如果选择BRAM的容量为8K。如果查看BRAM,会发现其深度竟然只有2048,并非8192,如下图所示。
Zynq 7000 PL和PS通信——使用BRAM_第18张图片
其实这是因为BRAM的地址是以8位为单位的,BRAM容量设置成8K是指8KB,即8K×8bits。换算成32位宽时为8K×8bits = 2K×32bits,也就是说只有2048个32位地址。PL在对BRAM进行寻址时,也是使用的8位位宽寻址,也就是说地址会从0~1FFF,会有13根地址线,而并非11根地址线32位位宽。这是PL对BRAM进行操作时需要注意的。

5.2 PS地址映射

在PL这边搭好工程之后,会分配两个地址0x40000000和0x42000000。这两个地址是真实的物理地址,在Linux系统下不能直接访问,需要进行地址映射,然后才能对其进行操作。关于地址映射可是个麻烦的事情,还需仔细学习和谨慎使用。

你可能感兴趣的:(Zynq 7000 PL和PS通信——使用BRAM)