Vivado通过mmi文件更新bit文件

在做MCU的FPGA仿真平台的时候,如果需要验证ROM的功能,那么我们就会碰到直接更新ROM程序的问题,我们知道FPGA中ROM一般是通过block RAM实现的,因为芯片设计的时候ROM是只读的,所以在下载ROM的时候就不能像RAM程序那样通过下载器下载。所以这里就要用到一种新的ROM程序的更新方式。

生成RAM报表

我们知道FPGA中的RAM都是有固定的cell的,我们在更新ROM(实际对应FPGA内部block RAM)的时候,只需要知道ROM在FPGA内部的位置,这样就可以通过updatemem命令实现bit文件的更新,这个位置信息可以通过一个mmi文件描述。在有MicroBlade核心的设计中,Vivado是可以直接产生我们需要的mmi文件的,这个命令是write_mem_mmi,不过因为我们相当于自己定义的设计,所以vivado直接就不支持,没有办法,我们只好自己实现一个write_mmi了,具体的实现过程如下:

# Sort bram cells by ramloop index
proc byramloop {a b} {                                                                                                   
        regexp {ramloop\[(\d+)\]} $a m num_a ;                                                                           
        regexp {ramloop\[(\d+)\]} $b m num_b ;                                                                           
        #puts [expr $num_a - $num_b]                                                                                     
        return [expr $num_a - $num_b]
}
# Generate mmi file
# cell_name is key word of rom module
# max_addr is ROM size in words
proc write_mmi {cell_name  max_addr} {
        set proj [current_project]
        set filename "${cell_name}.mmi"
        set fileout [open $filename "w"]
        set brams [get_cells -hier -filter [list REF_NAME =~ RAMB* && NAME =~ "*${cell_name}*"]]
        set brams [lsort -command byramloop $brams]
        puts $fileout "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
        puts $fileout "<MemInfo Version=\"1\" Minor=\"0\">"
        puts $fileout "  <Processor Endianness=\"Little\" InstPath=\"dummy\">"
        puts $fileout "  <AddressSpace Name=\"bram\" Begin=\"0\" End=\"${max_addr}\">"
        foreach cell $brams {
                puts $cell
                set loc [get_property LOC $cell]
                puts $fileout "    <!-- $cell -->"
                puts $fileout "    <BusBlock>"
                puts $fileout "      <BitLane MemType=\"RAMB32\" Placement=\"[string range $loc 7 end]\">"
                puts $fileout "        <DataWidth LSB=\"0\" MSB=\"31\"/>"
                puts $fileout "        <AddressRange Begin=\"0\" End=\"1023\"/>"
                puts $fileout "        <Parity NumBits=\"0\" ON=\"false\"/>"
                puts $fileout "      </BitLane>"
                puts $fileout "    </BusBlock>"
                puts $fileout ""
}
puts $fileout "  </AddressSpace>"
puts $fileout "</Processor>"

puts $fileout "<Config>"
puts $fileout "  <Option Name=\"Part\" Val=\"[get_property PART [current_project ]]\"/>"
puts $fileout "</Config>"
puts $fileout "</MemInfo>"
puts "MMI file ($filename) created successfully."
close $fileout
}

上面脚本基本的实现流程:

  1. 根据指定的关键字查找相应的bram列表
  2. 对列表进行排序
  3. 依次输出bram的位置信息,位置通过get_property LOC $cell获取,不过获取结果会有一个前缀,所以要用string语句将不用的前缀去掉,否则生成的mmi文件将不能正常工作
  4. PR之后就可以通过这个语句生成mmi文件了

注意:上述脚本对于ROM的实现cell是有要求的,这里要求直接设定cell的实现方式为256x32的单元,这个是必须要的,否则mmi文件将无法工作。

更新bit文件

有了RAM分布的报表之后,我们就可以通过这个mmi文件对bit文件进行更新,并且还可以读取当前bit文件中的内容,更新文件的命令如下:

updatemem -debug -meminfo rom.mmi -data flash.elf -bit FB1_uA.bit -proc dummy -out new.bit -force 

可以看到这个命令可以直接读取elf文件,这个也是一般gcc工具直接生成的一个标准文件格式。通过-debug参数,我们可以直接读取当前FB1_uA.bit文件中的ROM内容,这样可以方便我们检查mmi文件是否正确,确认没有问题之后,工具会生成新的new.bit,如果需要.cfg文件,可以直接将FB1_uA.bit对应的FB1_uA.cfg拷贝为new.cfg使用。现在我们就可以将新的bit下载到FPGA运行了。

自定义数据文件

上面例子中,我们用到的是flash.elf这样标准的gcc生成文件,但是某些场合,我们并没有这样的文件,或者要对数据进行修改,对于这种情况,我们就要用到另外一种mem文件。

比如MCU中,ROM的首地址通常并不是0地址,这样我们生成的elf文件是有一个地址偏移的,而对于FPGA的RAM,地址都应该是从0地址开始的,对于这种情况,我们就需要手动进行修改。updatemem支持直接使用mem格式的数据文件,这种数据文件类似于我们平常用的hex格式文件,这种文件可以通过如下的命令经elf文件生成。

data2mem -bd flash_fw.elf -d -o m flash.mem

对应的文件内容:

// MEM file.                                                                                
//                                                                                          
// Release 14.6 - Data2MEM P_INT.20180726, build 3.0.10 Apr 3, 2013                         
// Copyright (c) 1995-2019 Xilinx, Inc.  All rights reserved.                               
//                                                                                          
// Command: data2mem -bd flash_fw.elf -d -o m flash.mem                                     
//                                                                                          
//     flash_fw.elf                                                                         


// Program header record #0, Size = 0x4F0, at 0x10000000 to 0x100004EF.

@10000000
    93 00 00 00 93 01 00 00 13 02 00 00 93 02 00 00 13 03 00 00 93 03 00 00 13 04 00 00 93 04 00 00
    13 05 00 00 93 05 00 00 13 06 00 00 93 06 00 00 13 07 00 00 93 07 00 00 13 08 00 00 93 08 00 00
    13 09 00 00 93 09 00 00 13 0A 00 00 93 0A 00 00 13 0B 00 00 93 0B 00 00 13 0C 00 00 93 0C 00 00
    13 0D 00 00 93 0D 00 00 13 0E 00 00 93 0E 00 00 13 0F 00 00 93 0F 00 00 17 05 00 00 13 05 85 47
....

我们可以看到上面是有一个便宜的,这个时候我们就需要将这个偏移改成0,然后更新bit:

updatemem -debug -meminfo rom.mmi -data flash.mem -bit FB1_uA.bit -proc dummy -out new.bit -force

多块ROM更新

对于多块ROM的更新,我们可以将ROM分别设置不同的关键字(模块例化成不同的名字),然后根据关键字多次运行write_mmi生成相对应的mmi文件。

通过dcp生成mmi文件

dcp全称是Design Checkpoint, 相当于Vivado在不同阶段的一个快照,比如place之后,Route之后,有了dcp之后,我们可以方便的通过dcp导出我们需要的各种信息,比如RAM的分布信息(mmi)文件,后仿Verilog文件,sfa文件等等,当然也可以重新生成bit等等。有了dcp之后,这些操作都不需要重新进行place和route,这样可以大大减少设计时间。

dcp文件的使用也比较简单,直接通过GUI打开dcp文件之后就可以当成一个新的工程使用了,但是命令行模式的使用,我现在还没搞定。。。


本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

相关文章

发表新评论