【SystemVerilog】interfaceを使用して回路を作成する

Systemverilog
田中太郎
田中太郎

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を使用することで記述量が減っていますね

コメント

タイトルとURLをコピーしました