这篇文章主要是介绍接口interface的作用,为什么要有接口,接口的优点、缺点,以及利用接口去实现一主一从结构之间的读写操作。
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 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));
//接口声明
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.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
在模块实例化时的接口连接中说明
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
仿真结果
主从结构写功能仿真结果:
主从结构读功能仿真结果:
分析:
由于测试代码中的地址与数据是同一个数,地址与所在地址写入的数据相等,从图中可以看出主机可以实现写地址写数据功能,第二张图可以看出,输入连续的读地址以后,读出的数据正确,可以实现正确的读写功能。
收获:
比如接口的输入信号必须声明端口,第一次就是因为主机的输入信号wdata,waddr,raddr没有在接口列表中声明,导致数据无法传输,第二个问题是为了实现主机与从机的通信,必须设置wr_done与rd_done的标志位,因为如果不设置wr_done可能主机没有写完数据,从机开始进行数据的写入,写入的就是不定态,同理读出的也是不定态,存储器无法存储正确的值。
下一篇文章是关于systemverilog撰写的成绩管理系统