时间:2024-07-28 来源:网络搜集 关于我们 0
1)实验平台:正点原子达芬奇FPGA开发板
2) 摘自【正点原子】达芬奇之Microblaze 开发指南
3)购买链接: https://detail.tmall.com/item.htm?id=6243354965054)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_dafenqi.html5) 正点原子官方B站:https://space.bilibili.com/3946208906)对正点原子FPGA感兴趣的同学可以加群讨论:876744900在前面的实验中,都是不带DDR的系统结构,本次实验将学习带DDR的MicroBlaze系统搭建过程。
点击“Open Block Design”,在Diagram窗口中进行硬件设计。
首先打开MicroBlaze IP 核,点击该IP进行配置,这里启用存储于指令优化选项,如图 9.3.11所示:
图 9.3.11 MicroBlaze设置
继续点击“Next”至第3页Cache页,将地址设置如图 9.3.12所示:
图 9.3.12 地址设置
上处地址值必须与IP INITRATOR->Adress Editor->microblaze_0->的mig_7series_0数据和指令的地址值保持一致,如图 9.3.13所示:
图 9.3.13 MicroBlaze地址设置参考值
MicroBlaze IP核设置完毕。
双击Clocking Wizard时钟IP核,在Output Clocks页面,选中clk_out1和clk_out2,时钟频率分别设置为100和200,复位设为低电平,如图 9.3.14所示:图 9.3.14 设置输出时钟
clk_out1是给microblaze_0、uart、spi等模块提供时钟;clk_out2给mig_7series_0模块提供时钟。
接着添加MIG(Memory Interface Generalor) IP核,点击“+”按钮,输入“MIG”,双击Memory Interface Generator,添加IP核,如图 9.3.15所示:
图 9.3.15 添加MIG IP核
双击添加的MIG IP,进入配置页面,我们在达芬奇开发板FPGA设计部分,已经对MIG的配置操作有过详细讲解,这里我们简略说明一下配置需主要的配置步骤。
在Controller Options目录,DDR芯片的运行时钟设为“400M”,DDR3芯片型号选择“MT41K128M16XX-15E”,数据位宽这里设为“16”。如图 9.3.16所示:
图 9.3.16 时钟及物理芯片配置
在Memory Options目录, Input Clock Period我们选择“200M”。如图 9.3.17所示:
图 9.3.17 储存器配置
在FPGA Options目录,按照图 9.3.18所示进行配置。
图 9.3.18 系统时钟配置
“Pin/Bank Selection Mode”我们选择第二项,后面我们会使用.xdc文件。
图 9.3.19 Pin/Bank Selection Mode选项卡
在Pin Selection目录我们选择第二个选项“Read XDC/UCF”直接导入管脚分配文件,如图 9.3.20所示。
图 9.3.20 管脚分配导入选项卡
如下图所示,在工程目录下,我们已经为大家准备好了一个ddr3_rw_test.xdc文件,用户只要直接导入这个.xdc文件就可以完成ddr3的管脚分配。ddr3_rw_test.xdc文件的路径为G:/tis_pro/axi4_ddr_rw。
导入后我们点击“Validate”,此时会跳出对话框,表明已经验证通过,我们点击“OK”,此时“Next”变成可选,点击“Next”完成管脚分配。如图 9.3.21所示:
图 9.3.21 分配验证管脚
后续操作就不一一说明了,MIG IP设置完成后,将sys_rst与sys_rst_n相连,clk_out2与sys_clk_i、clk_ref_i相连,ext_reset_in与sys_rst_n相连,如图 9.3.22所示:
图 9.3.22 手动连线
连线完成后,点击“Run Connection Automation”,弹出提示页面后,选中所有信号,点击“OK”,如图 9.3.23所示:
图 9.3.23 自动连线
我们再把自定义的IP添加进来,添加完成后如图 9.3.24所示:
图 9.3.24 添加DDR3_TEST IP核
在图 9.3.24中,M_AXI是AXI-Full类型的主机接口,我们将通过这个接口对DDR3进行读写操作。DDR3_TEST IP核在检测到m_axi_init_axi_txn端口的上升沿后会启动读写过程,并将读出的数据与写入的数据作比较,比较完成后m_axi_txn_done输出高电平。另外,在比较完成后,m_axi_error信号会指示整个过程是否出错。如果在读写过程中出错,或者在比较的过程中发现读出的数据与写入的数据不一致,那么m_axi_error将会拉高。
添加完自定义IP核之后,双击该IP核对其进行配置,如图 9.3.25所示:
图 9.3.25 配置DDR_TEST IP核
在图 9.3.25中,我们将变量C M AXI TARGET SLAVE BASE ADDR的值修改为0x88000_0000(DDR3的基地
址为0x80000000),它位于DDR3存储器的地址空间,是DDR3_TEST IP核进行读写操作的起始地址。
点击“Run Connection Automation”,连接DDR3_TEST IP核。
接下来,我们还要添加Utility Vector Logic IP核。然后将其配置成 非门,位宽为1,作为反向器使用。这是因为我们需要使用按键来作为DDR_TEST IP核的启动信号,达芬奇开发板上的按键在按下的时候为低电平,因此我们通过添加一个反向器,将其修改为按下时输出高电平。
最后,添加一个AXI_Quad_SPI核,用来存储要固化的程序(若不需要固化,该IP核可不添加)。搜索“spi”并添加该IP核后如图 9.3.26:
图 9.3.26 AXI_Quad_SPI核
双击AXI_Quad_SPI核进行配置,Mode选择“Quad”,Slave Device选择“Micron”,FIFO Depth选择“256”,勾选“Enable STARTUP Primitive”。如图 9.3.27所示:
图 9.3.27 配置AXI_Quad_SPI核
点击“Run Connection Automation”,选中所有连线,点击“OK”,自动连接AXI_Quad_SPI核。将ext_spi_clk 与s_axi_aclk连接,管脚spi_rtl_0改名为“qspi_flash”。
图 9.3.28 最终布局图
另手动添加上图中4个橙色的外部接口,其中“key_init”用于连接达芬奇开发板上的按键,“error_flag”和“compare_done”两个接口用于连接开发板上的LED。
到这里我们的Block Design就设计完成了,在Diagram窗口空白处右击,然后选择“Validate Design”验证设计。验证完成后弹出对话框提示“Validation Successful”表明设计无误,点击“OK”确认。最后按快捷键“Ctrl + S”保存设计。
接下来在Source窗口中右键点击Block Design设计文件“system.bd”,然后依次执行“Generate Output Products”和“Create HDL Wrapper”。
添加约束文件:打开system_wrapper.xdc文件删除之前的约束文件,添加如下管脚约束:
create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
set_property PACKAGE_PIN R4 [get_ports sys_clk]set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]set_property IOSTANDARD LVCMOS33 [get_ports sys_rst_n]set_property IOSTANDARD LVCMOS33 [get_ports UART_rxd]set_property IOSTANDARD LVCMOS33 [get_ports UART_txd]set_property IOSTANDARD LVCMOS33 [get_ports {qspi_flash_ss_io[0]}]set_property IOSTANDARD LVCMOS33 [get_ports qspi_flash_io0_io]set_property IOSTANDARD LVCMOS33 [get_ports qspi_flash_io1_io]set_property IOSTANDARD LVCMOS33 [get_ports qspi_flash_io2_io]set_property IOSTANDARD LVCMOS33 [get_ports qspi_flash_io3_io]set_property IOSTANDARD LVCMOS33 [get_ports {key_init[0]}]set_property IOSTANDARD LVCMOS33 [get_ports compare_done]set_property IOSTANDARD LVCMOS33 [get_ports error_flag]set_property PACKAGE_PIN T19 [get_ports {qspi_flash_ss_io[0]}]set_property PACKAGE_PIN P22 [get_ports qspi_flash_io0_io]set_property PACKAGE_PIN R22 [get_ports qspi_flash_io1_io]set_property PACKAGE_PIN P21 [get_ports qspi_flash_io2_io]set_property PACKAGE_PIN R21 [get_ports qspi_flash_io3_io]set_property PACKAGE_PIN T1 [get_ports {key_init[0]}]set_property PACKAGE_PIN U5 [get_ports UART_rxd]set_property PACKAGE_PIN T6 [get_ports UART_txd]set_property PACKAGE_PIN U2 [get_ports sys_rst_n]set_property PACKAGE_PIN R2 [get_ports compare_done]set_property PACKAGE_PIN R3 [get_ports error_flag]set_property CFGBVS VCCO [current_design]set_property CONFIG_VOLTAGE 3.3 [current_design]set_property BITSTREAM.GENERAL.COMPRESS true [current_design]set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design]set_property BITSTREAM.CONFIG.SPI_FALL_EDGE Yes [current_design]管脚分配完成后按快捷键“Ctrl+S”保存管脚约束。
最后在左侧Flow Navigator导航栏中找到PROGRAM AND DEBUG,点击该选项中的“Generate Bitstream”,对设计进行综合、实现、并生成Bit(Bitstream)文件。
在生成Bitstream之后,在菜单栏中依次点击“File->Export->Export hardware”导出硬件,并在弹出的对话框中,勾选“Include bitstream”,在导出路径最后添加“/vitis”。如图 9.3.29所示:
图 9.3.29 设置硬件导出地址
然后在菜单栏依次点击“Tools->Launch Vitis”,启动vitis软件。
9.4软件设计
在Vitis软件中新建一个BSP工程和一个空的应用工程,应用工程名为“axi4_ddr_rw”。然后为应用工程新建一个源文件“main.c”,我们在新建的main.c文件中输入本次实验的代码。代码如下所示:
1 #include <stdio.h>2 #include "xil_cache.h"3 #include "xil_printf.h"4 #include "xil_io.h"5 6 int main()7 {8 int i;9 char c;10 11 Xil_DCacheDisable();12 print("AXI4 DDR TEST!\n\r");13 14 while(1){15 scanf("%c",&c);16 if(c==c){17 printf("start\n\r");18 for(i=0;i<4096;i=i+4){19 printf("%d is %d\n",i,(int)Xil_In32(0x88000000+i));20 }21 }22 }23 24 return 0;25 }可以看出,我们的软件程序特别简单。在代码的第14行至22行,通过一个while(1)死循环,连续判断用户输入的字符。当输入字符“c”时,程序通过一个for循环开始从地址0x8800_0000读取DDR3存储器中的数据,读取的存储空间大小为4KB。需要注意的是,变量i每次累加4,这是因为我们调用了函数Xil_In32( )来读取内存数据,每次读取32bit。而内存地址是以字节(1字节==8bit)为单位的,那么操作完成后地址应该累加4。
可以看出,我们在软件中读取的内存地址与硬件设计过程中DDR_TEST IP核所写入的地址是一致的。我们将软件读出的数据通过串口打印出来,与DDR_TEST IP核写入的数据进行对比,即可验证我们通过AXI4接口对DDR进行的读写操作是否成功。
另外,在代码的第11行,我们通过调用函数Xil_DCacheDisable( )来关闭数据缓存(Data Cache),以避免从缓存中读取数据。这是因为在对同一地址进行读操作时,读出的有可能是Data Cache中缓存的数据,而不是DDR中真正的数据。
程序设计完成后,按快捷键Ctrl+S保存main.c文件,接着编译工程。编译完成后控制台(Console)中会出现提示信息“Build Finished”,同时在应用工程的Binaries目录下可以看到生成的elf文件。
9.5下载验证和固化
下载验证
首先我们将下载器与达芬奇开发板上的JTAG接口连接,下载器另外一端与电脑连接。然后使用USB连接线将开发板左侧的USB_UART接口与电脑连接,用于串口通信。最后连接开发板的电源,并打开电源开关。
在Vitis软件中设置并连接串口。然后下载本次实验硬件设计过程中所生成的bit文件。最后下载软件程序,下载完成后,在右下方的Terminal中可以看到应用程序打印的信息“AXI4 DDR TEST!”。
图 9.5.1 程序下载完成
然后点击Terminal窗口空白处,Terminal窗口就会出现一个黑色的光标,此时键盘输入字符“c”。程序会打印从DDR3中读出的数据,如图 9.5.2所示:
图 9.5.2 第一次从DDR3中读出的数据
如图 9.5.2所示,串口打印出了DDR3存储空间中从地址0x8800_0000开始的1024个数据,每个数据占4个字节。从图中可以看出,开发板上电后,在没对该地址空间进行写操作之前,DDR3中的数据是随机的。
按下开发板上的按键KEY0然后释放,该动作会启动DDR3_TEST IP核对DDR3的读写操作。然后开发板上的LED0会点亮,表示读写操作完成。如图 9.5.3所示:
图 9.5.3 达芬奇开发板实物图
在图 9.5.3中,如果LED1也点亮了,这表明在对DDR3进行读写操作过程中出现了错误。但这个错误指示灯点亮的原因并不唯一,通过分析DDR3_TEST IP核的源码可以看出,在AXI4通信过程中写响应出错、读响应出错、以及读出与写入的数据不一致均会导致错误指示灯点亮。
因此,要判断我们自定义的IP核究竟有没有成功地向DDR3指定地址中写入数据,还是要通过查看该内存空间中的数据来判断。
在Terminal窗口中重新输入字符“c”并发送,程序会再次打印从DDR3中读出的数据,如下图所示:
图 9.5.4 第二次从DDR3中读出的数据
从图 9.5.4中可以看出,从DDR3中指定的4KB存储空间中读出的数据依次为1到1024,与DDR3_TEST IP核
写入的数据一致,这时再按下KEY0然后释放,只有LED0亮。说明本次实验在达芬奇开发板上面下载验证成功。
程序固化
在《AXI GPIO控制LED实验》中,固化时,将硬件设计生成的bit流文件和软件应用程序合并成一个download.bit文件,然后再将download.bit文件写入QSPI Flash芯片中。Flash芯片内的数据在断电之后不会丢失的,再次上电后,QSPI Flash内的数据被写入BRAM中,程序就会再次执行。
BRAM为FPGA内部的资源,程序写入后无需引导即可运行,然而,有些程序是需要引导的,如本章实验DDR3读写程序,该程序占用空间资源较大,不宜在BRAM内运行,所以将该程序放在DDR RAM中运行。DDR RAM是FPGA的外部资源而非内部资源,所以程序想要在DDR RAM中运行程序就必须先进行引导,本章实验采用Bootloader引导程序对其进行引导。
Bootloader:简单地说,Bootloader就是在应用程序运行之前,运行的一段小程序。系统在 上电或复位时通常都从地址0x00000000开始执行,这个地址安排的通常就是系统的Bootloader程序。通过这段小程序,我们可以初始化硬件设备,建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为调用应用程序准备好正确的环境。
本章实验中,先将硬件配置文件system_wrapper.bit及Bootloader引导程序文件bootloader.elf合并为download.bit文件,然后将download.bit文件和用户的应用程序axi4_ddr_rw.elf文件固化到QSPI Flash中。板子断电再次上电后进行加载,FPGA自动从QSPI Flash中读取download.bit文件,将硬件配置和引导程序信息配置到片内BRAM中。加载后进行引导,FPGA内部逻辑启动,运行Bootloader引导程序读取QSPI Flash中的用户应用程序,并写到外部DDR3的相应位置。引导完成后,Bootloader程序切换指针到用户应用程序开始运行的指定位置,在外部的DDR3中开始执行用户的应用程序。
接下来我们就开始采用Bootloader的引导方式进行程序的固化。
设置板级支持包信息
按图 9.5.5指示依次点击,最后点击“Modify BSP Setting”。
图 9.5.5 设置板级支持包信息
选中“xilisf”,即Flash库(如果不选则无法固化QSPI Flash),如图 9.5.6所示:
图 9.5.6 选中xilisf
点击左上角的“xilisf”,在serial_flash_family将原先的值“1”改为“5”(板载Flash芯片MT41K128M16XX-15E属于镁光,对应值为5)。如图 9.5.7所示:
图 9.5.7 选择串行flash对应值
右键system_wraper,点击“Build Project”,编译工程。
建立BootLoader工程
新建工程,工程名设置为“bootloader”,点击“Next”。如图 9.5.8所示:
图 9.5.8 新建工程bootloader
选择当前的硬件平台,点击“Next”。如图 9.5.9所示:
图 9.5.9 选择硬件平台
继续点击“Next”,选择模板“SERC SPI Bootloader”,点击“Finish”。如图 9.5.10所示:
图 9.5.10 选择SERC SPI Bootloader
点击修改blconfig.h文件,将MicroBlaze软件文件在Flash中的加载点修改为“0x00800000”:Flash前面的8MB空间用来存储Bitstream文件,从地址0x00800000开始保存软件,如图 9.5.11所示。(这里设置加载点的值可以根据比特流文件的大小进行调整)。
图 9.5.11 修改软件存储位置
右键bootloader_system,点击“Build Project”,编译工程,生成引导程序bootloader.elf文件。
生成download.bit文件
点击菜单栏“Xilinx”,选择“Program FPGA”,将硬件设计生成的bit文件和bootloader.elf文件合成为一个download.bit文件,用于烧录到QSPI Flash芯片中。如图 9.5.12所示:
图 9.5.12 Program FPGA
点击“microblaze_0”,选择编译生成的bootloader.elf文件,路径G:\vitis_pro\axi4_ddr_rw\vitis\bootloader\Debug。点击“Program”,生成download.bit文件。
图 9.5.13 加载bootloader.elf
download.bit文件路径为G:\vitis_pro\axi4_ddr_rw\vitis\bootloader\_ide\bitstream\download.bit。
生成download.bit文件后,点击菜单栏Xilinx,选择“Program Flash”。如图 9.5.14所示:
图 9.5.14 Program Flash
在Project Type栏,选中Application。在Image File栏,点击“Browse…”,选择上一步生成的download.bit文件,选择器件“mt25ql128-spi_x1_x2_x4”,勾选“Verify after flash”,点击“Program”。如图 9.5.15所示:
图 9.5.15 烧录download.bit文件
烧录download.bit成功,现象如图 9.5.16所示:
图 9.5.16 烧录download.bit成功
烧写应用程序
再次点击菜单栏“Xilinx”,选择“Program Flash”。
将用户的应用程序文件axi4_ddr_rw.elf烧写到地址“0x00800000”,elf路径G:\vitis_pro\axi4_ddr_rw\vitis\axi4_ddr_rw\Debug。选择器件“mt25ql128-spi_x1_x2_x4”,勾选“Convert ELF to bootloadable SREC format and program”和“Verify after flash”,点击“Program”。如图 9.5.17所示:
图 9.5.17 烧录axi4_ddr_rw.elf
烧录应用程序成功,现象如图 9.5.18所示:
图 9.5.18 烧录应用程序成功
关掉电源,拔出JTAG连接线,重新上电,板子自动运行。
重新连接Terminal,等待引导完毕,引导过程耗费的时间与用户程序的大小成正比(优化技巧详见文章末尾)。程序引导完毕,如下图所示:
图 9.5.19 用户程序引导完成
按下按键,LED0亮;输入字符“c”,Terminal中会打印值1到1024;说明本实验的程序固化成功,如下图所示:
图 9.5.20 LED0亮
优化技巧:如果用户程序比较大,图 9.5.19中的引导时间会相应变长,我们可以使用“//”号屏蔽VERBOSE宏定义,省去不必要的打印信息,进而缩短引导时间。如下图所示:
图 9.5.21 屏蔽宏