65 lines
2.0 KiB
Verilog
65 lines
2.0 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
|
|
);
|
|
// Absolute value and sign of C
|
|
localparam integer C_NEG = (C < 0) ? 1 : 0;
|
|
localparam integer C_ABS = (C < 0) ? -C : C;
|
|
|
|
// Find MSB index of C_ABS to size the network
|
|
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);
|
|
|
|
// Partial products
|
|
wire signed [W+I_MAX: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]) ? ($signed(x) <<< i) : { (W+I_MAX+1){1'b0} };
|
|
end
|
|
endgenerate
|
|
|
|
// Adder chain (simple; replace with tree if you want higher performance)
|
|
wire signed [W+I_MAX: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 [W+I_MAX:0] mag = (I_MAX==0) ? part[0] : sum[I_MAX];
|
|
wire signed [W+I_MAX:0] prod = C_NEG ? -mag : mag;
|
|
|
|
// Stretch to fixed y width (truncate/extend as you wish outside)
|
|
assign y = prod;
|
|
|
|
endmodule |