Systemverilog interface/modport 簡介&使用方法
在新的 SystemVerilog 標準中,引入了 interface
跟 modport
這樣的語法。本文章中將會討論這兩者的用法、限制以及突破限制的方法。
如下方所示,這是一個 Verilog struct
的語法:
1typedef struct {
2 parameter DATA_SIZE = 8;
3 parameter ADDR_SIZE = 4;
4 logic [DATA_SIZE-1:0] addr;
5 logic [ADDR_SIZE-1:0] data;
6} SramStruct;
struct
是不夠的,主要的原因是因為 Verilog 的信號有方向性 (input/output
),一個 struct
可能就同時有兩個方向性的信號,必須有額外的方法來定義信號方向性。
Interface and modport
於是在 Verilog 標準中,就有了 interface
來取代 struct
的功能,然後再用 modport
加以描述方向性。例如在 SRAM 中,我們會用一個位置 addr
跟一個資料 data
,來表示一個我們要把資料寫入 SRAM 或是從 SRAM 讀出。為了定義不同的信號方向,我們可以這樣使用 interface/modport
:
1interface SramInterface();
2 parameter DATA_SIZE = 8;
3 parameter ADDR_SIZE = 4;
4 logic [DATA_SIZE-1:0] addr;
5 logic [ADDR_SIZE-1:0] data;
6 modport Read__Slave (input addr, output data, input en);
7 modport Write_Slave (input addr, input data, input en);
8 modport Read__Master(output addr, input data, output en);
9 modport Write_Master(output addr, output data, output en);
10endinterface
1module SRAM(clk, rin, win);
2 input clk;
3 SramInterface.Read__Slave rin;
4 SramInterface.Write_Slave win;
1module Top();
2 MemoryInterface read_port, write_port;
3 SRAM sram(clk, read_port, write_port);
4 initial read_port.addr = 4'h9;
5endmodule
Interface/modport 限制
即使有這麼多的好處,作者仍認為 interface/modport
在實用上有很多限制,其最大的原因是在現在的語法規範中,雖然可以透過 parameter
產生不同大小的 interface
,其運作方式導致其缺乏的防錯機制。
舉例而言,在上面的例子中:
1module SRAM(clk, rin, win);
2 input clk;
3 SramInterface.Read__Slave rin;
4 SramInterface.Write_Slave win;
rin/win
這兩個 interface
只能根據上層呼叫方式自動推導,來決定兩個的 interface
的 parameter
。換句話說,如果上層使用了:
1module Top();
2 MemoryInterface#(8,4) read__port;
3 MemoryInterface#(4,8) write_port;
4 SRAM sram(clk, read__port, write_port);
5 initial read_port.addr = 4'h9;
6endmodule
interface
,這對 nLint 等等工具來說也不好檢查。問題就出在 Verilog 語法上,一個 module 中無法先給 parameter
,再用他來決定 interface
。也就是說,這樣的語法不存在的:
1module SRAM(clk, rin, win);
2 parameter D = 8;
3 parameter A = 4;
4 input clk;
5 SramInterface#(D,A).Read__Slave rin;
6 SramInterface#(D,A).Write_Slave win;
interface
的 parameter
來確認 parameter
是正確的:
1module SRAM(clk, rin, win);
2 input clk;
3 SramInterface.Read__Slave rin;
4 SramInterface.Write_Slave win;
5 assert(rin.DATA_SIZE == win.DATA_SIZE);
6 assert(rin.ADDR_SIZE == win.ADDR_SIZE);
用 macro 來當替代方案
基於上述這麼多限制,加上目前仍有許多 EDA tool 支援度不佳,作者不建議使用 interface
功能。
於是,這邊來討論一種用 define 來達成類似的效果的方法。這個方法中,macro 展開之後,甚至可以完全相容於舊 Verilog 語法,並且對於使用 module 的人來說,其需要寫的程式碼跟 interface
一樣多:
1`define SramInterface#_Logic(A,D,name)\
2 logic [A-1:0] name``_addr;\
3 logic [D-1:0] name``_data;
4`define SramInterface#_Name(A,D,name)\
5 name``_addr,\
6 name``_data,
7`define SramInterface#_Read__Slave(A,D,name)\
8 input [A-1:0] name``_addr;\
9 input [D-1:0] name``_data;
10`define SramInterface_Write_Slave (A,D,name)\
11 input [A-1:0] name``_addr;\
12 output logic [D-1:0] name``_data;
13`define SramInterface_Read__Master(A,D,name)\
14 output logic [A-1:0] name``_addr;\
15 output logic [D-1:0] name``_data;
16`define SramInterface_Write_Master(A,D,name)\
17 output logic [A-1:0] name``_addr;\
18 input [D-1:0] name``_data;
19`define SramInterface#_Connect(port_name,id_name)\
20 port_name``_addr(id_name``_addr),\
21 .port_name``_data(id_name``_data)
1module SRAM(clk, `SramInterface_Name(mif_input));
2 parameter A = 4;
3 parameter D = 8;
4 input clk;
5 `SramInterface_Read__Slave(D, A, rin);
6 `SramInterface_Write_Slave(D, A, win);
7endmodule
8
9module Top();
10 `SramInterface_Logic read__port;
11 `SramInterface_Logic write_port;
12 SRAM sram(
13 .clk(clk),
14 `SramInterface_Connect(read_port, rin),
15 `SramInterface_Connect(write_port,win)
16 );
17 initial mif_addr = 4'h9;
18endmodule