ROM乗算器

9177 ワード

きほんアルゴリズム
ROM乗算器のアルゴリズムは、乗算の結果を1つのROMで保存し、演算が必要なときに対応するアドレスに直接テーブルを調べるだけで簡単です.例えば2つの4ビットの2進数の乗算a*bを計算すると、8ビット入力8ビット出力のROMが計算結果を記憶する必要があり、そのアドレスと記憶データとの関係は、アドレス{a,b}(ビットパッチ)がa*b(例えばアドレス8'b00010010が記憶する結果が0001*0001=8'b00000010)を記憶する場合に用いるROMが比較的大きい、従って、タイミング要求が厳しくない場合には、クロックで面積を変えることができ、例えば8ビット*8ビットのROM乗算器については、乗数1の高さ4ビット、低4ビット、乗数2の高さ4ビットの2つの乗算に分解する.高4ビットと高4ビットを乗算した結果を左に4ビットシフトし、高4ビットと低4ビットを乗算した結果を左に2ビットシフトし、低4ビットと低4ビットを乗算した結果を一定に累積しない(手算乗算でよく使われるパターン)4クロックサイクル後に結果を得ることができ、使用するROMは16*16から4*4に下げることができる
シングルROM乗算器
Pythonジェネレータ
単一ROMはVerilogでcase文シミュレーションが可能で、手書きのような繰り返し化の高いcase文は間違いなく効率の低い方法であり、今回はPython文を用いて生成した
class ROMGenerator(object):
    """docstring for ROMGenerator"""

    def __init__(self, Width):
        super(ROMGenerator, self).__init__()
        self.Width = Width

    def GeneratorROM(self, FileName):
        RomContent = ["""
module ROM_%s (
    input [%s:0]addr,
    output reg [%s:0]dout
);

always @(*) begin
    case(addr)\
""" % (self.Width, self.Width * 2 - 1, self.Width * 2 - 1)]
        for i in range(2 ** self.Width):
            for j in range(2 ** self.Width):
                RomContent.append(
                    "\t\t%s\'d%s:dout = %s\'d%s;" %
                    (2 * self.Width, i * (2 ** self.Width) + j,
                        2 * self.Width, i * j))
        RomContent.append("""\t\tdefault:dout = \'b0;
    endcase
end
endmodule
""")
        with open("./%s.v" % FileName, "w") as filepoint:
            filepoint.write("
".join(RomContent)) return "
".join(RomContent) if __name__ == '__main__': test = ROMGenerator(4) print(test.GeneratorROM("ROM_4"))

コードは簡単で、先頭と末尾を除いて、バッチ化生成に必要な\t\t%s\'d%s:dout = %s\'d%s;です.
テストプラットフォーム
テスト時にSystemVerilogで作成したテストプラットフォームを使用し、*演算子を使用して自分のモジュールの出力と比較します.
module mult_tb (
);

parameter WIDTH = 4;

logic clk,rst_n;
logic [WIDTH - 1:0]multiplier1;
logic [WIDTH - 1:0]multiplier2;

logic [2 * WIDTH - 1:0]product;

ROM_4 dut(
    .addr({multiplier1,multiplier2}),
    .dout(product)
);

initial begin
    clk = 1'b0;
    forever begin
        #50 clk = ~clk;
    end
end

initial begin
    rst_n = 1'b1;
    #5 rst_n = 1'b0;
    #10 rst_n = 1'b1;
end

initial begin
    {multiplier1,multiplier2} = 'b0;
    repeat(100) begin
        @(negedge clk);
        multiplier1 = (WIDTH)'($urandom_range(0,2 ** WIDTH));
        multiplier2 = (WIDTH)'($urandom_range(0,2 ** WIDTH));
    end
    $stop();
end

logic [2 * WIDTH - 1:0]exp;
initial begin
    exp = 'b0;
    forever begin
        @(posedge clk);
        exp = multiplier1 * multiplier2;
        if(exp == product) begin
            $display("successful");
        end else begin
            $display("fail");
        end
    end
end
endmodule

タイムシェアリングROM乗算器
RTLコード
コアセクション
module serial_multrom_mult_core #(
    parameter HALF_WIDTH = 4
)(
    input clk,    // Clock
    input rst_n,  // Asynchronous reset active low

    input [2 * HALF_WIDTH - 1:0]mult1,mult2,

    input start,
    input [2 * HALF_WIDTH - 1:0]rom_dout,
    output reg [2 * HALF_WIDTH - 1:0]rom_address,
    output reg [4 * HALF_WIDTH - 1:0]dout
);

parameter INIT = 1'b0,
          WORK = 1'b1;
reg mode;
reg [1:0]counte_4_decay2;
always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        mode <= 1'b0;
    end else begin
        case (mode)
            INIT:begin
                if(start == 1'b1) begin
                    mode <= WORK;
                end else begin
                    mode <= INIT;
                end
            end
            WORK:begin
                if(counte_4_decay2 == 2'd3) begin
                    mode <= INIT;
                end else begin
                    mode <= WORK;
                end
            end
            default:mode <= INIT;
        endcase
    end
end

ここまではステートマシンの状態部分であり、開始信号が有効であると状態がWORKになり、演算終了状態がINITになる.
reg [1:0]counte_4;
always @(posedge clk or negedge rst_n) begin : proc_counte_4
    if(~rst_n) begin
        counte_4 <= 'b0;
    end else if(mode == WORK)begin
        counte_4 <= counte_4 + 1'b1;
    end else begin
        counte_4 <= 'b0;
    end
end

reg [2 * HALF_WIDTH - 1:0]mult1_lock,mult2_lock;
always @(posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        {mult1_lock,mult2_lock} <= 'b0;
    end else if(start == 1'b1)begin
        {mult1_lock,mult2_lock} <= {mult1,mult2};
    end else begin
        {mult1_lock,mult2_lock} <= {mult1_lock,mult2_lock};
    end
end

reg [1:0]counte_4_decay;
always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        {rom_address,counte_4_decay} <= 'b0;
    end else if(start == 1'b1) begin
        {rom_address,counte_4_decay} <= 'b0;
    end else begin
        case (counte_4)
            2'd0:rom_address <= {mult1_lock[HALF_WIDTH - 1:0],mult2_lock[HALF_WIDTH - 1:0]};
            2'd1:rom_address <= {mult1_lock[2 * HALF_WIDTH - 1:HALF_WIDTH],mult2_lock[HALF_WIDTH - 1:0]};
            2'd2:rom_address <= {mult1_lock[HALF_WIDTH - 1:0],mult2_lock[2 * HALF_WIDTH - 1:HALF_WIDTH]};
            2'd3:rom_address <= {mult1_lock[2 * HALF_WIDTH - 1:HALF_WIDTH],mult2_lock[2 * HALF_WIDTH - 1:HALF_WIDTH]};
            default:rom_address <= 'b0;
        endcase
        counte_4_decay <= counte_4;
    end
end

以上が入力制御部であり、乗数1が高い4ビット低い4ビット、乗数2が高い4ビット低い4ビットをつなぎ合わせてROMに送り込み、積を取得する
wire [4 * HALF_WIDTH - 1:0]rom_dout_ex = '{rom_dout};
reg [4 * HALF_WIDTH - 1:0]rom_dout_lock;

always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        {rom_dout_lock,counte_4_decay2} <= 'b0;
    end else if(start == 1'b1) begin
        {rom_dout_lock,counte_4_decay2} <= 'b0;
    end else begin
        case (counte_4_decay)
            2'd0:rom_dout_lock <= rom_dout_ex;
            2'd1:rom_dout_lock <= rom_dout_ex << HALF_WIDTH;
            2'd2:rom_dout_lock <= rom_dout_ex << HALF_WIDTH;
            2'd3:rom_dout_lock <= rom_dout_ex << (2 * HALF_WIDTH);
            default:rom_dout_lock <= 'b0;
        endcase
        counte_4_decay2 <= counte_4_decay;
    end
end

always @ (posedge clk or negedge rst_n) begin
    if(~rst_n) begin
        dout <= 'b0;
    end else if(mode == WORK) begin
        dout <= dout + rom_dout_lock;
    end else if(start == 1'b1) begin
        dout <= 'b0;
    end else begin
        dout <= dout;
    end
end

endmodule

ROMからデータを取得した後、乗数組成で該当ビット数だけシフトして加算
最上位セクション
module serial_multrom_mult_top #(
    parameter HALF_WIDTH = 2
)(
    input clk,    // Clock
    input rst_n,  // Asynchronous reset active low

    input start,
    input [2 * HALF_WIDTH - 1:0]mult1,mult2,
    output [4 * HALF_WIDTH - 1:0]dout
);

wire [2 * HALF_WIDTH - 1:0]rom_dout;
wire [2 * HALF_WIDTH - 1:0]rom_address;
serial_multrom_mult_core #(
    .HALF_WIDTH(HALF_WIDTH)
) u_serial_multrom_mult_core (
    .clk(clk),    // Clock
    .rst_n(rst_n),  // Asynchronous reset active low

    .mult1(mult1),
    .mult2(mult2),

    .start(start),
    .rom_dout(rom_dout),
    .rom_address(rom_address),
    .dout(dout)
);

ROM_4 u_ROM_4(
    .addr(rom_address),
    .dout(rom_dout)
);
endmodule

Testbench
testbenchは、単一ROMのTestbenchがクロックや開始信号を加えるなど改良されている
`timescale 1ns/1ps
module mult_tb (
);

parameter HALF_WIDTH = 4;
parameter WIDTH = HALF_WIDTH * 2;

logic clk,rst_n;
logic start;
logic [WIDTH - 1:0]multiplier1;
logic [WIDTH - 1:0]multiplier2;

logic [2 * WIDTH - 1:0]product;

serial_multrom_mult_top #(
    .HALF_WIDTH(HALF_WIDTH)
) dut (
    .clk(clk),    // Clock
    .rst_n(rst_n),  // Asynchronous reset active low

    .start(start),
    .mult1(multiplier1),
    .mult2(multiplier2),
    .dout(product)
);

initial begin
    clk = 1'b0;
    forever begin
        #50 clk = ~clk;
    end
end

initial begin
    rst_n = 1'b1;
    #5 rst_n = 1'b0;
    #10 rst_n = 1'b1;
end

logic [2 * WIDTH - 1:0]exp;
initial begin
    {multiplier1,multiplier2} = 'b0;
    repeat(100) begin
        @(negedge clk);
        start = 1'b1;
        multiplier1 = (WIDTH)'($urandom_range(0,2 ** WIDTH));
        multiplier2 = (WIDTH)'($urandom_range(0,2 ** WIDTH));
        exp = multiplier1 * multiplier2;
        repeat(12) begin
            @(negedge clk);
            start = 'b0;
        end
        if(product == exp) begin
            $display("successful");
        end else begin
            $display("fail");
        end
    end
    $stop();
end

endmodule

なお、modelsimシミュレーションを使用するとエラーコード211が発生し、波形最適化機能をオフにすると正常にシミュレーションできる