在上一篇文章中说明了如何使用SystemVerilog的interface来实现FPGA内部的输出数据总线功能,但仅给出了传输单个数据的方法。然而在实际应用中,我们可能会遇到需要传输多个数据的需求,此时前文给出的方案将存在一定的局限性。虽然我们可以声明多个不同的interface实例来分别传输这些数据,但总没有仅通过一个interface实例传输来得方便。
要将多个数据通过一个interface传输,我们首先想到的是SystemVerilog的struct——通过将这些待传输的数据用struct打包,便可以通过一个interface传输了。但问题也随之而来了:
1.struct不像interface那样支持参数化,对不同位宽的数据必须单独定义struct,这显然有悖于泛化编程的思想;
2.数据总线interface中定义的struct类型不能被外部模块引用,只能通过interface中定义的modport来传输给外部的目标模块,然而interface的一个modport只能传递一个struct实例,对需要使用不同struct实例的外部模块来说需求显然无法满足,即无法实现多个外部模块与interface内部不同struct实例的连接。
实际上,interface也可以实现struct的数据打包功能,而由于interface支持参数化,这也使得对打包数据的泛化编程变得可能;而且,interface支持定义modport,支持嵌入运算代码,使得其功能比struct更加强大。
因此,传输多个数据的数据总线的解决方案是:
1.用interface打包待传输数据,然后在数据总线interface中声明多个打包interface的实例;
2.外部模块以打包interface或其内定义的modport作为形参,而在外部模块实例化时,选择数据总线interface中与本模块实例对应的打包interface实例连接至本模块实例上。
示例代码:
interface data_transfer;
wire[31:0] data1;
wire[15:0] data2;
endinterface
interface if_dbus #(
parameter LINKED_MDL
) (
output data_transfer odbus
);
logic[LINKED_MDL_CNT-1:0] ocs;
data_transfer data2sel[LINKED_MDL_CNT-1:0];
always_comb begin
odbus = '0;
for (int i = 0; i < LINKED_MDL_CNT; i++) begin
if (ocs[i]) odbus = data2sel[i];
end
end
endinterface
应用示例:
module dbus_mdl1 #(
parameter DATA1,
parameter DATA2
)(
output data_transfer o_data
);
assign o_data.data1 = 32'(DATA1);
assign o_data.data2 = 16'(DATA2);
endmodule
module dbus_mdl2 #(
parameter DATA1,
parameter DATA2
)(
output data_transfer data_o
);
assign data_o.data1 = 32'(DATA1);
assign data_o.data2 = 16'(DATA2);
endmodule
module tb_if_dbus;
data_transfer dbus2exam;
if_dbus #(
.LINKED_MDL_CNT(5)
) dbus_ifi(
.odbus(dbus2exam)
);
genvar i;
generate
for (i= 0; i < 3; i++) begin:MDL1_GEN
dbus_mdl1 #(
.DATA1(i),
.DATA2(i*2)
) im(
.o_data(dbus_ifi.data2sel[i])
);
end
for (i = 3; i < 5; i++) begin:MDL2_GEN
dbus_mdl2 #(
.DATA1(i),
.DATA2(i*3)
) im2(
.o_data(dbus_ifi.data2sel[i])
);
end
endgenerate
// 测试总线输出数据的代码,查看dbus2exam
reg clk;
initial begin
clk = 0;
end
always #1 clk = ~clk;
reg[2:0] idxcntr;
initial begin
idxcntr = '0;
end
always_ff @(posedge clk) begin
idxcntr = idxcntr + 1;
end
always_comb begin
dbus_ifi.ocs = '0;
if (idxcntr < 5) dbus_ifi.ocs[i] = 1'b1;
end
endmodule