一,摘要
本文主要用於記錄作者本人實現ZYNQ動態修改LUT內容過程中主要參考的資料,實現的步驟,遇到的問題等。主要參考的文章鏈接如下:https://cloud.tencent.com/developer/article/1528881,其中該工作大部分的原理部分該篇博客都有介紹,本篇博客不再贅述。
在此我主要介紹一下使用Vivado進行部分動態重構系統的開發流程,AXI_HWICAP IP基本的使用方法,以及通過ARM部分實現LUT內容動態修改的程序流程(此部分見下篇博客)。
二,系統框圖
系統結構主要如上圖,其中LUT的讀寫操作由PS端的程序控制,通過PC端的串口助手可以觀察讀取的配置幀數據,設置要修改的LUT位置以及修改後的LUT中的新數據。
三,PL工程的創建,綜合,佈局佈線。
考慮到調試過程中可能要多次設置目標LUT的初始值來搞清楚bitstream中數據與LUT中數據的映射關係,爲了提高該系統的編譯調試效率,本文針對目標操作的LUT採取局部動態重構的設計方法。實際應用中不採用部分重構設計流程也可以對LUT進行動態讀寫。
以下是本次設計使用Vivado進行部分重構系統開發的整體流程:
- 使用Vivado創建整個設計,將部分重構模塊保留爲空模塊。綜合工程生成靜態設計的.dcp文件(如top_syn_static.dcp)。
- 綜合部分重構模塊的源文件,生成對應的.dcp文件(如:pr_lut_syn.dcp)。
- 打開第一步生成的top_syn_static.dcp,讀入第二步生成的pr_lut_syn.dcp,設置重構屬性,保存設計點。
- 爲重構模塊指定重構區域,讀入約束文件。
- 優化,佈局,佈線,保存設計點。
- 生成bitstream文件,及ila調試相關文件。
- 將重構模塊清除保留爲黑盒,將其他部分在佈局佈線後的級別上保存其設計點。
- 打開上一步保存的設計點,讀入新的重構模塊,優化,佈局佈線,保存設計點,生成bitstream等。
- 使用pr_verify指令對生成的bitstream進行驗證。
第一步,創建設計。
可直接在給出的工程目錄下面執行腳本自動創建,綜合,以及硬件的導出。在工程目錄下執行以下代碼(windows下需要在Vivado的命令行下操作):
source ./tcl/step1_create_project.tcl
具體工程文件結構如下圖所示:
其中原理圖部分如下:
這一步我們要用到的文件爲.\vivado\lut_modify.runs\synth_1\top.dcp,我們需要將此文件複製到.\checkpoints\synth\static\路徑下,便於後續操作。
當然也可以自己手動來創建工程,編寫相關文件。
第二步,綜合重構模塊
直接執行腳本文件即可,腳本代碼如下:
read_verilog ./src/rms/pr_lut1/pr_lut.v
synth_design -mode out_of_context -flatten_hierarchy rebuilt -top pr_lut -part xc7z020clg400-1
write_checkpoint ./checkpoints/synth/rms/pr_lut1.dcp
close_design
close_project
以下是重構模塊的verilog代碼,該模塊例化一個內部的固定位置的LUT,便於我們以後尋址更改其內部內容:
`timescale 1ns/1ps
//*******************
//DEFINE MODULE PORT
//*******************
module pr_lut (
input clk ,
input rst ,
input [5:0] din ,
output reg dout
) ;
//*********************
//INNER SIGNAL DECLARATION
//*********************
//REGS
reg [5:0] din_ff ;
//WIRES
wire dout_pre ;
//*********************
//MAIN CORE
//*********************
always @(posedge clk or posedge rst) begin
if (rst == 1'b1) begin
din_ff <= 'd0 ;
end
else begin
din_ff <= din ;
end
end
always @(posedge clk or posedge rst) begin
if (rst == 1'b1) begin
dout <= 'd0 ;
end
else begin
dout <= dout_pre ;
end
end
// 0001 0010 0011
(* DONT_TOUCH= "TRUE" *) (*BEL="D6LUT",LOC="SLICE_X57Y53"*) LUT6 #(
.INIT(64'h1234123412341234) // Specify LUT Contents
) LUT6_inst_D_right (
.O(dout_pre), // LUT general output
.I0(din_ff[0]), // LUT input
.I1(din_ff[1]), // LUT input
.I2(din_ff[2]), // LUT input
.I3(din_ff[3]), // LUT input
.I4(din_ff[4]), // LUT input
.I5(din_ff[5]) // LUT input
);
//*********************
endmodule
該步操作主要生成的文件如下圖,後面可用不同lut初始值的rm來創建完整設計,方便在bitstream文件中找到目標lut配置數據的位置及內容。便於分析bitstream配置數據同實際LUT中數據的映射關係:
第三步,打開設計點,讀入重構模塊,劃分重構區域
執行以下腳本
open_checkpoint ./checkpoints/synth/static/top.dcp
read_checkpoint -cell pr_lut_0 ./checkpoints/synth/rms/pr_lut1.dcp
set_property HD.RECONFIGURABLE 1 [get_cells pr_lut_0]
write_checkpoint ./checkpoints/synth/top_link_lut.dcp -force
#劃分重構區域。
startgroup
create_pblock pblock_pr_lut_0
resize_pblock pblock_pr_lut_0 -add {SLICE_X54Y50:SLICE_X59Y59 RAMB18_X3Y20:RAMB18_X3Y23 RAMB36_X3Y10:RAMB36_X3Y11}
add_cells_to_pblock pblock_pr_lut_0 [get_cells [list pr_lut_0]] -clear_locs
endgroup
其中爲重構模塊劃分重構區域手動操作如下圖,然後在layout上面繪製相應的重構區域即可,注意:此例中由於指定了LUT的位置信息,所以重構區域必須覆蓋到該LUT的物理區域。
此例中劃分的重構區域如下:
第四步,優化,佈局佈線,保存設計點,生成.bit及.rbt文件。
直接執行以下命令:
opt_design
place_design
route_design
write_checkpoint -force ./checkpoints/implement/top_route_design.dcp
write_checkpoint -force -cell pr_lut_0 ./checkpoints/implement/pr_lut_instance_route_design.dcp
write_bitstream -raw_bitfile ./bitstreams/top1 -force
write_debug_probes -force ./bitstreams/top
執行上述命令後可以觀察到器件已經完成佈局佈線,此時可生成對應的bitstream文件及部分bitstream文件。我們可通過生成的top.rbt來分析top.bit的結構,(top.rbt爲top.bit的二進制asii格式表示,可通過python腳本來將rbt文件轉換爲16進制格式表示數據並保存爲txt文件,後續我們從器件中讀配置數據的時候要與之進行對比,來驗證讀操作的正確性)
第五步,保存靜態設計。
腳本命令如下:
update_design -cell pr_lut_0 -black_box
lock_design -level routing
write_checkpoint -force ./checkpoints/routed/static_route_design.dcp
update_design -buffer_ports -cell pr_lut_0
place_design
route_design
write_checkpoint -force ./checkpoints/implement/top_route_design.dcp
close_project
第六步,讀入新的重構模塊,生成的完整bit文件及部分bit文件。
腳本和上面操作多有重複,這裏不再給出。
本操作的意義在於利用前面保存過的靜態設計,載入新的重構模塊進行綜合,佈局佈線,大大減少了整個工程綜合,佈局佈線的時間成本。
針對此次設計任務需求,需要多次更改目標LUT的初始值,採用此種設計流程能夠大大減少後期bitstream文件的生成時間,而不必牽一髮而動全身,每次都浪費這麼長的時間來進行類似的操作。
第七步,驗證兩個設計是否兼容。
腳本命令如下:
pr_verify -initial ./checkpoints/implement/top_route_design.dcp -additional {./checkpoints/implement/top_route_design2.dcp }
close_project
驗證結果如下圖
至此整個重構系統基於Vivado的脫離上下文的設計流程結束。
四、AXI_HWICAP相關信息
相關寄存器如下表:
讀寫配置:
- 將bitstream寫進寫FIFO寄存器進行配置。
- 從讀FIFO寄存器讀取配置bitstream。
- 向CR寄存器寫值去啓動bitstream的讀取或寫入,控制寄存器決定了數據傳輸的方向。向控制寄存器寫入0x00000001開始寫配置。寫0x00000002開始讀配置。
- 狀態寄存器的Done位表明了ICAPEn接口是否處於忙碌狀態,並不代表讀配置或寫配置是否成功完成。
- 在成功進行讀或者寫配置之後硬件清零CR寄存器。
- 在CR寄存器未被清零的情況下,軟件不能初始化另一個讀或寫配置操作。
精簡模式下寫序列:
- 將指令寫進寫FIFO寄存器進行配置。
- 向控制寄存器寫控制字初始化寫指令。
- 成功完成配置後硬件清零控制寄存器位。
- 向寫FIFO寄存器寫入第二個指令,寫控制字執行向ICAPEn端口的寫操作。
- 繼續執行上述操作直到所有指令被寫到ICAPEn端口。
中止:
- 向控制寄存器寫控制字開始bitstream讀寫。
- 將bitstream寫入到FIFO寄存器執行配置,從讀FIFO寄存器獲取讀取到的bitstream。
- 向控制寄存器的第五位寫1來執行中止。
- 狀態寄存器的Done位顯示了ICAPEn端口是否正處於忙碌狀態,不表示讀,寫操作成功完成。
- 在成功執行中止操作後硬件清零控制寄存器位。
- 在CR寄存器未被清零的情況下,軟件不能初始化另一個讀或寫配置操作。
注意:
- 幀是配置數據允許被讀或寫的最小粒度。
- 修改單個LUT內容必須讀出整幀,修改後再將整幀數據讀入。
五、總結
本篇博客主要記錄了使用Vivado工具進行脫離上下文(Out Of Context)方式進行開發的流程。以本篇生成的硬件系統爲基礎,可以進一步通過PS端的軟件編程來實現對目標LUT的內容進行修改,亦可通過軟件來爲PR區域加載不同的重構模塊來實現FPGA內部電路的動態重構。