VIVADO通过mmi文件更新bit文件
背景:有一个MCU的工程,这个工程有很多HDL文件,由于某些原因需要联合使用synplify和vivado,即先通过synplify综合,之后将edf网表文件交给vivado进行p&r。
MCU的启动ROM的内容初始化是这样做的:
- 首先使用vivado的BRAM IP生成符合条件的IP
- 在HDL文件中添加生成的IP文件中的的stub文件以通过synplify综合
- vivado添加edf文件后会将这个ROM当作一个black box,添加第一步生成的IP后即可进行impl、p&r
最开始时使用的方法是通过COE文件初始化BRAM,由于工程很大,如果每次ROM的内容有变动的话都需要重新进行第一步、第三步,而第三步每次都需要重新p&r相当耗费时间。因此开始考虑能不能通过直接修改比特流来修改ROM的初始化内容,在参考了以下资料后发现是可以实现的:
干货:Vivado 直接修改RAM初始化文件,避免重新综合、实现的方法
Vivado中用HDL定义BRAM存储器并用updatemem合成bit文件
Vivado通过mmi文件更新bit文件
UpdateMEM User Guide (UG1580)
Vivado BRAM资源使用
差异之处
我使用的vivado版本为2019.1,与《Vivado中用HDL定义BRAM存储器并用updatemem合成bit文件》这篇文章的差异之处在于我在RAMB36的cell properties中看不到这块bram的对应的行列范围。通过阅读UG1580和《Vivado BRAM资源使用》我发现需要想办法不通过cell properties人工确定每块bram的对应的行列范围。
具体实例
生成BRAM
我的ROM IP如图所示,需要注意的是它虽然名字叫2kx64但是实际上是4kx64,由于位宽为64位,为了方便起见,在algorithm中原语选择为256x72,这样我的ROM是8个并联,不用串联凑位宽。
确定每个RAMB36的行列范围
经过我的测试,updatemem只能操作RAMB32(这个好像也就是不启用partial check的RAMB36),因此需要确保使用的BRAM资源全部均为36K BRAMs。
使用到的8个RAMB36如下图所示
可以发现每个init行对应4个64位数据,右边的数据为放在ROM中的启动程序
检查发现每个RAMB36单元通过下图方式输出64位,奇偶校验位不参与(奇偶校验位参与的情况能否使用updatemem未进行验证)
同时需要记录每个RAMB36的坐标以编写mmi文件
编写mmi文件
参考UG1580,经过计算每个RAMB36包含4096个字节,每个AddressSpace只有一个BitLane,编写mmi文件如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| <?xml version="1.0" encoding="UTF-8"?> <MemInfo Version="1" Minor="0">
<Processor Endianness="Little" InstPath="dummy"> <AddressSpace Name="axi_rom_0" Begin="0" End="4095"> <BusBlock> <BitLane MemType="RAMB32" Placement="X1Y19"> <DataWidth MSB="63" LSB="0"/> <AddressRange Begin="0" End="511"/> <Parity ON="false" NumBits="0"/> </BitLane> </BusBlock> </AddressSpace> <AddressSpace Name="axi_rom_1" Begin="4096" End="8191"> <BusBlock> <BitLane MemType="RAMB32" Placement="X1Y21"> <DataWidth MSB="63" LSB="0"/> <AddressRange Begin="0" End="511"/> <Parity ON="false" NumBits="0"/> </BitLane> </BusBlock> </AddressSpace> <AddressSpace Name="axi_rom_2" Begin="8192" End="12287"> <BusBlock> <BitLane MemType="RAMB32" Placement="X1Y22"> <DataWidth MSB="63" LSB="0"/> <AddressRange Begin="0" End="511"/> <Parity ON="false" NumBits="0"/> </BitLane> </BusBlock> </AddressSpace> <AddressSpace Name="axi_rom_3" Begin="12288" End="16383"> <BusBlock> <BitLane MemType="RAMB32" Placement="X1Y20"> <DataWidth MSB="63" LSB="0"/> <AddressRange Begin="0" End="511"/> <Parity ON="false" NumBits="0"/> </BitLane> </BusBlock> </AddressSpace> <AddressSpace Name="axi_rom_4" Begin="16384" End="20479"> <BusBlock> <BitLane MemType="RAMB32" Placement="X1Y23"> <DataWidth MSB="63" LSB="0"/> <AddressRange Begin="0" End="511"/> <Parity ON="false" NumBits="0"/> </BitLane> </BusBlock> </AddressSpace> <AddressSpace Name="axi_rom_5" Begin="20480" End="24575"> <BusBlock> <BitLane MemType="RAMB32" Placement="X2Y21"> <DataWidth MSB="63" LSB="0"/> <AddressRange Begin="0" End="511"/> <Parity ON="false" NumBits="0"/> </BitLane> </BusBlock> </AddressSpace> <AddressSpace Name="axi_rom_6" Begin="24576" End="28671"> <BusBlock> <BitLane MemType="RAMB32" Placement="X1Y24"> <DataWidth MSB="63" LSB="0"/> <AddressRange Begin="0" End="511"/> <Parity ON="false" NumBits="0"/> </BitLane> </BusBlock> </AddressSpace> <AddressSpace Name="axi_rom_7" Begin="28672" End="32767"> <BusBlock> <BitLane MemType="RAMB32" Placement="X2Y22"> <DataWidth MSB="63" LSB="0"/> <AddressRange Begin="0" End="511"/> <Parity ON="false" NumBits="0"/> </BitLane> </BusBlock> </AddressSpace> </Processor>
<Config> <Option Name="Part" Val="xc7vx485tffg1761-2"/> </Config>
</MemInfo>
|
准备mem文件
接下来就是mem文件了,注意每个RAMB36的对应一个开始地址指示符号,例如下图第一个RAMB36开始地址为0x0,第二个RAMB36开始地址为0x1000(即4096)。另外还需要注意数据的大小端,这里需要转换一下大小端。
updatemem
以上准备工作完成之后便可以通过updatemem -debug -meminfo xxx.mmi -data xxx.mem -bit xxx.bit -proc dummy -out new.bit -force
来生成新的bit文件了。如无意外,-debug选项会显示每个被重新初始化的RAMB36的数据。
最后将新生成的bit烧录,验证是否成功。