Remove op_b (rs2) register from muldiv_seq for modest LUT/FF savings
This commit is contained in:
parent
d3667769d2
commit
aa438fc37c
|
@ -3,14 +3,14 @@
|
||||||
| SPDX-License-Identifier: Apache-2.0 |
|
| SPDX-License-Identifier: Apache-2.0 |
|
||||||
\*****************************************************************************/
|
\*****************************************************************************/
|
||||||
|
|
||||||
// Combined multiply/divide/modulo circuit.
|
// Combined multiply/divide/modulo circuit. All operations performed at 1 bit
|
||||||
// All operations performed at 1 bit per clock; aiming for minimal resource usage.
|
// per clock; aiming for minimal resource usage on iCE40 FPGA. Optionally the
|
||||||
// There are lots of opportunities for off-by-one errors here. See muldiv_model.py
|
// circuit can be unrolled for slightly higher performance.
|
||||||
// for a simple reference model of the mul/div/mod iterations.
|
|
||||||
//
|
//
|
||||||
// When op_kill is high, the current calculation halts immediately. op_vld can be
|
// When op_kill is high, the current calculation halts immediately. op_vld can
|
||||||
// asserted on the same cycle, and the new calculation begins without delay, regardless
|
// be asserted on the same cycle, and the new calculation begins without
|
||||||
// of op_rdy. This may be used by the processor on e.g. mispredict or trap.
|
// delay, regardless of op_rdy. This may be used by the processor on e.g.
|
||||||
|
// mispredict or trap.
|
||||||
//
|
//
|
||||||
// The actual multiply/divide hardware is unsigned. We handle signedness at
|
// The actual multiply/divide hardware is unsigned. We handle signedness at
|
||||||
// input/output.
|
// input/output.
|
||||||
|
@ -57,10 +57,11 @@ parameter W_CTR = $clog2(XLEN + 1);
|
||||||
|
|
||||||
reg [W_MULOP-1:0] op_r;
|
reg [W_MULOP-1:0] op_r;
|
||||||
reg [2*XLEN-1:0] accum;
|
reg [2*XLEN-1:0] accum;
|
||||||
reg [XLEN-1:0] op_b_r;
|
|
||||||
reg op_a_neg_r;
|
reg op_a_neg_r;
|
||||||
reg op_b_neg_r;
|
reg op_b_neg_r;
|
||||||
|
|
||||||
|
reg prenegate_op_b;
|
||||||
|
|
||||||
wire op_a_signed =
|
wire op_a_signed =
|
||||||
op_r == M_OP_MULH ||
|
op_r == M_OP_MULH ||
|
||||||
op_r == M_OP_MULHSU ||
|
op_r == M_OP_MULHSU ||
|
||||||
|
@ -73,13 +74,16 @@ wire op_b_signed =
|
||||||
op_r == M_OP_REM;
|
op_r == M_OP_REM;
|
||||||
|
|
||||||
wire op_a_neg = op_a_signed && accum[XLEN-1];
|
wire op_a_neg = op_a_signed && accum[XLEN-1];
|
||||||
wire op_b_neg = op_b_signed && op_b_r[XLEN-1];
|
wire op_b_neg = op_b_signed && op_b[XLEN-1];
|
||||||
|
|
||||||
// Non-divide parts of the circuit should be constant-folded if all the MUL
|
// Non-divide parts of the circuit should be constant-folded if all the MUL
|
||||||
// operations are handled by the fast multiplier
|
// operations are handled by the fast multiplier
|
||||||
|
|
||||||
wire is_div = op_r[2] || (MUL_FAST && MULH_FAST);
|
wire is_div = op_r[2] || (MUL_FAST && MULH_FAST);
|
||||||
|
|
||||||
|
|
||||||
|
wire [XLEN-1:0] op_b_sign_adj = prenegate_op_b ? -op_b : op_b;
|
||||||
|
|
||||||
// Controls for modifying sign of all/part of accumulator
|
// Controls for modifying sign of all/part of accumulator
|
||||||
wire accum_neg_l;
|
wire accum_neg_l;
|
||||||
wire accum_inv_h;
|
wire accum_inv_h;
|
||||||
|
@ -103,7 +107,7 @@ always @ (*) begin: alu
|
||||||
addsub_tmp = {2*XLEN{1'b0}};
|
addsub_tmp = {2*XLEN{1'b0}};
|
||||||
neg_l_borrow = 1'b0;
|
neg_l_borrow = 1'b0;
|
||||||
for (i = 0; i < MULDIV_UNROLL; i = i + 1) begin
|
for (i = 0; i < MULDIV_UNROLL; i = i + 1) begin
|
||||||
addend = {is_div && |op_b_r, op_b_r, {XLEN-1{1'b0}}};
|
addend = {is_div && |op_b_sign_adj, op_b_sign_adj, {XLEN-1{1'b0}}};
|
||||||
shift_tmp = is_div ? accum_next : accum_next >> 1;
|
shift_tmp = is_div ? accum_next : accum_next >> 1;
|
||||||
addsub_tmp = shift_tmp + addend;
|
addsub_tmp = shift_tmp + addend;
|
||||||
accum_next = (is_div ? !addsub_tmp[2 * XLEN - 1] : accum_next[0]) ?
|
accum_next = (is_div ? !addsub_tmp[2 * XLEN - 1] : accum_next[0]) ?
|
||||||
|
@ -138,7 +142,7 @@ always @ (posedge clk or negedge rst_n) begin
|
||||||
op_r <= {W_MULOP{1'b0}};
|
op_r <= {W_MULOP{1'b0}};
|
||||||
op_a_neg_r <= 1'b0;
|
op_a_neg_r <= 1'b0;
|
||||||
op_b_neg_r <= 1'b0;
|
op_b_neg_r <= 1'b0;
|
||||||
op_b_r <= {XLEN{1'b0}};
|
prenegate_op_b <= 1'b0;
|
||||||
accum <= {XLEN*2{1'b0}};
|
accum <= {XLEN*2{1'b0}};
|
||||||
end else if (op_kill || (op_vld && op_rdy)) begin
|
end else if (op_kill || (op_vld && op_rdy)) begin
|
||||||
// Initialise circuit with operands + state
|
// Initialise circuit with operands + state
|
||||||
|
@ -146,8 +150,8 @@ always @ (posedge clk or negedge rst_n) begin
|
||||||
sign_preadj_done <= !op_vld;
|
sign_preadj_done <= !op_vld;
|
||||||
sign_postadj_done <= !op_vld;
|
sign_postadj_done <= !op_vld;
|
||||||
sign_postadj_carry <= 1'b0;
|
sign_postadj_carry <= 1'b0;
|
||||||
|
prenegate_op_b <= 1'b0;
|
||||||
op_r <= op;
|
op_r <= op;
|
||||||
op_b_r <= op_b;
|
|
||||||
accum <= {{XLEN{1'b0}}, op_a};
|
accum <= {{XLEN{1'b0}}, op_a};
|
||||||
end else if (!sign_preadj_done) begin
|
end else if (!sign_preadj_done) begin
|
||||||
// Pre-adjust sign if necessary, else perform first iteration immediately
|
// Pre-adjust sign if necessary, else perform first iteration immediately
|
||||||
|
@ -157,8 +161,9 @@ always @ (posedge clk or negedge rst_n) begin
|
||||||
if (accum_neg_l || (op_b_neg ^ is_div)) begin
|
if (accum_neg_l || (op_b_neg ^ is_div)) begin
|
||||||
if (accum_neg_l)
|
if (accum_neg_l)
|
||||||
accum[0 +: XLEN] <= accum_next[0 +: XLEN];
|
accum[0 +: XLEN] <= accum_next[0 +: XLEN];
|
||||||
if (op_b_neg ^ is_div)
|
if (op_b_neg ^ is_div) begin
|
||||||
op_b_r <= -op_b_r;
|
prenegate_op_b <= 1'b1;
|
||||||
|
end
|
||||||
end else begin
|
end else begin
|
||||||
ctr <= ctr - MULDIV_UNROLL[W_CTR-1:0];
|
ctr <= ctr - MULDIV_UNROLL[W_CTR-1:0];
|
||||||
accum <= accum_next;
|
accum <= accum_next;
|
||||||
|
@ -206,7 +211,7 @@ wire op_signs_differ = op_a_neg_r ^ op_b_neg_r;
|
||||||
|
|
||||||
assign accum_neg_l =
|
assign accum_neg_l =
|
||||||
!sign_preadj_done && op_a_neg ||
|
!sign_preadj_done && op_a_neg ||
|
||||||
do_postadj && !sign_postadj_carry && op_signs_differ && !(is_div && ~|op_b_r);
|
do_postadj && !sign_postadj_carry && op_signs_differ && !(is_div && ~|op_b_sign_adj);
|
||||||
|
|
||||||
assign {accum_incr_h, accum_inv_h} =
|
assign {accum_incr_h, accum_inv_h} =
|
||||||
do_postadj && is_div && op_a_neg_r ? 2'b11 :
|
do_postadj && is_div && op_a_neg_r ? 2'b11 :
|
||||||
|
|
Loading…
Reference in New Issue