// Approximate Lower-part OR Adder (LoA)
module loa_adder16 #(parameter LOWER_WIDTH = 3)(
input [15:0] a,
input [15:0] b,
output [15:0] sum
);
wire [LOWER_WIDTH-1:0] lower_sum;
wire [15:LOWER_WIDTH] upper_sum;
// Use OR for lower bits (approximate)
assign lower_sum = a[LOWER_WIDTH-1:0] | b[LOWER_WIDTH-1:0];
// Accurate addition for upper bits
assign upper_sum = a[15:LOWER_WIDTH] + b[15:LOWER_WIDTH];
// Form final 16-bit sum
assign sum = {upper_sum, lower_sum};
endmodule
// Multiply-Accumulate (MAC) unit using LoA in partial accumulator updates
module mac_16bit_loa (
input wire clk,
input wire rst,
input wire start,
input wire [15:0] a,
input wire [15:0] b,
output reg done,
output reg [31:0] acc
);
reg [4:0] bit_index;
reg processing;
reg [31:0] partial_product;
wire [15:0] loa_sum;
// LoA instance — approximates lower 3 bits of acc update
loa_adder16 #(.LOWER_WIDTH(3)) loa_inst (
.a(acc[15:0]),
.b(partial_product[15:0]),
.sum(loa_sum)
);
always @(posedge clk or posedge rst) begin
if (rst) begin
acc <= 0;
bit_index <= 0;
processing <= 0;
done <= 0;
end else begin
if (start && !processing) begin
// Begin new MAC operation
acc <= 0;
bit_index <= 0;
done <= 0;
processing <= 1;
end else if (processing) begin
if (bit_index < 16) begin
if (a[bit_index]) begin
// Generate partial product by shifting
partial_product = b << bit_index;
// Approximate-add lower 16 bits, exact-add upper 16 bits
acc[15:0] <= loa_sum;
acc[31:16] <= acc[31:16] + partial_product[31:16];
end
bit_index <= bit_index + 1;
end else begin
// MAC complete
processing <= 0;
done <= 1;
end
end
end
end
endmodule
module mac_16bit_loa_tb;
reg clk, rst, start;
reg [15:0] a, b;
wire [31:0] acc;
wire done;
mac_16bit_loa dut (
.clk(clk),
.rst(rst),
.start(start),
.a(a),
.b(b),
.done(done),
.acc(acc)
);
always #5 clk = ~clk;
integer i, j;
reg [31:0] expected;
integer total = 0, pass = 0, fail = 0;
integer curr_error, max_error = 0;
integer timeout;
time start_time, end_time;
initial begin
$dumpfile("mac_16bit_loa_tb.vcd");
$dumpvars(0, mac_16bit_loa_tb);
end
initial begin
$display("======= TB: MAC_16BIT_LOA (Lower LOA Adder) =======");
clk = 0;
rst = 1;
start = 0;
a = 0;
b = 0;
#15 rst = 0;
start_time = $time;
for (i = 0; i < 16; i = i + 1) begin
for (j = 0; j < 16; j = j + 1) begin
rst = 1; @(posedge clk);
rst = 0; @(posedge clk);
a = i;
b = j;
start = 1; @(posedge clk);
start = 0;
timeout = 0;
while (!done && timeout < 1000) begin
@(posedge clk);
timeout = timeout + 1;
end
expected = i * j;
total = total + 1;
if (timeout >= 1000) begin
$display("TIMEOUT: a=%0d, b=%0d", a, b);
fail = fail + 1;
end else begin
curr_error = (acc > expected) ? acc - expected : expected - acc;
if (curr_error > max_error)
max_error = curr_error;
if (curr_error > 12) begin
$display("FAIL: a=%0d, b=%0d | acc=%0d, expected=%0d, error=%0d",
a, b, acc, expected, curr_error);
fail = fail + 1;
end else begin
$display("PASS: a=%0d, b=%0d | acc=%0d ≈ expected=%0d (error=%0d)",
a, b, acc, expected, curr_error);
pass = pass + 1;
end
end
@(posedge clk);
end
end
end_time = $time;
$display("===================================================");
$display("Passed : %0d", pass);
$display("Failed : %0d", fail);
$display("Total Cases : %0d", total);
$display("Efficiency : %0.2f%%", (pass * 100.0) / total);
$display("Max Error Seen : %0d", max_error);
$display("Time Taken : %0t ns", end_time - start_time);
$display("===================================================");
$finish;
end
endmodule