/* verilator lint_off MULTITOP */ /// =================== Unsigned, Fixed Point ========================= module std_fp_add #( parameter WIDTH = 32, parameter INT_WIDTH = 16, parameter FRAC_WIDTH = 16 ) ( input logic [WIDTH-1:0] left, input logic [WIDTH-1:0] right, output logic [WIDTH-1:0] out ); assign out = left + right; endmodule module std_fp_sub #( parameter WIDTH = 32, parameter INT_WIDTH = 16, parameter FRAC_WIDTH = 16 ) ( input logic [WIDTH-1:0] left, input logic [WIDTH-1:0] right, output logic [WIDTH-1:0] out ); assign out = left - right; endmodule module std_fp_mult_pipe #( parameter WIDTH = 32, parameter INT_WIDTH = 16, parameter FRAC_WIDTH = 16, parameter SIGNED = 0 ) ( input logic [WIDTH-1:0] left, input logic [WIDTH-1:0] right, input logic go, input logic clk, input logic reset, output logic [WIDTH-1:0] out, output logic done ); logic [WIDTH-1:0] rtmp; logic [WIDTH-1:0] ltmp; logic [(WIDTH << 1) - 1:0] out_tmp; // Buffer used to walk through the 3 cycles of the pipeline. logic done_buf[1:0]; assign done = done_buf[1]; assign out = out_tmp[(WIDTH << 1) - INT_WIDTH - 1 : WIDTH - INT_WIDTH]; // If the done buffer is completely empty and go is high then execution // just started. logic start; assign start = go; // Start sending the done signal. always_ff @(posedge clk) begin if (start) done_buf[0] <= 1; else done_buf[0] <= 0; end // Push the done signal through the pipeline. always_ff @(posedge clk) begin if (go) begin done_buf[1] <= done_buf[0]; end else begin done_buf[1] <= 0; end end // Register the inputs always_ff @(posedge clk) begin if (reset) begin rtmp <= 0; ltmp <= 0; end else if (go) begin if (SIGNED) begin rtmp <= $signed(right); ltmp <= $signed(left); end else begin rtmp <= right; ltmp <= left; end end else begin rtmp <= 0; ltmp <= 0; end end // Compute the output and save it into out_tmp always_ff @(posedge clk) begin if (reset) begin out_tmp <= 0; end else if (go) begin if (SIGNED) begin // In the first cycle, this performs an invalid computation because // ltmp and rtmp only get their actual values in cycle 1 out_tmp <= $signed( { {WIDTH{ltmp[WIDTH-1]}}, ltmp} * { {WIDTH{rtmp[WIDTH-1]}}, rtmp} ); end else begin out_tmp <= ltmp * rtmp; end end else begin out_tmp <= out_tmp; end end endmodule /* verilator lint_off WIDTH */ module std_fp_div_pipe #( parameter WIDTH = 32, parameter INT_WIDTH = 16, parameter FRAC_WIDTH = 16 ) ( input logic go, input logic clk, input logic reset, input logic [WIDTH-1:0] left, input logic [WIDTH-1:0] right, output logic [WIDTH-1:0] out_remainder, output logic [WIDTH-1:0] out_quotient, output logic done ); localparam ITERATIONS = WIDTH + FRAC_WIDTH; logic [WIDTH-1:0] quotient, quotient_next; logic [WIDTH:0] acc, acc_next; logic [$clog2(ITERATIONS)-1:0] idx; logic start, running, finished, dividend_is_zero; assign start = go && !running; assign dividend_is_zero = start && left == 0; assign finished = idx == ITERATIONS - 1 && running; always_ff @(posedge clk) begin if (reset || finished || dividend_is_zero) running <= 0; else if (start) running <= 1; else running <= running; end always_comb begin if (acc >= {1'b0, right}) begin acc_next = acc - right; {acc_next, quotient_next} = {acc_next[WIDTH-1:0], quotient, 1'b1}; end else begin {acc_next, quotient_next} = {acc, quotient} << 1; end end // `done` signaling always_ff @(posedge clk) begin if (dividend_is_zero || finished) done <= 1; else done <= 0; end always_ff @(posedge clk) begin if (running) idx <= idx + 1; else idx <= 0; end always_ff @(posedge clk) begin if (reset) begin out_quotient <= 0; out_remainder <= 0; end else if (start) begin out_quotient <= 0; out_remainder <= left; end else if (go == 0) begin out_quotient <= out_quotient; out_remainder <= out_remainder; end else if (dividend_is_zero) begin out_quotient <= 0; out_remainder <= 0; end else if (finished) begin out_quotient <= quotient_next; out_remainder <= out_remainder; end else begin out_quotient <= out_quotient; if (right <= out_remainder) out_remainder <= out_remainder - right; else out_remainder <= out_remainder; end end always_ff @(posedge clk) begin if (reset) begin acc <= 0; quotient <= 0; end else if (start) begin {acc, quotient} <= {{WIDTH{1'b0}}, left, 1'b0}; end else begin acc <= acc_next; quotient <= quotient_next; end end endmodule module std_fp_gt #( parameter WIDTH = 32, parameter INT_WIDTH = 16, parameter FRAC_WIDTH = 16 ) ( input logic [WIDTH-1:0] left, input logic [WIDTH-1:0] right, output logic out ); assign out = left > right; endmodule /// =================== Signed, Fixed Point ========================= module std_fp_sadd #( parameter WIDTH = 32, parameter INT_WIDTH = 16, parameter FRAC_WIDTH = 16 ) ( input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed [WIDTH-1:0] out ); assign out = $signed(left + right); endmodule module std_fp_ssub #( parameter WIDTH = 32, parameter INT_WIDTH = 16, parameter FRAC_WIDTH = 16 ) ( input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed [WIDTH-1:0] out ); assign out = $signed(left - right); endmodule module std_fp_smult_pipe #( parameter WIDTH = 32, parameter INT_WIDTH = 16, parameter FRAC_WIDTH = 16 ) ( input [WIDTH-1:0] left, input [WIDTH-1:0] right, input logic reset, input logic go, input logic clk, output logic [WIDTH-1:0] out, output logic done ); std_fp_mult_pipe #( .WIDTH(WIDTH), .INT_WIDTH(INT_WIDTH), .FRAC_WIDTH(FRAC_WIDTH), .SIGNED(1) ) comp ( .clk(clk), .done(done), .reset(reset), .go(go), .left(left), .right(right), .out(out) ); endmodule module std_fp_sdiv_pipe #( parameter WIDTH = 32, parameter INT_WIDTH = 16, parameter FRAC_WIDTH = 16 ) ( input clk, input go, input reset, input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed [WIDTH-1:0] out_quotient, output signed [WIDTH-1:0] out_remainder, output logic done ); logic signed [WIDTH-1:0] left_abs, right_abs, comp_out_q, comp_out_r, right_save, out_rem_intermediate; // Registers to figure out how to transform outputs. logic different_signs, left_sign, right_sign; // Latch the value of control registers so that their available after // go signal becomes low. always_ff @(posedge clk) begin if (go) begin right_save <= right_abs; left_sign <= left[WIDTH-1]; right_sign <= right[WIDTH-1]; end else begin left_sign <= left_sign; right_save <= right_save; right_sign <= right_sign; end end assign right_abs = right[WIDTH-1] ? -right : right; assign left_abs = left[WIDTH-1] ? -left : left; assign different_signs = left_sign ^ right_sign; assign out_quotient = different_signs ? -comp_out_q : comp_out_q; // Remainder is computed as: // t0 = |left| % |right| // t1 = if left * right < 0 and t0 != 0 then |right| - t0 else t0 // rem = if right < 0 then -t1 else t1 assign out_rem_intermediate = different_signs & |comp_out_r ? $signed(right_save - comp_out_r) : comp_out_r; assign out_remainder = right_sign ? -out_rem_intermediate : out_rem_intermediate; std_fp_div_pipe #( .WIDTH(WIDTH), .INT_WIDTH(INT_WIDTH), .FRAC_WIDTH(FRAC_WIDTH) ) comp ( .reset(reset), .clk(clk), .done(done), .go(go), .left(left_abs), .right(right_abs), .out_quotient(comp_out_q), .out_remainder(comp_out_r) ); endmodule module std_fp_sgt #( parameter WIDTH = 32, parameter INT_WIDTH = 16, parameter FRAC_WIDTH = 16 ) ( input logic signed [WIDTH-1:0] left, input logic signed [WIDTH-1:0] right, output logic signed out ); assign out = $signed(left > right); endmodule module std_fp_slt #( parameter WIDTH = 32, parameter INT_WIDTH = 16, parameter FRAC_WIDTH = 16 ) ( input logic signed [WIDTH-1:0] left, input logic signed [WIDTH-1:0] right, output logic signed out ); assign out = $signed(left < right); endmodule /// =================== Unsigned, Bitnum ========================= module std_mult_pipe #( parameter WIDTH = 32 ) ( input logic [WIDTH-1:0] left, input logic [WIDTH-1:0] right, input logic reset, input logic go, input logic clk, output logic [WIDTH-1:0] out, output logic done ); std_fp_mult_pipe #( .WIDTH(WIDTH), .INT_WIDTH(WIDTH), .FRAC_WIDTH(0), .SIGNED(0) ) comp ( .reset(reset), .clk(clk), .done(done), .go(go), .left(left), .right(right), .out(out) ); endmodule module std_div_pipe #( parameter WIDTH = 32 ) ( input reset, input clk, input go, input [WIDTH-1:0] left, input [WIDTH-1:0] right, output logic [WIDTH-1:0] out_remainder, output logic [WIDTH-1:0] out_quotient, output logic done ); logic [WIDTH-1:0] dividend; logic [(WIDTH-1)*2:0] divisor; logic [WIDTH-1:0] quotient; logic [WIDTH-1:0] quotient_msk; logic start, running, finished, dividend_is_zero; assign start = go && !running; assign finished = quotient_msk == 0 && running; assign dividend_is_zero = start && left == 0; always_ff @(posedge clk) begin // Early return if the divisor is zero. if (finished || dividend_is_zero) done <= 1; else done <= 0; end always_ff @(posedge clk) begin if (reset || finished || dividend_is_zero) running <= 0; else if (start) running <= 1; else running <= running; end // Outputs always_ff @(posedge clk) begin if (dividend_is_zero || start) begin out_quotient <= 0; out_remainder <= 0; end else if (finished) begin out_quotient <= quotient; out_remainder <= dividend; end else begin // Otherwise, explicitly latch the values. out_quotient <= out_quotient; out_remainder <= out_remainder; end end // Calculate the quotient mask. always_ff @(posedge clk) begin if (start) quotient_msk <= 1 << WIDTH - 1; else if (running) quotient_msk <= quotient_msk >> 1; else quotient_msk <= quotient_msk; end // Calculate the quotient. always_ff @(posedge clk) begin if (start) quotient <= 0; else if (divisor <= dividend) quotient <= quotient | quotient_msk; else quotient <= quotient; end // Calculate the dividend. always_ff @(posedge clk) begin if (start) dividend <= left; else if (divisor <= dividend) dividend <= dividend - divisor; else dividend <= dividend; end always_ff @(posedge clk) begin if (start) begin divisor <= right << WIDTH - 1; end else if (finished) begin divisor <= 0; end else begin divisor <= divisor >> 1; end end // Simulation self test against unsynthesizable implementation. `ifdef VERILATOR logic [WIDTH-1:0] l, r; always_ff @(posedge clk) begin if (go) begin l <= left; r <= right; end else begin l <= l; r <= r; end end always @(posedge clk) begin if (done && $unsigned(out_remainder) != $unsigned(l % r)) $error( "\nstd_div_pipe (Remainder): Computed and golden outputs do not match!\n", "left: %0d", $unsigned(l), " right: %0d\n", $unsigned(r), "expected: %0d", $unsigned(l % r), " computed: %0d", $unsigned(out_remainder) ); if (done && $unsigned(out_quotient) != $unsigned(l / r)) $error( "\nstd_div_pipe (Quotient): Computed and golden outputs do not match!\n", "left: %0d", $unsigned(l), " right: %0d\n", $unsigned(r), "expected: %0d", $unsigned(l / r), " computed: %0d", $unsigned(out_quotient) ); end `endif endmodule /// =================== Signed, Bitnum ========================= module std_sadd #( parameter WIDTH = 32 ) ( input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed [WIDTH-1:0] out ); assign out = $signed(left + right); endmodule module std_ssub #( parameter WIDTH = 32 ) ( input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed [WIDTH-1:0] out ); assign out = $signed(left - right); endmodule module std_smult_pipe #( parameter WIDTH = 32 ) ( input logic reset, input logic go, input logic clk, input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output logic signed [WIDTH-1:0] out, output logic done ); std_fp_mult_pipe #( .WIDTH(WIDTH), .INT_WIDTH(WIDTH), .FRAC_WIDTH(0), .SIGNED(1) ) comp ( .reset(reset), .clk(clk), .done(done), .go(go), .left(left), .right(right), .out(out) ); endmodule /* verilator lint_off WIDTH */ module std_sdiv_pipe #( parameter WIDTH = 32 ) ( input reset, input clk, input go, input logic signed [WIDTH-1:0] left, input logic signed [WIDTH-1:0] right, output logic signed [WIDTH-1:0] out_quotient, output logic signed [WIDTH-1:0] out_remainder, output logic done ); logic signed [WIDTH-1:0] left_abs, right_abs, comp_out_q, comp_out_r, right_save, out_rem_intermediate; // Registers to figure out how to transform outputs. logic different_signs, left_sign, right_sign; // Latch the value of control registers so that their available after // go signal becomes low. always_ff @(posedge clk) begin if (go) begin right_save <= right_abs; left_sign <= left[WIDTH-1]; right_sign <= right[WIDTH-1]; end else begin left_sign <= left_sign; right_save <= right_save; right_sign <= right_sign; end end assign right_abs = right[WIDTH-1] ? -right : right; assign left_abs = left[WIDTH-1] ? -left : left; assign different_signs = left_sign ^ right_sign; assign out_quotient = different_signs ? -comp_out_q : comp_out_q; // Remainder is computed as: // t0 = |left| % |right| // t1 = if left * right < 0 and t0 != 0 then |right| - t0 else t0 // rem = if right < 0 then -t1 else t1 assign out_rem_intermediate = different_signs & |comp_out_r ? $signed(right_save - comp_out_r) : comp_out_r; assign out_remainder = right_sign ? -out_rem_intermediate : out_rem_intermediate; std_div_pipe #( .WIDTH(WIDTH) ) comp ( .reset(reset), .clk(clk), .done(done), .go(go), .left(left_abs), .right(right_abs), .out_quotient(comp_out_q), .out_remainder(comp_out_r) ); // Simulation self test against unsynthesizable implementation. `ifdef VERILATOR logic signed [WIDTH-1:0] l, r; always_ff @(posedge clk) begin if (go) begin l <= left; r <= right; end else begin l <= l; r <= r; end end always @(posedge clk) begin if (done && out_quotient != $signed(l / r)) $error( "\nstd_sdiv_pipe (Quotient): Computed and golden outputs do not match!\n", "left: %0d", l, " right: %0d\n", r, "expected: %0d", $signed(l / r), " computed: %0d", $signed(out_quotient), ); if (done && out_remainder != $signed(((l % r) + r) % r)) $error( "\nstd_sdiv_pipe (Remainder): Computed and golden outputs do not match!\n", "left: %0d", l, " right: %0d\n", r, "expected: %0d", $signed(((l % r) + r) % r), " computed: %0d", $signed(out_remainder), ); end `endif endmodule module std_sgt #( parameter WIDTH = 32 ) ( input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed out ); assign out = $signed(left > right); endmodule module std_slt #( parameter WIDTH = 32 ) ( input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed out ); assign out = $signed(left < right); endmodule module std_seq #( parameter WIDTH = 32 ) ( input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed out ); assign out = $signed(left == right); endmodule module std_sneq #( parameter WIDTH = 32 ) ( input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed out ); assign out = $signed(left != right); endmodule module std_sge #( parameter WIDTH = 32 ) ( input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed out ); assign out = $signed(left >= right); endmodule module std_sle #( parameter WIDTH = 32 ) ( input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed out ); assign out = $signed(left <= right); endmodule module std_slsh #( parameter WIDTH = 32 ) ( input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed [WIDTH-1:0] out ); assign out = left <<< right; endmodule module std_srsh #( parameter WIDTH = 32 ) ( input signed [WIDTH-1:0] left, input signed [WIDTH-1:0] right, output signed [WIDTH-1:0] out ); assign out = left >>> right; endmodule // Signed extension module std_signext #( parameter IN_WIDTH = 32, parameter OUT_WIDTH = 32 ) ( input wire logic [IN_WIDTH-1:0] in, output logic [OUT_WIDTH-1:0] out ); localparam EXTEND = OUT_WIDTH - IN_WIDTH; assign out = { {EXTEND {in[IN_WIDTH-1]}}, in}; `ifdef VERILATOR always_comb begin if (IN_WIDTH > OUT_WIDTH) $error( "std_signext: Output width less than input width\n", "IN_WIDTH: %0d", IN_WIDTH, "OUT_WIDTH: %0d", OUT_WIDTH ); end `endif endmodule