使用ZYNQ實現單LUT內容的動態修改(一)PL端OOC設計流程

一,摘要

本文主要用於記錄作者本人實現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進行部分重構系統開發的整體流程:

  1. 使用Vivado創建整個設計,將部分重構模塊保留爲空模塊。綜合工程生成靜態設計的.dcp文件(如top_syn_static.dcp)。
  2. 綜合部分重構模塊的源文件,生成對應的.dcp文件(如:pr_lut_syn.dcp)。
  3. 打開第一步生成的top_syn_static.dcp,讀入第二步生成的pr_lut_syn.dcp,設置重構屬性,保存設計點。
  4. 爲重構模塊指定重構區域,讀入約束文件。
  5. 優化,佈局,佈線,保存設計點。
  6. 生成bitstream文件,及ila調試相關文件。
  7. 將重構模塊清除保留爲黑盒,將其他部分在佈局佈線後的級別上保存其設計點。
  8. 打開上一步保存的設計點,讀入新的重構模塊,優化,佈局佈線,保存設計點,生成bitstream等。
  9. 使用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中數據的映射關係:
rms

第三步,打開設計點,讀入重構模塊,劃分重構區域

執行以下腳本

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相關信息

相關寄存器如下表:
reg1
在這裏插入圖片描述
在這裏插入圖片描述
讀寫配置:

  1. 將bitstream寫進寫FIFO寄存器進行配置。
  2. 從讀FIFO寄存器讀取配置bitstream。
  3. 向CR寄存器寫值去啓動bitstream的讀取或寫入,控制寄存器決定了數據傳輸的方向。向控制寄存器寫入0x00000001開始寫配置。寫0x00000002開始讀配置。
  4. 狀態寄存器的Done位表明了ICAPEn接口是否處於忙碌狀態,並不代表讀配置或寫配置是否成功完成。
  5. 在成功進行讀或者寫配置之後硬件清零CR寄存器。
  6. 在CR寄存器未被清零的情況下,軟件不能初始化另一個讀或寫配置操作。

精簡模式下寫序列:

  1. 將指令寫進寫FIFO寄存器進行配置。
  2. 向控制寄存器寫控制字初始化寫指令。
  3. 成功完成配置後硬件清零控制寄存器位。
  4. 向寫FIFO寄存器寫入第二個指令,寫控制字執行向ICAPEn端口的寫操作。
  5. 繼續執行上述操作直到所有指令被寫到ICAPEn端口。

中止:

  1. 向控制寄存器寫控制字開始bitstream讀寫。
  2. 將bitstream寫入到FIFO寄存器執行配置,從讀FIFO寄存器獲取讀取到的bitstream。
  3. 向控制寄存器的第五位寫1來執行中止。
  4. 狀態寄存器的Done位顯示了ICAPEn端口是否正處於忙碌狀態,不表示讀,寫操作成功完成。
  5. 在成功執行中止操作後硬件清零控制寄存器位。
  6. 在CR寄存器未被清零的情況下,軟件不能初始化另一個讀或寫配置操作。

注意:

  1. 幀是配置數據允許被讀或寫的最小粒度。
  2. 修改單個LUT內容必須讀出整幀,修改後再將整幀數據讀入。

五、總結

本篇博客主要記錄了使用Vivado工具進行脫離上下文(Out Of Context)方式進行開發的流程。以本篇生成的硬件系統爲基礎,可以進一步通過PS端的軟件編程來實現對目標LUT的內容進行修改,亦可通過軟件來爲PR區域加載不同的重構模塊來實現FPGA內部電路的動態重構。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章