­

高級綜合工具StratusHLS學習筆記(2)

  • 2020 年 3 月 26 日
  • 筆記

學習目標為:

  • 如何使用高級綜合生成流水線
  • 如何使用Stratus進行層次化設計

1.生成流水線

Stratus允許指定一個主循環(while(1))中的內容為流水線方式實現,即每個時鐘周期均可以進入數據執行,需要在主循環開始時添加如下語句指定使用流水線實現:

HLS_PIPELINE_LOOP(<STALL_TYPE>, <cycle>, <name>);

上述指定該loop為流水線實現,具有三個參數,分別如下所示:

  • STALL_TYPE:實現類型,包括HARD_STALLSOFT_STALL兩種
  • cycle:數據進入間隔,即「每隔多少個時鐘周期可進入一個數據」,當cycle=1時表示每個周期均可進入數據
  • name:流水線配置名稱,每個循環流水線名稱不同即可

對於STALL_TYPE中的兩種,具有以下的區別:

  • HARD_STALL:當流水線的某一級阻塞時,整條流水線停止運行
  • SOFT_STALL:當流水線的某一級阻塞時,僅阻塞級之前的流水線停止運行,阻塞級之後的流水線繼續運行

對於要生成流水線的代碼片(循環體),Stratus有以下的要求:

  • 循環展開(Nested Loops):循環體中僅可以嵌套次數指定的循環,且被指定生成流水線的循環要麼為無限循環,要麼為指定次數循環
  • 數據依賴(Data Dependencies):需要保證每一級需要的數據在運行這一級之前已經生成,即數據流向固定向前,不存在反向數據流(產生數據衝突),若發生這種情況Stratus會報錯:Unable to satisfy HLS_HLS_PIPELINE_LOOP directive "main_pipeline",possibly because of a statement in this line.
  • 端口訪問(Port Access Conflicts):對於端口的訪問需要謹慎,需要避免連續兩個周期訪問一個端口的寫法,因為會產生對端口的訪問衝突(前一次進入loop和後一次loop在同一周期需要訪問同一個接口),這種情況會報出Warning:Pipelining forces multiple assignments to output data_out
  • 非平衡流水線(Unbalanced Protocol Blocks):避免在展開為流水線的循環中使用消耗時鐘周期不同的條件判斷。即若在循環中使用if-else語句,兩個代碼塊消耗的時鐘周期必須一致。
  • 循環跳出(Conditional Exits in Pipelined Loops):允許使用break語句跳出循環,但用於判斷是否跳出循環的邏輯消耗的時間必須少於數據進入間隔時鐘周期

學習過程使用上一次使用的+1功能電路,將其執行線程改為以下按流水線展開:

void dut_template::t() {      {          HLS_DEFINE_PROTOCOL("reset");          x_in.reset();          y_out.reset();          wait();      }      while(1) {          // HLS_PIPELINE_LOOP(HARD_STALL, 1, "main_loop");          HLS_PIPELINE_LOOP(SOFT_STALL, 1, "main_loop");          DT x_val = x_in.get();          DT out_val = x_val + 1;          y_out.put(out_val);      }  }

這裡使用了輸入間隔為1個周期(每個周期均可輸入)的SOFT_STALL形式的流水線。

2.層次化設計

為了觀察流水線功能,這次將兩個+1功能模塊dut_template連在一起進行仿真,頂層為pipeline_test,代碼如下所所示:

#ifndef _DUT_PIPE  #define _DUT_PIPE    #include "cynw_p2p.h"  #include "cynw_fifo.h"  #include "defines.h"  #include "dut_template_wrap.h"    SC_MODULE(pipeline_test) {  public:      cynw_p2p<DT, ioConfig>::base_in x_in;      cynw_p2p<DT, ioConfig>::base_out y_out;      cynw_p2p<DT, ioConfig> tmp;      sc_in_clk clk;      sc_in<bool> rst;        dut_template_wrapper *ut0;      dut_template_wrapper *ut1;        SC_CTOR(pipeline_test):          x_in("x_in"),y_out("y_out"),tmp("tmp"),          clk("clk"),rst("rst") {            ut0 = new dut_template_wrapper("ut0");          ut0->clk(clk);          ut0->rst(rst);          ut0->x_in(x_in);          ut0->y_out(tmp);            ut1 = new dut_template_wrapper("ut1");          ut1->clk(clk);          ut1->rst(rst);          ut1->x_in(tmp);          ut1->y_out(y_out);      }        // void t();  不可調用函數進行連線!!!    };    #endif

首先關注使用的p2p接口如下所示:

    cynw_p2p<DT, ioConfig>::base_in x_in;      cynw_p2p<DT, ioConfig>::base_out y_out;

需要注意的是本次使用了base_inbase_out而不是inout(參考筆記1),因為這兩個端口的目的僅僅為連接使用,相當於連線,因此不需要使用inout,也不需要指定時鐘與複位信號。隨後關注調用部分:

    dut_template_wrapper *ut0;      dut_template_wrapper *ut1;

這裡的調用方式為調用dut_template_wrapper而不是dut_template,這是Stratus的區別,若要在高級綜合中保留層次結構,則需要在這裡調用wrapper而不是本身,對應的,也需要在tcl中指定子模塊dut_template為待綜合模塊。最後一點需要注意的是,SC_CTOR中連線部分需要在本函數中編寫,不可像system中一樣調用函數進行連線,否則會在仿真過程中產生問題。該設計對應的project.tcl如下所示:

...  use_tech_lib    "$LIB_PATH/$LIB_LEAF"      set_attr clock_period           10.0  set_attr message_detail         3  set_attr default_input_delay    0.1    set_attr cc_options             "  -g --std=c++0x"  enable_waveform_logging -vcd  set_attr end_of_sim_command "make saySimPassed"    define_system_module basic_ut/main.cpp  define_system_module basic_ut/system.cpp  define_system_module basic_ut/tb.cpp    define_hls_module pipeline_test dut_module/pipeline_test.cpp  define_hls_module dut_template dut_module/dut_template.cpp # 子模塊也需要指定為待綜合模塊    define_io_config * TLM  define_io_config * PIN    define_hls_config pipeline_test BASIC  define_hls_config dut_template BASIC # 子模塊也需要指定綜合等級    define_sim_config T -io_config TLM  define_sim_config B -io_config PIN    define_sim_config H {pipeline_test RTL_V BASIC}

3.仿真結果

仿真結束後使用verdi查看波形,未添加流水線的波形如下所示:

可以發現這種情況下每兩個周期才能輸入一個數據,添加了流水線的波形如下所示:

添加了流水線展開後,可以發現每個時鐘周期均可輸入新的數據。