一、接口的定义
SystemVerilog在Verilog语言基础上扩展了“接口”(interface)结构,SystemVerilog增加了新的端口类型—接口,接口允许许多信号合成一组由一个端口表示,只需在一个地方对组成接口的信号进行声明,使用这些信号的模块只需一个接口类型的端口。
接口声明举例:main_bus.sv
interface main_bus;
wire [15:0] data;
wire [15:0] address;
logic [ 7:0] slave_instr;
logic slave_req;
logic bus_grant;
logic bus_req;
logic slave_rdy;
logic data_rdy;
logic mem_read;
logic mem_write;
endinterface
在顶层模块中例化接口:top.sv
module top (input logic clock, resetn, test_mode);
logic [15:0] program_addr, jump_addr;
logic [ 7:0] instr, next_instr;
main_bus bus ( ); // instance of an interface
// (instance name is bus)
processor proc1 (
// main_bus ports
.bus(bus), // interface connection
// other ports
.jump_addr (jump_addr),
.instr (instr),
.clock(clock),
.resetn(resetn),
.test_mode(test_mode));
slave slave1 (
// main_bus ports
.bus(bus), // interface connection
// other ports
.clock(clock),
.resetn(resetn));
dual_port_ram ram (
// main_bus ports
.bus(bus), // interface connection
// other ports
.program_addr (program_addr),
.data_b(next_instr));
endmodule
二、接口的内容
接口不仅仅是一组连接线,它也可以封装模块间通信的所有细节。使用接口可以:(1)在一个地方—接口中定义通信所需的各个信号和端口;(2)在接口中定义通信协议;(3)在接口中直接建立协议校验和其它验证程序。
接口可以包含类型声明、任务、函数、过程块、程序块和断言。
接口与模块之间有三个不同点:
(1)接口不可以包含设计层次,接口不可以包含模块或原语的实例;
(2) 接口可以用作模块端口,表示模块间的通信通道,而在端口中使用模块则是非法的
(3)接口可以包含“modport”,这使得每个连接到接口上的模块以不同的方式访问接口。 (4) 接口内部信号的使用,在有接口类型端口的模块中,接口内部信号必须用端口名进行访问:<端口名称>.<接口内部信号名称>。
//接口声明
interface main_bus;
wire [15:0] data;
wire [15:0] address;
logic [ 7:0] slave_instr;
logic slave_req;
logic bus_grant;
logic bus_req;
logic slave_rdy;
logic data_rdy;
logic mem_read;
logic mem_write;
endinterface
//接口信号调用
module slave ( main_bus bus);
// internal signals
logic [15:0] slave_data, slave_addr;
logic [15:0] operand_A, operand_B;
logic mem_select, read, write;
//assign bus.address=mem_select ? slave_addr :'z;
assign bus.address = mem_select? slave_addr: 'z;
assign bus.data = bus.slave_rdy? slave_data: 'z;
enum logic [4:0] {RESET = 5'b00001, START = 5'b00010,
REQ_DATA = 5'b00100, EXECUTE = 5'b01000,
DONE = 5'b10000} State, NextState;
always_ff @(posedge bus.clock, negedge bus.resetn)
begin: FSM
if (!bus.resetn) State <= START;
else State <= NextState;
end
always_comb begin : FSM_decode
unique case (State)
START: if (!bus.slave_req) begin
bus.bus_req = 0;
NextState = State;
end
else begin
operand_A = bus.data;
slave_addr = bus.address;
bus.bus_req = 1;
NextState = REQ_DATA;
end
// decode other states
endcase
end: FSM_decode
endmodule
SystemVerilog提供两种方法指定模块接口端口使用modport的方式:
(1)在模块实例化时的接口连接中说明;
(2)在模块定义的端口声明时说明;
在模块实例化时的接口连接中说明
interface chip_bus (input logic clock, resetn);
logic interrupt_req, grant, ready;
logic [31:0] address;
wire [63:0] data;
modport master (input interrupt_req,
input address,
output grant, ready,
inout data,
input clock, resetn);
modport slave (output interrupt_req,
output address,
input grant, ready,
inout data,
input clock, resetn);
endinterface
//
module primary (interface pins); // generic interface port
endmodule
module secondary (chip_bus pins); // specific interface port
endmodule
module chip (input logic clock, resetn);
chip_bus bus (clock, resetn); // instance of an interface
primary i1 (bus.master); // use the master modport view
secondary i2 (bus.slave); // use the slave modport view
endmodule
在modport既可在模块实例化时指定,又可以在模块定义中指定,但不能同时选择这两种方式!!
模块定义的端口声明时说明;
module primary (chip_bus.master pins); // generic interface port
...
endmodule
module secondary (chip_bus.slave pins); // specific interface port
...
endmodule
module chip (input logic clock, resetn);
chip_bus bus (clock, resetn); // instance of an interface
primary i1 (bus); // use the master modport view
secondary i2 (bus); // use the slave modport view
endmodule
这一部分主要是interface的实际应用,在总线上挂有一主一从结构,主机对从机进行读写操作,使用modport的接口方式,同时博主也对于自己在撰写过程中遇见的错误进行了总结。
接口部分:
//需要输入的信号 一定要在接口端口声明,否则无法传递数值
interface zuoye_interface(input logic CLK,
input logic RESETN,
input [31:0] wdata,
input [31:0] waddr,
input [31:0] raddr);
logic [31:0] WDATA;
logic [31:0] RDATA;
logic [31:0] rdata;
logic [31:0] WADDR;
logic [31:0] RADDR;
logic WRITE_REQ;
logic WRITE_ACK;
logic READ_ACK;
logic READ_REQ;
logic WR_DONE;
logic RD_DONE;
//主机master
modport M1(
input CLK,
input RESETN,
input WRITE_ACK,
input READ_ACK,
input RD_DONE,
input wdata,
input RDATA,
input waddr,
input raddr,
output WDATA,
output rdata,
output WRITE_REQ,
output WADDR,
output RADDR,
output READ_REQ,
output WR_DONE
);
//从机slave
modport S1(
input WR_DONE,
input CLK,
input RESETN,
input WDATA,
input WADDR,
input RADDR,
input WRITE_REQ,
input READ_REQ,
output RDATA,
output READ_ACK,
output WRITE_ACK,
output RD_DONE
);
endinterface
TOP模块部分
module zuoye_top(input CLK,RESETN);
zuoye_interface u1(.CLK,.RESETN);
zuoye_M1 i1(u1.M1);
zuoye_S1 i2(u1.S1);
endmodule
主机Master部分
module zuoye_M1(zuoye_interface.M1 M1);
enum logic [2:0]
{IDLE,WAIT,WRITE_ADDR,WRITE_DATA,
STOP,READ_ADDR,READ_DATA}State,NextState;
logic [1:0] wr_flag;
always_ff @(posedge M1.CLK , negedge M1.RESETN)
begin
if(!M1.RESETN)
State <= IDLE;
else
State <= NextState;
end
always_comb
begin
NextState=State;
unique case(State)
IDLE: begin
if(wr_flag==1)
NextState=READ_ADDR;
else
NextState=WAIT;
end
WAIT: begin
if(M1.WRITE_ACK==1)
NextState=WRITE_ADDR;
end
WRITE_ADDR:begin
NextState=WRITE_DATA;
end
WRITE_DATA:begin
NextState=IDLE;
end
READ_ADDR:begin
if(M1.RD_DONE==1)
NextState=READ_DATA;
end
READ_DATA:begin
NextState=STOP;
end
STOP:begin
NextState=IDLE;
end
endcase
end
always_ff@(posedge M1.CLK , negedge M1.RESETN)
begin
// wr_flag <=0;
wr_flag <=0;
M1.WR_DONE <=0;
unique case(State)
IDLE: begin
if(wr_flag==1) begin
M1.READ_REQ <=1;
M1.WRITE_REQ <=0;
end
else begin
M1.WRITE_REQ <=1;
M1.READ_REQ <=0;
end
end
WAIT: begin
;
end
WRITE_ADDR: begin
M1.WADDR <=M1.waddr;
end
WRITE_DATA: begin
M1.WDATA <=M1.wdata;
wr_flag <=1;
M1.WR_DONE <=1;
end
READ_ADDR: begin
M1.RADDR <=M1.raddr;
end
READ_DATA:begin
M1.rdata <=M1.RDATA;
end
STOP: begin
;
end
endcase
end
endmodule
从机slave部分
module zuoye_S1(zuoye_interface.S1 S1);
enum logic [2:0]
{IDLE,WAIT,WRITE,STOP,READ}State,NextState;
logic [1:0] flag; //write finish
logic [7:0] array [0:255]; //memory
always_ff @(posedge S1.CLK , negedge S1.RESETN)
begin
if(!S1.RESETN)
State <= IDLE;
else
State <= NextState;
end
always_comb
begin
NextState=State; //初始化
unique case(State)
IDLE:
begin
if(flag==1)
NextState=READ;
else
NextState=WAIT;
end
WAIT: begin
if(S1.WRITE_REQ==1&&S1.WR_DONE==1)
NextState=WRITE;
else if(S1.READ_REQ==1)
NextState=READ;
else
NextState=WAIT;
end
WRITE:begin
NextState=IDLE;
end
READ:begin
NextState=STOP;
end
STOP:begin
NextState=IDLE;
end
endcase
end
always_ff@(posedge S1.CLK , negedge S1.RESETN)
begin
flag <=0;
S1.RD_DONE <=0;
unique case(State)
IDLE: begin
;
end
WAIT: begin
if(S1.WRITE_REQ==1)
S1.WRITE_ACK <= 1;
else if(S1.READ_REQ==1)
S1.READ_ACK <= 1;
end
WRITE: begin
array[S1.WADDR] <= S1.WDATA;
flag <=1;
end
READ: begin
S1.RDATA <= array[S1.RADDR];
S1.RD_DONE <= 1;
end
STOP: begin
;
end
endcase
end
endmodule
测试代码
module zuoye_top_tb;
logic CLK,RESETN;
logic [31:0] waddr,raddr;
logic [31:0] wdata;
zuoye_interface bus(.CLK(CLK),.RESETN(RESETN),
.wdata(wdata),.waddr(waddr),.raddr(raddr));
zuoye_M1 u_M1(bus.M1);
zuoye_S1 u_S1(bus.S1);
initial
begin
CLK=0;
forever #5 CLK=~CLK;
end
initial
begin
RESETN=1;
#10
RESETN=0;
#10
RESETN=1;
end
initial
begin
wdata=32'h8000_0000;
waddr=32'h8000_0000;
#50;
for(int i=0;i<32;i++)
begin
waddr=i;
wdata=i;
#120;
end
for(int j=0;j<32;j++)
begin
raddr=j;
#120;
end
$finish;
end
endmodule