最近被這么一個工作折騰了好幾天:一個非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的回復。
評論
查看更多