构建SoC系统,毕竟是需要实现PS和PL间的数据交互,而像上一讲那样PL主机与PL从机之间通过AXI4-Lite总线进行交互有点杀鸡用牛刀了。
如果PS与PL端进行数据交互,可以直接设计PL端为从机,PS端向PL端的reg写入数据即可,但是对于图像处理等大数据量的数据交互来说,PL端的BRAM毕竟容量有限,很难用BRAM作为两者间的数据缓存器。对于这样的应用来说,利用DDR3作为PS端与PL端之间交互的数据缓存器是最合适不过的。
1.工程规划
再ZedBoard板子的硬件设计方案中,DDR芯片的管脚是绑定到Zynq的DDR接口上的。
而Zynq系统的这个DDR总线接口有是链接在其内部“Memory Interfaces”中的“DDR2/3,LPDDR2 Controller”可配置硬核上的。
因此,要想PL访问板上的DDR3存储器,必须借道Zynq中的“Memory Interfaces----DDR2/3,LPDDR2 Controller”(后文简称“DDR3 Controller”)。根据之前的经验,在Zynq系统中,ARM Core(CPU)能够访问硬核“DDR3 Controller”,根据经验可以确定“DDR3 Controller”一定是一个从设备,而PL要想访问“DDR3 Controller”的话,PL一定要是一个主设备,由PL发起读写操作。
“DDR3 Controller”是封装在Zynq子系统中的,因此,PL必须连接Zynq的从机接口。从上图可以看出,Zynq有两个从机接口,分别是“32b GP AXI Slave Ports”和“High Performance AXI 32b/64b Slave Ports”根据名称可以看出,一个是高性能的,另一个应该是普通的。之前Zynq作为主机连接AXI4-Lite从设备时,走的是“32b GP AXI Master Ports”,可以辅助证明,对于本节应用,走的接口应该是“32b GP AXI Slave Ports”。
交互数据将会经过Zynq子系统的内部总线(用空再考证一下是什么名称)控制器“Central Interconnect”转发给Memory Interfaces。具体数据交互路径如下图所示:
根据上述分析,SoC系统集成方案框图如下:
根据之前的经验,利用向导生成的PL端AXI4-Lite Master IP 用户用例再txn负脉冲触发下,主机模块的逻辑是连续4次(次数默认为4,可通过修改参数C_M00_AXI_TRANSACTIONS_NUM的数值改变次数)向递增的地址区间写入4组测试数据,测试数据每次加1。然后主机模块自动读取刚才写入数据的地址内的数据,将读的的数据与写入数据进行比较,如果正确,主机IP的ERROR信号保持低电平,如果错误,ERROR给出高电平。
本文后面的行为仿真证明:再次给主机IP模块一个txn的触发信号,重新执行4次写入操作+4次读取操作+比较操作。只不过每次触发都从原始基地址开始操作,目标寄存器地址并不增加。
本节的测试工程本着少改动逻辑代码的原则,仍基本采用此逻辑。每次按键触发,执行4次写操作+4次读操作+比对检查操作,增加利用4个LED灯表示每个地址比对结果是否正确(如果正确LED灯亮)。但是当再次按下按钮,LED灯全灭,松开按钮后再执行一组(4次写操作+4次读操作+比对检查)操作,只是新的一组操作地址会递进增加。
2.Zynq系统裁剪
创建工程后,添加一个Zynq IP核,然后调用官方对ZedBoard板中Zynq子系统的默认配置。
得到默认的Zynq硬核配置如下图所示:
对应的Zynq封装如下图所示:
明显看出有很多Zynq中的硬核在本项目中是不需要的,修改配置,将其裁剪掉。
修改后的Zynq配置显示如下:
Zynq的封装图如下所示:
输出端的FCLK_CLK0和FCLK_RESET0_N是输出时钟和复位信号。这两个信号的出处已在前一个图中进行标注。
ZedBoard开发板的ARM端时钟由IC18有源晶振提供33.33333MHz的时钟信号,绑定到Zynq芯片的F7(PS_CLK)管脚上,该管脚是Zynq子系统(PS)的时钟信号。如果工程中添加了Zynq子系统,Vivado会自动将F7绑定到Zynq子系统的PS_CLK管脚上(其实也不需要绑定,这是固话的。F7本身就是专用引脚,但是从硬件结构上也可以看出一个工程只能实例化一个Zynq子系统)。该绑定在约束文件上看不到,而在“Synthesis Design”的“I/O Planning”页面中可以看到。(因为这个管脚是专用管脚,不需要绑定)这里欠个图,后文会有,记着啊!!!
FCLK_CLK0是经过一个时钟管理模块(类似V4中的DCM)生成的,可以修改其参数。Zynq中包含了4个时钟生成模块。配置界面如下:
如果取消勾选,Zynq将不输出时钟信号。
那对于PL端的时序逻辑模块,需要另外寻找时钟信号,在ZedBoard开发板上,IC17有源晶振提供100MHz的GCLK时钟信号,连接到Y9上,可用PL端使用。但是这种方案PS端和PL端由独立的时钟分别驱动,必须考虑跨时钟域问题。
取消FCLK_CLK0的勾选后,Zynq的封装图如下所示:
FCLK_CLK0管脚就没有了,但是FCLK_RESET0_N仍然存在,因为Zynq中的Resets模块是不可配置的。但是Resets有四路输出,为什么这里只有1个呢?是不是跟FCLK_CLKx有绑定呢?
【添加,事后发现,FCLK_RESET0_N也是可以关闭的,操作如下】
太强大的软件了,永远别觉得自己学明白了!!!
为了验证上述疑问,勾选两路FCLK_CLK0和FCLK_CLK2,观察之后的Zynq模块封装如下所示:
没有唤出其余的几路Resets信号。如何召唤其他几路复位信号,还得研究研究。
【个人猜想】:这个FCLK_RESET0_N应该是跟Zynq子系统的复位工作是同步的,有可能Zynq子系统收到复位信号(PS-RST,BTN7,C9,低电平复位)后,FCLK_RESET0_N被拉低,等待一定时间保证Zynq子系统复位完成后,再延时一段时间,再拉高,是的PL端的模块完成复位进入正常工作状态时,Zynq子系统一定已经正常工作。
本项目中,Zynq子系统裁剪完毕后,封装图采用下图方案。
利用FCLK_CLK0驱动PL端的时序逻辑。而将Y9管脚引入的100MHzGCLK信号旁路。
再来看看S_AXI_GP0,即AXI4总线接口(兼容AXI4-Lite)。如下图所示:
终于,画箭头的管脚我们都熟悉了。其他的以后再说。
至此,Zynq子系统我们裁剪完毕了。
【还有个问题】:“DDR3 Controller”硬核在Zynq子系统内部的内存映射还没有看到。打开Address Editor页面,也没有配置信息。
另外,作为从机接口,读写地址总线均为32bits,而不像之前的验证方案,从机的读写地址去掉高位的段地址只留下低位的offset Address,这样的地址接口,能跟PL端的AXI InterConnect匹配吗?
3.PL端主机IP核设计编码
根据之前的经验,利用向导生成一个AXI4-Lite Master IP示例代码,构建主从模块直连的测试系统,从机模块默认内部寄存器数量为4个,构建本仿真系统时,将从机模块内部寄存器数量设置为32个,其余不用修改。
功能仿真文件(详见附录2),模拟连续两次触发txn信号,设置参数,每次txn触发执行两次写读操作,对应的仿真结果如下图所示。
每次txn触发,起始地址均归零,即恢复成初始地址0x4000_0000,写入的数据也变为初始值0xaa00_0000。跟工程设计的逻辑需求不一致。
因此根据需求改造代码逻辑,底层模块的源码详见附录。编码时一定避免Multi-Driver禁告。修改后的逻辑仿真结果如下所示。
可也看出主机模块的功能满足设计要求。
之后将该IP核加入到工程的IP Catalog中。
4.在Block Design中增加PL端IP核
首先在Block Design中实例化一个AzIP_AXI_Master_3的一个IP模块,如下图所示,此时没有连接。
点击页面上的Run Connection Audomation按钮,自动创建连接。整形以后如下图所示。新加的两个模块都是老朋友了,基本逻辑核管脚时序都基本明确了。此时再来看这个连接,基本上就没有问题了。图中用彩色标注出了PL端的时钟信号和复位信号。均由Zynq子系统产生。
同样按F6键检查连接,如果检查无误,表示这个系统的“原理图”画的基本没有问题了。
5.内存映射分析
点击Address Editor标签,看一看,这个图前文在zynq子系统配置完后曾经出现过,但是当时是没有数据的,现在我们放在一起对比一下:
下面我们尝试解读一下,第一幅图时,软件仅仅检测到存在一个AXI Slave接口GP0,当时没有匹配的,所以没有数据,但是当我们加上自行设计的AXI Master接口IP后,软件将二者匹配上了,通过层级结构发现,Zynq上的GP0的Slave接口是匹配用户IP的主机接口的,然后软件自动分配了地址。有一个Base Name,名字是GP0_DDR_LOWOCM,这个名字没看过,不过猜也知道是跟GP0口和DDR相关的,神奇软件怎么知道我想操作DDR呢?猜想应该是配置Zynq时,配置了DDR,所以在Zynq中的总线控制其中激活了DDR相关的段地址,即DDR_LOWCOM。
为了验证上述想法,在Zynq中裁剪掉DDR,具体操作如下:
得到的Zynq封装如下:
可以看到Zynq子系统各种去掉了DDR Control模块,再看Address Editor页面。
可以看到,由于GP0模块的存在,对于Zynq内部总线接口来说,它配置了一个地址GP0_LOW_OCM,关键词是OCM,经过查询,得知OCM的意思是片上存储器(On-Chip Memory),说白了就是内存映射地址。Zynq内部任何一个可配置的外设硬核,其电路均已经硬件实现了,对应的Zynq内部片地址也是固定的,增加外设,应该就会有新的片内地址。
增加了外设USRT1,SD0,USB0,ENET0,QUARD SPI,注意此时没有激活DDR Controller模块。先来看看Zynq的封装。
再来看Address Editor页面
可以看到,对于Quard SPI模块,有单独的地址映射GP0_QSPI_LINEAR,而USRT1,SD0,USB0,ENET0等外设没有,根据Zynq的结构图,大胆猜测,USRT1,SD0,USB0,ENET0等外设均包含在IO Peripherals模块内,因此它们的统一片内地址映射(OCM)的基地址均为GP0_IOP,其中IOP应该就是IO Peripherals的简写。
开始这些数据都没有分配地址,但是前面说了,Zynq中,由于硬件结构都是固定的,剪裁操作是指不激活某些功能模块,个人猜测目的可能是为了低功耗,但是对应的功能模块的内存地址映射都是固定的,跟用户设计的SoC工程无关。
所以这些Zynq内部的映射应该是固定的,我们怎么能够看到呢?点击Address Editor中的Auto Assign Address按钮,如下图所示,系统就会自动填写响应内存映射的Offset Address和Range了。这些都应该是经由GP0路由到这些外设硬核的映射地址。
【补充】LOW_OCM应该就是低速(低性能)存储器地址。有低速对应的肯定就有高速。如下所示。
勾选上图所示项,应该能够打开HIGH_OCM
从上文看出,对于GP0_QSPI_LINEAR和GP0_IOP的命名并没有LOW和OCM等信息。Zynq外设配置不变,仅仅是勾选上图Allow Access to High OCM项,地址映射数据如下。其中HIGH_OCM应该是个独立的内存区域。
去除USRT1,SD0,USB0,ENET0,QUARD SPI等外设,启动DDR Control,仍然勾选Allow Access to High OCM项,得到的Zynq封装如下图:
对应的Address Editor页面如下:
可以看出DDR还是属于LOW_OCM,而HIGH_OCM应该是Zynq系统中的一个特殊区域,具体在哪还不确定,因为跟DDR无关,这里暂时就不关注了。
由于PL中的IP核对Zynq管理的DDR操作是通过地址进行的,因此我们需要对DDR的内存映射整明白。下面再研究研究。
GP0_LOW_OCM已经没有了,但是其对应的地址被包含在GP0_DDR_LOWOCM地址段的低位区间上。
回想《笔记3:PS读写DDR3存储》中的内存映射:
对应的片外DDR3存储芯片的地址的起始位为0x00100000,和这个有点意思。
当然,Address Editor页面中的Offset Address参数和Range参数是可以修改的,而High Address参数是根据前两个参数计算出来的。
必须对Zynq子系统的内存映射原理特别明白,才能正确修改这些参数,因此我们在深入研究研究,看看能不能整明白。
为了深度研究这个问题,在Xilinx Documentation Navigator中搜索OCM,得到如下信息。
可以看到OCM还分为指令OCM和数据OCM。但是这些文件都是比较老的,不是针对Zynq芯片的。没有收获。
上图这些文件也没用,这些讲到都是在PL端用HDL编码实现一个与DDR直接向量的接口控制器的使用方案,而不是设计到Zynq的地址映射方案。
换个思路,查询Zynq的技术文档。
在该文档中得到一些信息,先摘录如下:
Zynq内部有256KB的RAM,就是前面GP0_LOW_OCM或者GP0_HIGH_OCM映射的吗?这个有意思,一定想办法找到。因为之前实验中C语言程序可能就运行在这个内存空间中。
这是前文提到的I/O Peripherals中的内容。
ZedBoard板子用到的芯片是XC7Z020,封装为clg484,但是由于芯片上覆盖了个散热片,不知道是-?。有空问问供货商。
难道是-1???可怜啊,CPU主频最高为667MHz。上图应该是从配置文件中获得的,硬件具体是-?其实还是从芯片丝印看最可靠。不过应该差别不会太大,毕竟是通用开发板,不是为了最求性能的。
下面图很重要!
250KB的RAM(OCM)在图中箭头处。
我的猜测,前面出现的内存映射Base Name,应该指的是Central Interconnect的出口,示意图如下:
找到了,就是在这篇文档中,有下列信息:
从上图看到,0x0000_0000是DDR and OCM,
0xFFFC_0000是OCM
那么Zynq内的256KB RAM(OCM)到底怎么访问呢????郁闷
最后的DDR内存映射配置按照系统默认的,如下:
记得主机模块开发是,使用的默认起始地址是0x4000_0000,这个在代码中定义的。不过利用IP核的GUI也能看到。
从前文知道,这个地址是向PL AXI Slave Port #0,即GP0的,Size是1G。
【问题】PL 中的Master IP向0x4000_0000地址写入数据,经过PL中的AXI Interconnect直连,将会转发给GP0,GP0收到后,怎么在转给DDR呢????
【个人猜测】:传入的地址-0x4000_0000,然后进行路由判断吗?假设是这样的,跑起来看看。
至此,本实验的硬件设计完毕。保存后,退出Block Design页面。
6.封装 & Synthesis
然后记得创建HDL Wrapper,
之后对这个工程进行Run Synthesis。
结果没有error和critical warning。
7.行为仿真
原想对这个包含Zynq的顶层模块进行行为仿真,但是看了一眼端口定义。
跟DDR相关的接口时序我根本没法设定,故此放弃了。
直接下板测试吧。
8.管脚绑定
综合完成后,进入Synthesized Design页面,先看看Schematic
然后重点看I/O Planning
根据原理图,PS_CLK(F7)和PS_POR_B(B5)是在Bank500上,驱动电压是3.3V,而PS_RST(C9)是在Bank501上,驱动电压是1.8V。但是这几项是Fixed,改不了,就这样吧。
只能绑定用户管脚,最后的配置图如下:
之后会创建约束文件。
之后,执行Run Implementation和Generate Bitstream。
9.是否还需要启用SDK
直接下板尝试。
没有反应啊。郁闷!Why????
时序逻辑没反应,先检查时钟信号和复位信号,在本实验系统中,PL端的时钟信号和复位信号均是从Zynq子系统中输出的,因此对工程做如下改变,将时钟信号和复位信号引到板上的LED灯处,用于显示和测量。
该工程Synthesis & Implementation后,仍然通过Hardware Manager下载后,现象为FCLK_CLK0驱动的LED灯常亮,使用示波器检测输出为持续高电平,FCLK_RESET0_N驱动的LED灯不亮。根据现象猜测,Zynq子系统中的时钟单元没有正常工作。
原因分析(个人观点):因为Zynq其实是一个ARM系统,其内部的硬核要想按照配置正常工作,应该是在初始化阶段对相应硬核的控制寄存器进行初始化配置,如果是使用Hardware Manager下载是无法操作这一步的。因此尝试使用SDK进行SoC工程配置。
10.是否还需要启用SDK
根据之前的学习,使用SDK有下列几步,不清楚的参看之间的学习笔记。
第一步:Vivado中Export Hardware...(其中需要Include Bitstream)
第二步:Vivado中Launch SDK
第三步:SDK中创建板载支持库函数,Board Support Package
第四步:创建用户工程,Application Project
第五步:编译
第六步:Program FPGA
下面对其中跟本实验有关的进行简单说明
(1)在第三步中,启用SDK可以看到内存映射信息,跟内存相关的一个是DDR,一个是Zynq系统内部的RAM,内存地址如下,根据标题分析,应该是Zynq子系统内部,对于CPU来说的地址设定。
个人猜测:从上图可以看出在Zynq内部,对于两个CPU Core,同一个硬核,操作地址是相同的,从而能够实现双核的数据交互。比如ps7_cortexa9_0能够直接对0x0010_0000的DDR读写数据,同样的ps7_cortexa9_1也可以对该地址的DDR直接读写数据。同时这将会带来一个问题就是同步。另外,还有个问题就是如果是双核运行,每个核都将有自己独立的代码段和栈空间(这两个空间是程序运行时系统使用的,用户不能强行进行读写操作),而堆空间是用户自行申请使用的内存空间,通常用来进行用户数据存储,也就是说堆空间才是双core可以公用的。而无论代码段、栈空间、堆空间的物理载体都是内存(RAM or DDR),因此c语言嵌入式程序对物理内存直接读写时,要避免对代码段和栈空间的地址进行操作。而对代码段和栈空间的配置应该是在编译过程中的link阶段配置的。具体配置接口如下:
这是配置界面,与传统ARM开发对应的配置文件如下所示,相应的详细原理参考传统ARM开发的相关知识。
(2)对Zynq子系统中各个硬核的初始化配置。
前面分析过,对于Zynq子系统,在不使用CPU Core的架构设计中,如果仅仅使用HardWare下载,Zynq子系统内部的外设硬核也是不能正常工作的。前面分析认为是没有进行初始化配置,在SDK软件中,打开ps7_init.tcl文件如下图所示,从名字可以看出,这是一个初始化的脚本配置文件,既然是tcl文件,应该就不需要c语言调用,而是直接配置,下面截图可以看出是对PLL硬核,Clock硬核、DDR硬核控制寄存器的初始化配置。
其中猜测:mask_write 0XF8000008 0x0000FFFF 0x0000DF0D
应该表示的是向0xF800_0008地址写入数据0x0000_ffff_0000_df0f(共计32bits)
在Run时,SDK软件会在配置“Run ps7_init”项对应的逻辑状态下自动调用该tcl文件完成初始化操作的。
(3)在第四步创建用户工程(Application Project)时,如果还按照之前的经验选择“Hello World”,将会显示如下信息:
提示信息为:目前硬件项目设计中没有Uart外设硬核,因此该示例不能创建,真丢本实验,仅有Peripheral Tests和Empty Application两项可以使用。经过验证,利用这两个模版都可以实现实验要求,这里只介绍Empty Application模版
使用Empty Application模版,创建的工程如下所示:
可以看出并没有任何c语言文件。
在src中创建一个c语言文件main.c(文件名可以随便起),其中自行输入一个main函数(必须是这个函数名),保存后,SDK会自动编译,生成一个debug文件和binaries项。
可以看出,这里main函数里什么都没有做。
【补充】对内存分配的进一步分析研究
双击Binaries下的elf文件,显示如下信息:
可以看出,编译完成之后,编译软件对嵌入式程序进行的内存划分。而且系统空间大小地址都分配在ddr的低地址区间,因本实验测试时,选用的DDR地址空间应以高地址空间为宜。
在详细分析elf文件中对内存的划分,重点看下图部分。
编译软件分析完毕后,得到的栈空间其实地址为0010_a030,栈空间大小为0000_3800(14K Bytes)。(0010_a030+0000_3800=0010_d830)
c语言嵌入式软件中,如果不是直接地址操作,本实验编译后的软件使用的数据空间为:0010_0000~0010_d830,空间大小为:54.046875K Bytes。
因此,本实验中对DDR的内存读写测试,测试的内存地址区间从0020_0000开始为宜。
另外前文分析时说过,板载DDR为512MB,但是在vivado生成的system.hdf文件中,定义的ps7_ddr_0的内存空间范围只有511MB,少了1MB,当时不知道这1MB时怎么被贪污的。
【个人猜测】但是再看elf文件,可以看到0000_0000开始的空间,是用来进行Debug调试的,因此被Vivado+SDK系统给扣下了,对用户不可见。
创建完用户逻辑后,配置Run As...项,用于下载程序。
其中,Run Configurations页面前面已经见过很多次,这次重点说明的地方已经框出:
其中“Reset Processor”仅仅时重新下载C语言嵌入式程序,download to processor
当选中“Reset Entire System”项,会先下载PL端的bit文件,然后再下载C语言嵌入式程序,download to processor。
具体说明如下所示。
选择reset processor项后,run成功,SDK Log窗口显示如下信息,可以看出,先执行ps_init.tcl脚本,配置PS的硬核,在运行ps7_post_config(具体工作内容还没整明白),然后复位ps7_cortexa9_0(是的c语言程序从main函数开始运行)。
在开发板上,程序下载成功后,如果按BTN6(PROG),相当于Program FPGA这一项需要重做。因此开发板不再有反应。
在开发板上,程序下载成功后,如果按BTN9(PS-RST),相当于zynq子系统复位,而非CPU(ps7_cortexa9_0)复位,此时需要重新执行Run ps7_init这一项。因此开发板不再有反应。
【结果】下载成功后,开发板反应如设计所示。实验结果表明:
当设置PL端的Master设备访问的地址为0x4020_0000(0x4000_0000为32b GP AXI Slave Port的地址 + 0x0020_0000为DDR内的地址)时,下板运行结果表明内存读写操作是失败的。
当设置PL端的Master设备访问的地址为0x0020_0000(为Zynq子系统内CPU对DDR访问的地址段)时,下板运行结果表明内存读写操作是成功的。
【结论】:
1. 只要SoC系统中,用到了Zynq子系统,无论逻辑上是否激活CPU,都需要使用SDK进行下载,原因时初始化Zynq子系统中硬核。
2. PL端对Zynq子系统中的寄存器进行操作,不需要考虑进行二次内存封装,直接使用Zynq内设定的地址即可。即整个芯片PL+PS是采用统一地址映射的。
编辑:黄飞
评论
查看更多