最近被这么一个工作折腾了好几天:一个非IPI的Vivado工程(需要全部Verilog手写),在Artix-7 FPGA中实现了一个ARM Cortex-M0 DesignStart软核(老版,没有debug模块),存放ARM程序的存储器是实现在FPGA片上RAM上的;ARM程序用Keil MDK编写,我希望在测试这个程序时,不用每次都重新综合一遍FPGA。
总体来说这个需求是:
Vivado;
全部HDL,非IPI;
无处理器(在Vivado看来M0 DesignStart不是处理器);
存储器放在BRAM上;
替换Bitstream中的BRAM初始化,而不用重新综合整个逻辑。
这件事看上去肯定是能做到的嘛。然而Vivado要用新的updatemem命令来替换以前ISE中的data2mem,我发现在无处理器的工程上,相关资料比较少……
这里总结一下整个实现过程。使用环境是Vivado 2015.2。
先把Vivado多线程开了……
参考资料:
[Xilinx AR# 53090] Vivado - Is it possible to run Tcl commands during initialization of the Vivado or PlanAhead tools?
在Vivado安装文件夹Vivadoversionscripts中新建一个init.tcl,里面写
set_param general.maxThreads 8
这样下次运行Vivado是自动开启8线程,综合实现比较快……
将寄存器定义在BRAM上
参考资料:?
[Xilinx AR# 54778] Design Assistant for Vivado Synthesis - Help with Synthesis HDL Attribute Support - keep, keep_hierarchy, ram_style, rom_style
这个页面的附件中给出了ram_style属性的例子,以verilog为例:
module ram_inf_64x1d_2 (a, dpra, clk, din, we, spo, dpo);
parameter ADDRESSWIDTH = 6;
parameter BITWIDTH = 1;
parameter DEPTH = 34;
input clk, din, we;
input [ADDRESSWIDTH-1:0] a, dpra;
output spo, dpo;
(* ram_style = "block" *)
reg [BITWIDTH-1:0] ram [DEPTH-1:0];
reg [ADDRESSWIDTH-1:0] read_dpra;
reg [ADDRESSWIDTH-1:0] read_a;
always @(posedge clk) begin
if (we) begin
ram [a] <= din;
end
read_a <= a;
read_dpra <= dpra;
end
assign spo = ram [read_a];
assign dpo = ram [read_dpra];
endmodule
上面代码展示了如何将寄存器数组reg ram强制定义在Block RAM上,并展示了两种访问方式:
单口访问:地址a、输入数据din、输出数据spo;
输入、输出分别访问:输入地址a、输入数据din、输出地址dpra、输出数据dpo。
实际使用中可根据需要选择保留其中一组引脚。
综合后可检查Project Summary和warning看是否成功综合成BRAM。建议这段module直接使用,不做逻辑上的修改。
查看BRAM模块的实现情况
参考资料:?
[Xilinx AR# 59259] 2013.4 - Vivado IPI - write_bmm Support with non-processor Based Designs?
这个帖子是针对bmm的,但其中提到了如何查看实现出的BRAM模块的位置,以及怎样知道每个模块分配的行、列地址范围。
把工程Implement一下,然后打开Implemented Design。
在打开的Implemented Design里面Ctrl+F搜索所有的BMEM:
搜索结果长这样:
每一行就是综合出的一个BRAM模块。选中其中一行,在它的属性里可以看到:
其中比较重要的是模块的名字(比如xxxxx/ram_reg_0)、位置(比如X1Y7)、行和列的地址范围([0:16383]和[0:1])。
为BRAM cell添加bmm属性
参考资料:?
[Xilinx论坛] A method to fix poor combination LBM of IPI Microblaze design?
我们需要做的是在Vivado工程里添加一个xdc约束文件,包含类似下面的内容:
create_property bmm_info_memory_device cell -type string
set_property bmm_info_memory_device {[ 0: 1][0:16383]} [get_cells u_block_ram/ram_reg_0]
set_property bmm_info_memory_device {[ 2: 3][0:16383]} [get_cells u_block_ram/ram_reg_1]
set_property bmm_info_memory_device {[ 4: 5][0:16383]} [get_cells u_block_ram/ram_reg_2]
set_property bmm_info_memory_device {[ 6: 7][0:16383]} [get_cells u_block_ram/ram_reg_3]
# 后面还有好几个,最后是ram_reg_15
除了第一行定义属性之外,后面每一行都对应上面搜索到的一个BRAM模块(当然不需要初始化的BRAM就不用写了)。
这里有两个字段需要根据实际工程来改写:一个是类似[ 0: 1][0:16383]这样的地址范围,另一个是u_block_ram/ram_reg_0这样的cell名字。其他都是不用动的。
我的工程里用到了64KBytes的RAM,总共使用了16个RAMB36模块,所以我的xdc文件中有16行,最后到{[30:31][0:16383]} [get_cells u_block_ram/ram_reg_15]。
添加xdc文件之后把工程再次综合实现一遍。
添加mmi文件
参考资料:
[Xilinx UG898] Vivado Design Suite User Guide: Embedded Processor Hardware Design?
[Xilinx AR# 63041] 2015.1 - Vivado IP Integrator - write_mem_info does not create MMI file?
上面一个文档里有一章专门讲mmi文件和updatemem命令。
下面帖子里描述了如何在有Memory Controller IP的Block Design中生成mmi文件,并附了一个tcl脚本。虽然我的系统中既没有Memory Controller、也没有Block Design,但资料还是有很大的参考意义。
实际上我把这个例子做了一遍,然后改写了它生成的mmi。
mmi文件大概是这个模样:
又是XML,现在不写几个XML都不好意思说自己用过Vivado……
这段代码主要需要修改的是:
里面:Name是自己随便起的;0和65535是分别是最小和最大地址,这里面地址的单位是字节,总共64KBytes,与总线是32位无关;另外这个地址应该是可以不从0起始的。
每一个里面:MSB、LSB、Begin、End这些和上面xdc约束文件中的每一项是对应的,Placement根据Implemented Design中搜索的结果来定。
里面:根据自己的FPGA型号修改,这个不用多说。
P.S. 在以前做bmm文件的时候,像Placement这种值是可以综合器自动帮助填写的,暂时还没发现如何在自定义bram时在mmi文件上实现类似功能。
合成Bitstream
参考资料:?
[Xilinx AR# 63041] 2015.1 - Vivado IP Integrator - write_mem_info does not create MMI file?
首先生成要往BRAM里烧写的数据,mem或elf格式。我这里是Keil MDK编译的可执行文件,直接把axf扩展名改成elf就能用。
把mmi、elf文件都放在Vivado工程的.runsimpl_1文件夹下,在Vivado中运行tcl命令:
cd {C:
on_processor_mmiproject_1.runsimpl_1}
updatemem -force --meminfo mmi文件名.mmi --data elf文件名.elf --bit 原始的bit文件.bit --proc dummy --out download.bit
其中C:
on_processor_mmiproject_1.runsimpl_1改成自己的工程文件夹。
updatemem后面跟着的是一行命令,比较长。
祈祷没有错误吧,然后可以看到impl_1文件夹中多了一个download.bit文件。
这一段可以看到Vivado重新launch_runs了write_bitstream,生成了一个新的bit文件,但并没有再次综合实现。
最后用Hardware Manager把Bitstream下载到FPGA中。
如何更新数据
比如说我用Keil MDK重新编译出了一个elf,那么只需要再次运行
updatemem -force --meminfo xxx.mmi --data yyy.elf --bit zzz.bit --proc dummy --out download.bit
就可以得到合成后的Bitstream,不需要再次综合。
总结
编写HDL文件,将寄存器强制定义在BRAM上;
综合实现一遍;
在xdc约束文件中添加bmm属性;
编写mmi文件;
运行updatemem合成Bitstream。
关于data2mem
这里记录一下在查找资料过程中查到的利用bmm合成bit的资料,待之后尝试。
[Xilinx UG658] Data2MEM User Guide?
Xilinx的data2mem使用手册,支持到ISE 14.7。
[Xilinx Answer 46945] Data2Mem Usage and Debugging Guide?
Xilinx整理的Data2Mem相关问答,相当于上面手册的补充说明,更实用一些的内容。
Using Data2Mem in a Non-EDK design?
Xilinx的另一篇文档,内容如其名。
Loading BRAM data?
这个wiki中作者记录了用data2mem合成AVR核的存储器数据的各种方法,使用的环境是ISE 12.4。
spartan3 picoblaze how to make .bmm file work?
这个帖子下面Walter Dvorak的回复提到了ISE可以自动生成bmm文件中的PLACED字段,使用的环境是ISE9.1.3。
Using Xilinx Data2MEM to Patch Block RAMs?
这个帖子把生成bmm到使用data2mem的过程总结的比较完整,其中也提到了上面那个Walter Dvorak的回复。
评论
查看更多