ROM乗算器
9177 ワード
きほんアルゴリズム
ROM乗算器のアルゴリズムは、乗算の結果を1つのROMで保存し、演算が必要なときに対応するアドレスに直接テーブルを調べるだけで簡単です.例えば2つの4ビットの2進数の乗算
シングルROM乗算器
Pythonジェネレータ
単一ROMはVerilogでcase文シミュレーションが可能で、手書きのような繰り返し化の高いcase文は間違いなく効率の低い方法であり、今回はPython文を用いて生成した
コードは簡単で、先頭と末尾を除いて、バッチ化生成に必要な
テストプラットフォーム
テスト時にSystemVerilogで作成したテストプラットフォームを使用し、
タイムシェアリングROM乗算器
RTLコード
コアセクション
ここまではステートマシンの状態部分であり、開始信号が有効であると状態が
以上が入力制御部であり、乗数1が高い4ビット低い4ビット、乗数2が高い4ビット低い4ビットをつなぎ合わせてROMに送り込み、積を取得する
ROMからデータを取得した後、乗数組成で該当ビット数だけシフトして加算
最上位セクション
Testbench
testbenchは、単一ROMのTestbenchがクロックや開始信号を加えるなど改良されている
なお、modelsimシミュレーションを使用するとエラーコード
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
が発生し、波形最適化機能をオフにすると正常にシミュレーションできる