hw/vendor/lowrisc_ibex/rtl/ibex_multdiv_slow.sv Cov: 100%

   1: // Copyright lowRISC contributors.
   2: // Copyright 2018 ETH Zurich and University of Bologna, see also CREDITS.md.
   3: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
   4: // SPDX-License-Identifier: Apache-2.0
   5: 
   6: /**
   7:  * Slow Multiplier and Division
   8:  *
   9:  * Baugh-Wooley multiplier and Long Division
  10:  */
  11: module ibex_multdiv_slow (
  12:     input  logic             clk_i,
  13:     input  logic             rst_ni,
  14:     input  logic             mult_en_i,
  15:     input  logic             div_en_i,
  16:     input  ibex_pkg::md_op_e operator_i,
  17:     input  logic  [1:0]      signed_mode_i,
  18:     input  logic [31:0]      op_a_i,
  19:     input  logic [31:0]      op_b_i,
  20:     input  logic [33:0]      alu_adder_ext_i,
  21:     input  logic [31:0]      alu_adder_i,
  22:     input  logic             equal_to_zero,
  23: 
  24:     output logic [32:0]      alu_operand_a_o,
  25:     output logic [32:0]      alu_operand_b_o,
  26:     output logic [31:0]      multdiv_result_o,
  27: 
  28:     output logic             valid_o
  29: );
  30: 
  31:   import ibex_pkg::*;
  32: 
  33:   logic [ 4:0] multdiv_state_q, multdiv_state_d, multdiv_state_m1;
  34:   typedef enum logic [2:0] {
  35:     MD_IDLE, MD_ABS_A, MD_ABS_B, MD_COMP, MD_LAST, MD_CHANGE_SIGN, MD_FINISH
  36:   } md_fsm_e;
  37:   md_fsm_e md_state_q, md_state_d;
  38: 
  39:   logic [32:0] accum_window_q, accum_window_d;
  40: 
  41:   logic [32:0] res_adder_l;
  42:   logic [32:0] res_adder_h;
  43: 
  44:   logic [32:0] op_b_shift_q, op_b_shift_d;
  45:   logic [32:0] op_a_shift_q, op_a_shift_d;
  46:   logic [32:0] op_a_ext, op_b_ext;
  47:   logic [32:0] one_shift;
  48:   logic [32:0] op_a_bw_pp, op_a_bw_last_pp;
  49:   logic [31:0] b_0;
  50:   logic        sign_a, sign_b;
  51:   logic [32:0] next_reminder, next_quotient;
  52:   logic [32:0] op_remainder;
  53:   logic [31:0] op_numerator_q, op_numerator_d;
  54:   logic        is_greater_equal;
  55:   logic        div_change_sign, rem_change_sign;
  56: 
  57:    // (accum_window_q + op_a_shift_q)
  58:   assign res_adder_l       = alu_adder_ext_i[32:0];
  59:    // (accum_window_q + op_a_shift_q)>>1
  60:   assign res_adder_h       = alu_adder_ext_i[33:1];
  61: 
  62:   always_comb begin
  63:     alu_operand_a_o   = accum_window_q;
  64:     multdiv_result_o  = div_en_i ? accum_window_q[31:0] : res_adder_l;
  65: 
  66:     unique case(operator_i)
  67: 
  68:       MD_OP_MULL: begin
  69:         alu_operand_b_o   = op_a_bw_pp;
  70:       end
  71: 
  72:       MD_OP_MULH: begin
  73:         alu_operand_b_o = (md_state_q == MD_LAST) ? op_a_bw_last_pp : op_a_bw_pp;
  74:       end
  75: 
  76:       default: begin
  77:         unique case(md_state_q)
  78:           MD_IDLE: begin
  79:             // 0 - B = 0 iff B == 0
  80:             alu_operand_a_o     = {32'h0  , 1'b1};
  81:             alu_operand_b_o     = {~op_b_i, 1'b1};
  82:           end
  83:           MD_ABS_A: begin
  84:             // ABS(A) = 0 - A
  85:             alu_operand_a_o     = {32'h0  , 1'b1};
  86:             alu_operand_b_o     = {~op_a_i, 1'b1};
  87:           end
  88:           MD_ABS_B: begin
  89:             // ABS(B) = 0 - B
  90:             alu_operand_a_o     = {32'h0  , 1'b1};
  91:             alu_operand_b_o     = {~op_b_i, 1'b1};
  92:           end
  93:           MD_CHANGE_SIGN: begin
  94:             // ABS(Quotient) = 0 - Quotient (or Reminder)
  95:             alu_operand_a_o     = {32'h0  , 1'b1};
  96:             alu_operand_b_o     = {~accum_window_q[31:0], 1'b1};
  97:           end
  98:           default: begin
  99:             // Division
 100:             alu_operand_a_o     = {accum_window_q[31:0], 1'b1}; // it contains the reminder
 101:             alu_operand_b_o     = {~op_b_shift_q[31:0], 1'b1};  // -denominator two's compliment
 102:           end
 103:         endcase
 104:       end
 105:     endcase
 106:   end
 107: 
 108:   // The adder in the ALU computes alu_operand_a_o + alu_operand_b_o which means
 109:   // Reminder - Divisor. If Reminder - Divisor >= 0, is_greater_equal is equal to 1,
 110:   // the next Reminder is Reminder - Divisor contained in res_adder_h and the
 111:   // Quotient multdiv_state_q-th bit is set to 1 using the shift register op_b_shift_q.
 112:   assign is_greater_equal = ((accum_window_q[31] ^ op_b_shift_q[31]) == 1'b0) ?
 113:       (res_adder_h[31] == 1'b0) : accum_window_q[31];
 114: 
 115:   assign one_shift     = {32'b0, 1'b1} << multdiv_state_q;
 116: 
 117:   assign next_reminder = is_greater_equal ? res_adder_h              : op_remainder;
 118:   assign next_quotient = is_greater_equal ? op_a_shift_q | one_shift : op_a_shift_q;
 119: 
 120:   assign b_0             = {32{op_b_shift_q[0]}};
 121: 
 122:   // build the partial product
 123:   assign op_a_bw_pp       = { ~(op_a_shift_q[32] & op_b_shift_q[0]),  (op_a_shift_q[31:0] & b_0) };
 124:   assign op_a_bw_last_pp  = {  (op_a_shift_q[32] & op_b_shift_q[0]), ~(op_a_shift_q[31:0] & b_0) };
 125: 
 126:   assign sign_a   = op_a_i[31] & signed_mode_i[0];
 127:   assign sign_b   = op_b_i[31] & signed_mode_i[1];
 128: 
 129:   assign op_a_ext = {sign_a, op_a_i};
 130:   assign op_b_ext = {sign_b, op_b_i};
 131: 
 132:   // division
 133:   assign op_remainder = accum_window_q[32:0];
 134: 
 135:   assign multdiv_state_m1  = multdiv_state_q - 5'h1;
 136:   assign div_change_sign  = sign_a ^ sign_b;
 137:   assign rem_change_sign  = sign_a;
 138: 
 139:   always_ff @(posedge clk_i or negedge rst_ni) begin : proc_multdiv_state_q
 140:     if (!rst_ni) begin
 141:       multdiv_state_q  <= 5'h0;
 142:       accum_window_q   <= 33'h0;
 143:       op_b_shift_q     <= 33'h0;
 144:       op_a_shift_q     <= 33'h0;
 145:       op_numerator_q   <= 32'h0;
 146:       md_state_q       <= MD_IDLE;
 147:     end else begin
 148:       multdiv_state_q  <= multdiv_state_d;
 149:       accum_window_q   <= accum_window_d;
 150:       op_b_shift_q     <= op_b_shift_d;
 151:       op_a_shift_q     <= op_a_shift_d;
 152:       op_numerator_q   <= op_numerator_d;
 153:       md_state_q       <= md_state_d;
 154:     end
 155:   end
 156: 
 157:   always_comb begin
 158:     multdiv_state_d  = multdiv_state_q;
 159:     accum_window_d   = accum_window_q;
 160:     op_b_shift_d     = op_b_shift_q;
 161:     op_a_shift_d     = op_a_shift_q;
 162:     op_numerator_d   = op_numerator_q;
 163:     md_state_d       = md_state_q;
 164:     if (mult_en_i || div_en_i) begin
 165:       unique case(md_state_q)
 166:         MD_IDLE: begin
 167:           unique case(operator_i)
 168:             MD_OP_MULL: begin
 169:               op_a_shift_d   = op_a_ext << 1;
 170:               accum_window_d = {       ~(op_a_ext[32]   &     op_b_i[0]),
 171:                                          op_a_ext[31:0] & {32{op_b_i[0]}}  };
 172:               op_b_shift_d   = op_b_ext >> 1;
 173:               md_state_d     = MD_COMP;
 174:             end
 175:             MD_OP_MULH: begin
 176:               op_a_shift_d   = op_a_ext;
 177:               accum_window_d = { 1'b1, ~(op_a_ext[32]   &     op_b_i[0]),
 178:                                          op_a_ext[31:1] & {31{op_b_i[0]}}  };
 179:               op_b_shift_d   = op_b_ext >> 1;
 180:               md_state_d     = MD_COMP;
 181:             end
 182:             MD_OP_DIV: begin
 183:               // Check if the Denominator is 0
 184:               // quotient for division by 0
 185:               accum_window_d = {33{1'b1}};
 186:               md_state_d     = equal_to_zero ? MD_FINISH : MD_ABS_A;
 187:             end
 188:             default: begin
 189:               // Check if the Denominator is 0
 190:               // reminder for division by 0
 191:               accum_window_d = op_a_ext;
 192:               md_state_d     = equal_to_zero ? MD_FINISH : MD_ABS_A;
 193:             end
 194:           endcase
 195:           multdiv_state_d   = 5'd31;
 196:         end
 197: 
 198:         MD_ABS_A: begin
 199:           // quotient
 200:           op_a_shift_d   = '0;
 201:           // A abs value
 202:           op_numerator_d = sign_a ? alu_adder_i : op_a_i;
 203:           md_state_d     = MD_ABS_B;
 204:         end
 205: 
 206:         MD_ABS_B: begin
 207:           // reminder
 208:           accum_window_d = { 32'h0, op_numerator_q[31]};
 209:           // B abs value
 210:           op_b_shift_d   = sign_b ? alu_adder_i : op_b_i;
 211:           md_state_d     = MD_COMP;
 212:         end
 213: 
 214:         MD_COMP: begin
 215:           multdiv_state_d   = multdiv_state_m1;
 216:           unique case(operator_i)
 217:             MD_OP_MULL: begin
 218:               accum_window_d = res_adder_l;
 219:               op_a_shift_d   = op_a_shift_q << 1;
 220:               op_b_shift_d   = op_b_shift_q >> 1;
 221:             end
 222:             MD_OP_MULH: begin
 223:               accum_window_d = res_adder_h;
 224:               op_a_shift_d   = op_a_shift_q;
 225:               op_b_shift_d   = op_b_shift_q >> 1;
 226:             end
 227:             default: begin
 228:               accum_window_d = {next_reminder[31:0], op_numerator_q[multdiv_state_m1]};
 229:               op_a_shift_d   = next_quotient;
 230:             end
 231:           endcase
 232: 
 233:           md_state_d = (multdiv_state_q == 5'd1) ? MD_LAST : MD_COMP;
 234:         end
 235: 
 236:         MD_LAST: begin
 237:           unique case(operator_i)
 238:             MD_OP_MULL: begin
 239:               accum_window_d = res_adder_l;
 240:               md_state_d     = MD_IDLE;
 241:             end
 242:             MD_OP_MULH: begin
 243:               accum_window_d = res_adder_l;
 244:               md_state_d     = MD_IDLE;
 245:             end
 246:             MD_OP_DIV: begin
 247:               // this time we save the quotient in accum_window_q since we do not need anymore the
 248:               // reminder
 249:               accum_window_d = next_quotient;
 250:               md_state_d     = MD_CHANGE_SIGN;
 251:             end
 252:             default: begin
 253:               // this time we do not save the quotient anymore since we need only the reminder
 254:               accum_window_d = {1'b0, next_reminder[31:0]};
 255:               md_state_d     = MD_CHANGE_SIGN;
 256:             end
 257:           endcase
 258:         end
 259: 
 260:         MD_CHANGE_SIGN: begin
 261:           md_state_d = MD_FINISH;
 262:           unique case(operator_i)
 263:             MD_OP_DIV:
 264:               accum_window_d = (div_change_sign) ? alu_adder_i : accum_window_q;
 265:             default:
 266:               accum_window_d = (rem_change_sign) ? alu_adder_i : accum_window_q;
 267:           endcase
 268:         end
 269: 
 270:         MD_FINISH: begin
 271:           md_state_d = MD_IDLE;
 272:         end
 273: 
 274:         default: begin
 275:           md_state_d = MD_IDLE;
 276:         end
 277:         endcase // md_state_q
 278:       end
 279:   end
 280: 
 281:   assign valid_o = (md_state_q == MD_FINISH) |
 282:                    (md_state_q == MD_LAST &
 283:                    (operator_i == MD_OP_MULL |
 284:                     operator_i == MD_OP_MULH));
 285: 
 286:   // State must be valid.
 287:   `ASSERT(IbexMultDivStateValid, md_state_q inside {
 288:       MD_IDLE, MD_ABS_A, MD_ABS_B, MD_COMP, MD_LAST, MD_CHANGE_SIGN, MD_FINISH
 289:       }, clk_i, !rst_ni)
 290: 
 291: endmodule // ibex_mult
 292: