2016年5月23日 星期一

Systemverilog interface/modport 使用心得

Systemverilog 裡面引入了 interface
試著把最基礎的部份引入正在寫的 code 裡面
並且整理了一些使用心得


interface MemoryInterface();
   parameter DAT_SIZ = 8;
   parameter ADR_SIZ = 4;
   logic [DAT_SIZ-1:0] addr;
   logic [ADR_SIZ-1:0] data;
   logic en;
   modport RI(input  addr, output data, input  en);
   modport WI(input  addr, input  data, input  en);
   modport RO(output addr, input  data, output en);
   modport WO(output addr, output data, output en);
endinterface

這東西看起來超棒的
例如說想要一個記憶體的界面可以如上這樣寫
我們想使用 SRAM 只要這樣(先假設只有讀取功能)
module SRAM(input clk, MemoryInterface.RI mif);
   // blahblah
endmodule

module Top();
   MemoryInterface mif;
   SRAM sram(clk, mif);
   initial mif.addr = 'x;
endmodule
Code 瞬間少了一堆,超棒的 然而 interface 遇到 parameter 的時候很微妙
這東西有點像具有 C++ 的型別推導功能
但是只有自動模式
個人覺得這非常微妙,舉例如下
例如我想要 2 port(1R 1W) 的 SRAM 作法如下
module SRAM(input clk, MemoryInterface.RI mif1, MemoryInterface.WI mif2);
   // blahblah
endmodule
嗯,我們要怎麼知道兩個進來的 port size 一樣?
這個模式下面沒辦法,因為只能根據外面怎麼叫來推導兩個的 interface 的 parameter
那這樣應該行了吧
module SRAM(clk, mif1, mif2);
   parameter D = 8;
   parameter A = 4;
   input clk;
   MemoryInterface#(D,A).RI mif1;
   MemoryInterface#(D,A).WI mif2;
endmodule
Sounds great!
不過根據我查了兩三天的結果
沒有這樣的語法
也就是說只有上面的版本可以用
在這個情形下,我們可以通過存取 interface 的 parameter 來取得必須的參數:
module SRAM(input clk, MemoryInterface.RI mif1, MemoryInterface.WI mif2);
   localparam A = mif1.ADR_SIZ;
   localparam D = mif1.DAT_SIZ;
   logic [D-1:0] blahblah;
endmodule
但是還是不能保證兩個進來的 port size 一樣
從結論上來說
用了 interface 的單一個 module 不能主動決定任何的 parameter
這產生了許多問題:
  • 用 nlint 檢查單一 module 的時候很不方便,因為上層 module 輸入大小後才能確定 intreface 的形狀
  • 我們不能確定傳進來的 interface 長什麼樣子

但是仔細想想
interface 的功能用 define 來作好像也很容易達成類似的效果
code 的複雜度也可以接受
`define MemIf_Logic(A,D,name)\
   logic [A-1:0] name``_addr;\
   logic [D-1:0] name``_data;\
   logic name``_en
`define MemIf_Name(A,D,name)\
   name``_addr,\
   name``_data,\
   name``_en
`define MemIf_RI(A,D,name)\
   input [A-1:0] name``_addr;\
   input [D-1:0] name``_data;\
   output name``_en
`define MemIf_Connect(port_name,id_name)\
    port_name``_addr(id_name``_addr),\
   .port_name``_data(id_name``_data),\
   .port_name``_en(id_name``_en)
...
兩個撇號是把 id 串起來的意思,跟 C 裡面的井號功能一樣
可以看到 define 的部份複雜了很多
不過使用上跟 interface 接近,而且避開了所有上述的問題
並且使用這種 workaround 的支援更好(Quartus 16 沒有支援 modport)
module SRAM(clk, `MemIf_Name(mif_input));
   parameter A = 4;
   parameter D = 8;
   input clk;
   `MemIf_RI(A,D,mif_input);
endmodule

module Top();
   `MemIf_Logic mif_yaya;
   SRAM sram(.clk(clk), `MemIf_Connect(mif_input,mif_yaya));
   initial mif_addr = 'x;
endmodule
不過 Verilog 的 define 好像不能加空格
像這樣似乎是錯誤的
`define MemIf_Connect   (port_name,id_name)

3 則留言:

  1. Hi 看到你用define來達到interface的功能,覺得收穫蠻大的,謝謝你的分享
    不過最後一段instantiate的範例寫法似乎有錯,下面是我推測你的意圖改寫,並加入parameterized的寫法
    ``` verilog
    module Top();
    localparam A = 4, D =8;
    `MemIf_Logic(A, D, mif);
    SRAM#(.A(A), .D(D)) sram(.clk(clk), .`MemIf_Connect(mif_input,mif));
    initial mif_addr = 'x;
    endmodule
    module SRAM #(parameter A = 3, D = 9)(clk, `MemIF_Name(A, D, mif_input));
    input clk;
    `MemIf_RI(A,D,mif_input);
    endmodule
    ```

    回覆刪除
    回覆
    1. PS. 我只有試過ncverilog 及verdi 兩者檢查語法

      刪除
    2. 嗯嗯,應該是當時複製的時候改錯了
      已改正

      刪除