71 lines
2.2 KiB
Verilog
71 lines
2.2 KiB
Verilog
`timescale 1ns/1ps
|
||
|
||
// =============================================================================
|
||
// Multiply a value by a constant
|
||
// Use a shift-add algorithm instead of a multiplier
|
||
// parameters:
|
||
// -- W : data width
|
||
// -- C : constant
|
||
// inout:
|
||
// -- x : input of width W
|
||
// -- y : output of widht 2W
|
||
// =============================================================================
|
||
module mul_const_shiftadd#(
|
||
parameter integer W = 16,
|
||
parameter integer C = 16'sh7fff
|
||
)(
|
||
input wire signed [W-1:0] x,
|
||
output wire signed [2*W-1:0] y
|
||
);
|
||
// Sign and magnitude of constant
|
||
localparam integer C_NEG = (C < 0) ? 1 : 0;
|
||
localparam integer C_ABS = (C < 0) ? -C : C;
|
||
|
||
// MSB index of |C| (0-based). Keeps network minimal.
|
||
function integer msb_index;
|
||
input integer v;
|
||
integer i;
|
||
begin
|
||
msb_index = -1;
|
||
for (i = 0; i < 32; i = i + 1)
|
||
if (v >> i) msb_index = i;
|
||
end
|
||
endfunction
|
||
|
||
localparam integer I_MAX = (C_ABS == 0) ? 0 : msb_index(C_ABS);
|
||
|
||
// Width big enough for the largest partial product: W bits shifted by I_MAX
|
||
localparam integer PPW = W + I_MAX + 1;
|
||
|
||
// Pre-extend x to PPW so shifts don’t truncate high/sign bits
|
||
wire signed [PPW-1:0] x_ext = {{(PPW-W){x[W-1]}}, x};
|
||
|
||
// Partial products (only where C’s bit is 1)
|
||
wire signed [PPW-1:0] part [0:I_MAX];
|
||
genvar i;
|
||
generate
|
||
for (i = 0; i <= I_MAX; i = i + 1) begin : GEN_PARTS
|
||
assign part[i] = C_ABS[i] ? (x_ext <<< i) : {PPW{1'b0}};
|
||
end
|
||
endgenerate
|
||
|
||
// Adder chain (you can replace with a balanced tree for speed)
|
||
wire signed [PPW-1:0] sum [0:I_MAX];
|
||
generate
|
||
if (I_MAX == 0) begin
|
||
assign sum[0] = part[0];
|
||
end else begin
|
||
assign sum[0] = part[0];
|
||
for (i = 1; i <= I_MAX; i = i + 1) begin : GEN_SUM
|
||
assign sum[i] = sum[i-1] + part[i];
|
||
end
|
||
end
|
||
endgenerate
|
||
|
||
// Apply sign of C
|
||
wire signed [PPW-1:0] mag = (I_MAX == 0) ? part[0] : sum[I_MAX];
|
||
wire signed [PPW-1:0] prod = C_NEG ? -mag : mag;
|
||
|
||
// Stretch/extend to 2W result width
|
||
assign y = {{((2*W)-PPW){prod[PPW-1]}}, prod};
|
||
endmodule |