hw/vendor/lowrisc_ibex/rtl/ibex_multdiv_fast.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: `define OP_L 15:0
   7: `define OP_H 31:16
   8: 
   9: /**
  10:  * Fast Multiplier and Division
  11:  *
  12:  * 16x16 kernel multiplier and Long Division
  13:  */
  14: module ibex_multdiv_fast (
  15:     input  logic             clk_i,
  16:     input  logic             rst_ni,
  17:     input  logic             mult_en_i,
  18:     input  logic             div_en_i,
  19:     input  ibex_pkg::md_op_e operator_i,
  20:     input  logic  [1:0]      signed_mode_i,
  21:     input  logic [31:0]      op_a_i,
  22:     input  logic [31:0]      op_b_i,
  23:     input  logic [33:0]      alu_adder_ext_i,
  24:     input  logic [31:0]      alu_adder_i,
  25:     input  logic             equal_to_zero,
  26: 
  27:     output logic [32:0]      alu_operand_a_o,
  28:     output logic [32:0]      alu_operand_b_o,
  29: 
  30:     output logic [31:0]      multdiv_result_o,
  31:     output logic             valid_o
  32: );
  33: 
  34:   import ibex_pkg::*;
  35: 
  36:   logic [ 4:0] div_counter_q, div_counter_n;
  37:   typedef enum logic [1:0] {
  38:     ALBL, ALBH, AHBL, AHBH
  39:   } mult_fsm_e;
  40:   mult_fsm_e mult_state_q, mult_state_n;
  41: 
  42:   typedef enum logic [2:0] {
  43:     MD_IDLE, MD_ABS_A, MD_ABS_B, MD_COMP, MD_LAST, MD_CHANGE_SIGN, MD_FINISH
  44:   } md_fsm_e;
  45:   md_fsm_e md_state_q, md_state_n;
  46: 
  47:   logic signed [34:0] mac_res_signed;
  48:   logic        [34:0] mac_res_ext;
  49: 
  50:   logic [33:0] mac_res_q, mac_res_n, mac_res, op_remainder_n;
  51:   logic [15:0] mult_op_a;
  52:   logic [15:0] mult_op_b;
  53:   logic [33:0] accum;
  54:   logic        sign_a, sign_b;
  55:   logic        div_sign_a, div_sign_b;
  56:   logic        signed_mult;
  57:   logic        is_greater_equal;
  58:   logic        div_change_sign, rem_change_sign;
  59:   logic [31:0] one_shift;
  60:   logic [31:0] op_denominator_q;
  61:   logic [31:0] op_numerator_q;
  62:   logic [31:0] op_quotient_q;
  63:   logic [31:0] op_denominator_n;
  64:   logic [31:0] op_numerator_n;
  65:   logic [31:0] op_quotient_n;
  66:   logic [31:0] next_remainder;
  67:   logic [32:0] next_quotient;
  68:   logic [32:0] res_adder_h;
  69:   logic        mult_valid;
  70:   logic        div_valid;
  71: 
  72:   always_ff @(posedge clk_i or negedge rst_ni) begin : proc_mult_state_q
  73:     if (!rst_ni) begin
  74:       mult_state_q     <= ALBL;
  75:       mac_res_q        <= '0;
  76:       div_counter_q    <= '0;
  77:       md_state_q       <= MD_IDLE;
  78:       op_denominator_q <= '0;
  79:       op_numerator_q   <= '0;
  80:       op_quotient_q    <= '0;
  81:     end else begin
  82: 
  83:       if (mult_en_i) begin
  84:         mult_state_q <= mult_state_n;
  85:       end
  86: 
  87:       if (div_en_i) begin
  88:         div_counter_q    <= div_counter_n;
  89:         op_denominator_q <= op_denominator_n;
  90:         op_numerator_q   <= op_numerator_n;
  91:         op_quotient_q    <= op_quotient_n;
  92:         md_state_q       <= md_state_n;
  93:       end
  94: 
  95:       unique case(1'b1)
  96:         mult_en_i:
  97:           mac_res_q <= mac_res_n;
  98:         div_en_i:
  99:           mac_res_q <= op_remainder_n;
 100:         default:
 101:           mac_res_q <= mac_res_q;
 102:        endcase
 103:     end
 104:   end
 105: 
 106:   assign signed_mult      = (signed_mode_i != 2'b00);
 107: 
 108:   assign multdiv_result_o = div_en_i ? mac_res_q[31:0] : mac_res_n[31:0];
 109: 
 110:   // The 2 MSBs of mac_res_ext (mac_res_ext[34:33]) are always equal since:
 111:   // 1. The 2 MSBs of the multiplicants are always equal, and
 112:   // 2. The 16 MSBs of the addend (accum[33:18]) are always equal.
 113:   // Thus, it is safe to ignore mac_res_ext[34].
 114:   assign mac_res_signed =
 115:       $signed({sign_a, mult_op_a}) * $signed({sign_b, mult_op_b}) + $signed(accum);
 116:   assign mac_res_ext    = $unsigned(mac_res_signed);
 117:   assign mac_res        = mac_res_ext[33:0];
 118: 
 119:   assign res_adder_h    = alu_adder_ext_i[33:1];
 120: 
 121:   assign next_remainder = is_greater_equal ? res_adder_h[31:0] : mac_res_q[31:0];
 122:   assign next_quotient  = is_greater_equal ? {1'b0,op_quotient_q} | {1'b0,one_shift} :
 123:                                              {1'b0,op_quotient_q};
 124: 
 125:   assign one_shift      = {31'b0, 1'b1} << div_counter_q;
 126: 
 127:   // The adder in the ALU computes alu_operand_a_o + alu_operand_b_o which means
 128:   // Remainder - Divisor. If Remainder - Divisor >= 0, is_greater_equal is equal to 1,
 129:   // the next Remainder is Remainder - Divisor contained in res_adder_h and the
 130:   always_comb begin
 131:     if ((mac_res_q[31] ^ op_denominator_q[31]) == 1'b0) begin
 132:       is_greater_equal = (res_adder_h[31] == 1'b0);
 133:     end else begin
 134:       is_greater_equal = mac_res_q[31];
 135:     end
 136:   end
 137: 
 138:   assign div_sign_a      = op_a_i[31] & signed_mode_i[0];
 139:   assign div_sign_b      = op_b_i[31] & signed_mode_i[1];
 140:   assign div_change_sign = div_sign_a ^ div_sign_b;
 141:   assign rem_change_sign = div_sign_a;
 142: 
 143: 
 144:   always_comb begin : md_fsm
 145:     div_counter_n    = div_counter_q - 5'h1;
 146:     op_remainder_n   = mac_res_q;
 147:     op_quotient_n    = op_quotient_q;
 148:     md_state_n       = md_state_q;
 149:     op_numerator_n   = op_numerator_q;
 150:     op_denominator_n = op_denominator_q;
 151:     alu_operand_a_o  = {32'h0  , 1'b1};
 152:     alu_operand_b_o  = {~op_b_i, 1'b1};
 153:     div_valid        = 1'b0;
 154: 
 155:     unique case(md_state_q)
 156:       MD_IDLE: begin
 157:         if (operator_i == MD_OP_DIV) begin
 158:           // Check if the Denominator is 0
 159:           // quotient for division by 0
 160:           op_remainder_n = '1;
 161:           md_state_n     = equal_to_zero ? MD_FINISH : MD_ABS_A;
 162:         end else begin
 163:           // Check if the Denominator is 0
 164:           // remainder for division by 0
 165:           op_remainder_n = {2'b0, op_a_i};
 166:           md_state_n     = equal_to_zero ? MD_FINISH : MD_ABS_A;
 167:         end
 168:         // 0 - B = 0 iff B == 0
 169:         alu_operand_a_o  = {32'h0  , 1'b1};
 170:         alu_operand_b_o  = {~op_b_i, 1'b1};
 171:         div_counter_n    = 5'd31;
 172:       end
 173: 
 174:       MD_ABS_A: begin
 175:         // quotient
 176:         op_quotient_n   = '0;
 177:         // A abs value
 178:         op_numerator_n  = div_sign_a ? alu_adder_i : op_a_i;
 179:         md_state_n      = MD_ABS_B;
 180:         div_counter_n   = 5'd31;
 181:         // ABS(A) = 0 - A
 182:         alu_operand_a_o = {32'h0  , 1'b1};
 183:         alu_operand_b_o = {~op_a_i, 1'b1};
 184:       end
 185: 
 186:       MD_ABS_B: begin
 187:         // remainder
 188:         op_remainder_n   = { 33'h0, op_numerator_q[31]};
 189:         // B abs value
 190:         op_denominator_n = div_sign_b ? alu_adder_i : op_b_i;
 191:         md_state_n       = MD_COMP;
 192:         div_counter_n    = 5'd31;
 193:         // ABS(B) = 0 - B
 194:         alu_operand_a_o  = {32'h0  , 1'b1};
 195:         alu_operand_b_o  = {~op_b_i, 1'b1};
 196:       end
 197: 
 198:       MD_COMP: begin
 199:         op_remainder_n  = {1'b0, next_remainder[31:0], op_numerator_q[div_counter_n]};
 200:         op_quotient_n   = next_quotient[31:0];
 201:         md_state_n      = (div_counter_q == 5'd1) ? MD_LAST : MD_COMP;
 202:         // Division
 203:         alu_operand_a_o = {mac_res_q[31:0], 1'b1};         // it contains the remainder
 204:         alu_operand_b_o = {~op_denominator_q[31:0], 1'b1}; // -denominator two's compliment
 205:       end
 206: 
 207:       MD_LAST: begin
 208:         if (operator_i == MD_OP_DIV) begin
 209:           // this time we save the quotient in op_remainder_n (i.e. mac_res_q) since
 210:           // we do not need anymore the remainder
 211:           op_remainder_n = {1'b0, next_quotient};
 212:         end else begin
 213:           // this time we do not save the quotient anymore since we need only the remainder
 214:           op_remainder_n = {2'b0, next_remainder[31:0]};
 215:         end
 216:         // Division
 217:         alu_operand_a_o  = {mac_res_q[31:0], 1'b1};         // it contains the remainder
 218:         alu_operand_b_o  = {~op_denominator_q[31:0], 1'b1}; // -denominator two's compliment
 219: 
 220:         md_state_n = MD_CHANGE_SIGN;
 221:       end
 222: 
 223:       MD_CHANGE_SIGN: begin
 224:         md_state_n  = MD_FINISH;
 225:         if (operator_i == MD_OP_DIV) begin
 226:           op_remainder_n = (div_change_sign) ? {2'h0,alu_adder_i} : mac_res_q;
 227:         end else begin
 228:           op_remainder_n = (rem_change_sign) ? {2'h0,alu_adder_i} : mac_res_q;
 229:         end
 230:         // ABS(Quotient) = 0 - Quotient (or Remainder)
 231:         alu_operand_a_o  = {32'h0  , 1'b1};
 232:         alu_operand_b_o  = {~mac_res_q[31:0], 1'b1};
 233:       end
 234: 
 235:       MD_FINISH: begin
 236:         md_state_n = MD_IDLE;
 237:         div_valid   = 1'b1;
 238:       end
 239: 
 240:       default: begin
 241:         md_state_n = MD_IDLE;
 242:       end
 243:     endcase // md_state_q
 244:   end
 245: 
 246:   assign valid_o = mult_valid | div_valid;
 247: 
 248:   always_comb begin : mult_fsm
 249:     mult_op_a    = op_a_i[`OP_L];
 250:     mult_op_b    = op_b_i[`OP_L];
 251:     sign_a       = 1'b0;
 252:     sign_b       = 1'b0;
 253:     accum        = mac_res_q;
 254:     mac_res_n    = mac_res;
 255:     mult_state_n = mult_state_q;
 256:     mult_valid   = 1'b0;
 257: 
 258:     unique case (mult_state_q)
 259: 
 260:       ALBL: begin
 261:         // al*bl
 262:         mult_op_a = op_a_i[`OP_L];
 263:         mult_op_b = op_b_i[`OP_L];
 264:         sign_a    = 1'b0;
 265:         sign_b    = 1'b0;
 266:         accum     = '0;
 267:         mac_res_n = mac_res;
 268:         mult_state_n = ALBH;
 269:       end
 270: 
 271:       ALBH: begin
 272:         // al*bh<<16
 273:         mult_op_a = op_a_i[`OP_L];
 274:         mult_op_b = op_b_i[`OP_H];
 275:         sign_a    = 1'b0;
 276:         sign_b    = signed_mode_i[1] & op_b_i[31];
 277:         // result of AL*BL (in mac_res_q) always unsigned with no carry, so carries_q always 00
 278:         accum     = {18'b0,mac_res_q[31:16]};
 279:         if (operator_i == MD_OP_MULL) begin
 280:           mac_res_n = {2'b0,mac_res[`OP_L],mac_res_q[`OP_L]};
 281:         end else begin
 282:           // MD_OP_MULH
 283:           mac_res_n = mac_res;
 284:         end
 285:         mult_state_n = AHBL;
 286:       end
 287: 
 288:       AHBL: begin
 289:         // ah*bl<<16
 290:         mult_op_a = op_a_i[`OP_H];
 291:         mult_op_b = op_b_i[`OP_L];
 292:         sign_a    = signed_mode_i[0] & op_a_i[31];
 293:         sign_b    = 1'b0;
 294:         if (operator_i == MD_OP_MULL) begin
 295:           accum        = {18'b0,mac_res_q[31:16]};
 296:           mac_res_n    = {2'b0,mac_res[15:0],mac_res_q[15:0]};
 297:           mult_valid   = 1'b1;
 298:           mult_state_n = ALBL;
 299:         end else begin
 300:           accum        = mac_res_q;
 301:           mac_res_n    = mac_res;
 302:           mult_state_n = AHBH;
 303:         end
 304:       end
 305: 
 306:       AHBH: begin
 307:         // only MD_OP_MULH here
 308:         // ah*bh
 309:         mult_op_a = op_a_i[`OP_H];
 310:         mult_op_b = op_b_i[`OP_H];
 311:         sign_a    = signed_mode_i[0] & op_a_i[31];
 312:         sign_b    = signed_mode_i[1] & op_b_i[31];
 313:         accum[17: 0]  = mac_res_q[33:16];
 314:         accum[33:18]  = {16{signed_mult & mac_res_q[33]}};
 315:         // result of AH*BL is not signed only if signed_mode_i == 2'b00
 316:         mac_res_n    = mac_res;
 317:         mult_state_n = ALBL;
 318:         mult_valid   = 1'b1;
 319:       end
 320:       default: begin
 321:         mult_state_n = ALBL;
 322:       end
 323:     endcase // mult_state_q
 324:   end
 325: 
 326:   // States must be knwon/valid.
 327:   `ASSERT(IbexMultDivStateValid, md_state_q inside {
 328:       MD_IDLE, MD_ABS_A, MD_ABS_B, MD_COMP, MD_LAST, MD_CHANGE_SIGN, MD_FINISH
 329:       }, clk_i, !rst_ni)
 330:   `ASSERT_KNOWN(IbexMultStateKnown, mult_state_q, clk_i, !rst_ni)
 331: 
 332: endmodule // ibex_mult
 333: