田中太郎
UVMの検証環境ではinterfaceをよく使用します
RTLでも使用できるので、サンプルを作成しました
回路仕様
dinにオフセットを3回に分けて加算して出力します
in_validが1のときのdinが有効データで、
out_validが1のときのdoutが有効データです
加算は加算回路(sub.sv)で行われます。加算回路間はbus_*to*で通信します
bus_*to*はvalid=1, ready=1のときのdataが有効データとなるプロトコルです
サンプルコード
bus.sv
interfaceを使用して加算回路間(sub.sv)のバスを定義します
interface i_bus_t;
logic valid;
logic ready;
logic [5:0] data;
modport slave (input valid, input data, output ready);
modport master (input ready, output valid, output data);
endinterface
sub.sv
加算回路です
offsetを加算して出力します
module sub (
input clk,
input rst_n,
input [1:0] offset,
i_bus_t.slave s,
i_bus_t.master m
);
assign s.ready = '1;
always_ff @(posedge clk, negedge rst_n) begin
if (!rst_n) begin
m.valid <= '0;
m.data <= '0;
end
else begin
if (s.valid) begin
m.valid <= s.valid;
m.data <= s.data + offset;
end
end
end
endmodule
dut.sv
subを3つインスタンスして、interfaceで作成したbusを接続しています
module top (
input clk,
input rst_n,
input in_valid,
input [5:0] din,
input [1:0] offset1,
input [1:0] offset2,
input [1:0] offset3,
output logic out_valid,
output logic [5:0] dout
);
i_bus_t bus_to1();
i_bus_t bus_1to2();
i_bus_t bus_2to3();
i_bus_t bus_3to();
assign bus_to1.valid = in_valid;
assign bus_to1.data = din;
sub i_sub1(
.clk(clk),
.rst_n(rst_n),
.offset(offset1),
.s(bus_to1), // トップポートからの入力。i_sub1から見たらスレーブ
.m(bus_1to2) // i_sub1の出力。i_sub2からみたマスタポート。
);
sub i_sub2(
.clk(clk),
.rst_n(rst_n),
.offset(offset2),
.s(bus_1to2), // i_sub1からの入力。i_sub2から見たらスレーブ。
.m(bus_2to3) // i_sub2の出力。i_sub3からみたマスタポート。
);
sub i_sub3(
.clk(clk),
.rst_n(rst_n),
.offset(offset3),
.s(bus_2to3), // i_sub2からの入力。i_sub3から見たらスレーブ。
.m(bus_3to) // i_sub3の出力。トップポートの出力。
);
assign out_valid = bus_3to.valid;
assign dout = bus_3to.data;
endmodule
動作確認
テストベンチを作成して動作確認します
シーケンスは以下です
リセット→クロックスタート→リセット解除→in_valid=1→終了
din=0, offset1=2’b10, offset2=2’b01, offset3=2’b11です
dinにoffset1~3を加算する回路なので、0+2+1+3=6が出力されるはずです
tb.sv
module tb;
bit clk = 0;
bit rst_n = 0;
always #5 clk = ~clk;
bit in_valid = 0;
bit out_valid;
bit ready;
bit [5:0] din = '0;
bit [5:0] dout;
top i_top(
.clk(clk),
.rst_n(rst_n),
.in_valid(in_valid),
.din(din),
.offset1(2'b10),
.offset2(2'b01),
.offset3(2'b11),
.out_valid(out_valid),
.dout(dout)
);
initial begin
@(posedge clk iff out_valid); // out_validが1のときのdoutを表示。
$display("%d", dout);
end
initial begin
#20;
rst_n = 1;
#10;
in_valid = 1;
#10;
repeat(10) @(posedge clk);
$finish;
end
endmodule
出力結果
6
まとめ
interfaceを使用してサンプル回路を作成しました。
この回路をinterfaceを使わないバージョンも記載しておきます
top.sv
module top (
input clk,
input rst_n,
input in_valid,
input [5:0] din,
input [1:0] offset1,
input [1:0] offset2,
input [1:0] offset3,
output logic out_valid,
output logic [5:0] dout
);
logic valid_1to2;
logic valid_2to3;
logic ready;
logic ready_2to1;
logic ready_3to2;
logic [5:0] data_1to2;
logic [5:0] data_2to3;
sub i_sub1(
.clk(clk),
.rst_n(rst_n),
.offset(offset1),
.s_valid(in_valid),
.s_ready(ready),
.s_data(din),
.m_valid(valid_1to2),
.m_ready(ready_2to1),
.m_data(data_1to2)
);
sub i_sub2(
.clk(clk),
.rst_n(rst_n),
.offset(offset2),
.s_valid(valid_1to2),
.s_ready(ready_2to1),
.s_data(data_1to2),
.m_valid(valid_2to3),
.m_ready(ready_3to2),
.m_data(data_2to3)
);
sub i_sub3(
.clk(clk),
.rst_n(rst_n),
.offset(offset3),
.s_valid(valid_2to3),
.s_ready(ready_3to2),
.s_data(data_2to3),
.m_valid(out_valid),
.m_ready(1'b1),
.m_data(dout)
);
endmodule
sub.sv
module sub (
input clk,
input rst_n,
input [1:0] offset,
input s_valid,
output logic s_ready,
input [5:0] s_data,
output logic m_valid,
input m_ready,
output logic [5:0] m_data
);
assign s_ready = '1;
always_ff @(posedge clk, negedge rst_n) begin
if (!rst_n) begin
m_valid <= '0;
m_data <= '0;
end
else begin
if (s_valid && m_ready) begin
m_valid <= s_valid;
m_data <= s_data + offset;
end
end
end
endmodule
interfaceを使用することで記述量が減っていますね
コメント