Systemverilog interface/modport 簡介&使用方法 (6 years after)

Share on:

本文內容採用創用 CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款授權.

六年之前的文章中,提到了新的 SystemVerilog 標準中,引入了 interfacestructmodport 這樣的語法,可以提供結構化的方式來宣告 port 或是 register 等等,大幅減少程式碼量。礙於當時 EDA tool 支援程度的限制,所以用了很多 macro 的 workaround。多年之後的現在,EDA tool 也有了大幅度的變化,所以重新來測試一下。

(本文部份參考自 Synopsys Users Group (SNUG)Synthesizing SystemVerilog; Busting the Myth that SystemVerilog is only for Verification,裡面討論了許多 Synopsys 認定現在 EDA tool 可以用於合成的 SystemVerilog 語法。)

如下方所示,一個常見的 bus write data channel 界面可能有:

1parameter ID_SIZE = 1;
2parameter NUM_BYTE = 16;
3parameter ADDR_SIZE = 16;
4logic [ID_SIZE   -1:0] WID;
5logic [ADDR_SIZE -1:0] WADDR;
6logic [NUM_BYTE*8-1:0] WDATA;
7logic [NUM_BYTE  -1:0] WBMASK;
理所當然的,當我們想要建立多個 Verilog bus IP 的時候,可以期待我們可以寫一個共通的 structure,類似 C++ 的 template 可以,來建立不同 address/byte 的 bus IP。
1typedef struct {
2    parameter ID_SIZE = 1;
3    parameter NUM_BYTE = 16;
4    parameter ADDR_SIZE = 16;
5    logic [ID_SIZE   -1:0] ID;
6    logic [ADDR_SIZE -1:0] ADDR;
7    logic [NUM_BYTE*8-1:0] DATA;
8    logic [NUM_BYTE  -1:0] BMASK;
9} WCHANNEL;
像是這樣
1module BusIP1(
2    output WCHANNEL#(1,4,8) wchannel
3);
4module BusIP2(
5    output WCHANNEL#(2,16,16) wchannel
6);

在 Verilog 的語法中,由於缺乏這些語言特性,所以就只能這樣寫,需要大量的程式碼。

 1module BusIP1(
 2    logic [1  -1:0] ID;
 3    logic [4  -1:0] ADDR;
 4    logic [8*8-1:0] DATA;
 5    logic [8  -1:0] BMASK;
 6);
 7module BusIP2(
 8    logic [2   -1:0] ID;
 9    logic [16  -1:0] ADDR;
10    logic [16*8-1:0] DATA;
11    logic [16  -1:0] BMASK;
12);
Code 的量一多必然容易出錯,尤其是當我們需要多層 hierarchy 之間連線的時候、宣告 logic、reset register 的時候,我們都必須使用一樣多的行數來做這件事情,必然有很多時間浪費在修改語法錯誤以及 linter 上面。

解決方案

在 SystemVerilog 中,的確引入的這樣的語法讓我們參數化 Verilog,不過 struct 本身不支援這樣的語法,需要使用 class。

 1class WCHANNEL#(
 2    parameter ID_SIZE = 1,
 3    parameter NUM_BYTE = 16,
 4    parameter ADDR_SIZE = 16
 5);
 6    typedef struct {
 7        logic [ID_SIZE   -1:0] ID;
 8        logic [ADDR_SIZE -1:0] ADDR;
 9        logic [NUM_BYTE*8-1:0] DATA;
10        logic [NUM_BYTE  -1:0] BMASK;
11    } dtype;
12endclass
13module BusIP1(
14    output WCHANNEL#(1,4,8)::dtype wchannel
15);
16module BusIP2(
17    output WCHANNEL#(2,16,16)::dtype wchannel
18);
(註:熟悉 C++ 的人,可能會覺得這個方案跟 C++ 類似,例如從 map 裡面可以取出一個在他下面的型別 value_type,他被定義為 pair<const string, int>,所以下面兩行是一樣的型別。)
1map<string, int>::value_type;
2pair<const string, int>;

支援程度

我測試了幾個我手上有的 tool,他們的支援程度如下:

  • Vivado 2020.2:合成了一個簡單的 module 來做測試,檢查了一下合成出來的 schematic,我認定他是支援的。
  • Synopsys DC:雖然沒有測試,不過 SNUG 的文件都這麼寫了,我想沒有理由懷疑其支援程度。
  • Verilator v5.002:不支援,雖然官方 github 上面有 issue 在討論,不過根據其活躍程度以及 issue 時間,我覺得短期之內應該是還看不到這個功能。