../src/lowrisc_ibex_ibex_core_0.1/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:
12: `include "prim_assert.sv"
13:
14: module ibex_multdiv_slow
15: (
16: input logic clk_i,
17: input logic rst_ni,
18: input logic mult_en_i, // dynamic enable signal, for FSM control
19: input logic div_en_i, // dynamic enable signal, for FSM control
20: input logic mult_sel_i, // static decoder output, for data muxes
21: input logic div_sel_i, // static decoder output, for data muxes
22: input ibex_pkg::md_op_e operator_i,
23: input logic [1:0] signed_mode_i,
24: input logic [31:0] op_a_i,
25: input logic [31:0] op_b_i,
26: input logic [33:0] alu_adder_ext_i,
27: input logic [31:0] alu_adder_i,
28: input logic equal_to_zero_i,
29: input logic data_ind_timing_i,
30:
31: output logic [32:0] alu_operand_a_o,
32: output logic [32:0] alu_operand_b_o,
33:
34: input logic [33:0] imd_val_q_i,
35: output logic [33:0] imd_val_d_o,
36: output logic imd_val_we_o,
37:
38: input logic multdiv_ready_id_i,
39:
40: output logic [31:0] multdiv_result_o,
41:
42: output logic valid_o
43: );
44:
45: import ibex_pkg::*;
46:
47: typedef enum logic [2:0] {
48: MD_IDLE, MD_ABS_A, MD_ABS_B, MD_COMP, MD_LAST, MD_CHANGE_SIGN, MD_FINISH
49: } md_fsm_e;
50: md_fsm_e md_state_q, md_state_d;
51:
52: logic [32:0] accum_window_q, accum_window_d;
53: logic unused_imd_val;
54:
55: logic [32:0] res_adder_l;
56: logic [32:0] res_adder_h;
57:
58: logic [ 4:0] multdiv_count_q, multdiv_count_d;
59: logic [32:0] op_b_shift_q, op_b_shift_d;
60: logic [32:0] op_a_shift_q, op_a_shift_d;
61: logic [32:0] op_a_ext, op_b_ext;
62: logic [32:0] one_shift;
63: logic [32:0] op_a_bw_pp, op_a_bw_last_pp;
64: logic [31:0] b_0;
65: logic sign_a, sign_b;
66: logic [32:0] next_quotient;
67: logic [31:0] next_remainder;
68: logic [31:0] op_numerator_q, op_numerator_d;
69: logic is_greater_equal;
70: logic div_change_sign, rem_change_sign;
71: logic div_by_zero_d, div_by_zero_q;
72: logic multdiv_hold;
73: logic multdiv_en;
74:
75: // (accum_window_q + op_a_shift_q)
76: assign res_adder_l = alu_adder_ext_i[32:0];
77: // (accum_window_q + op_a_shift_q)>>1
78: assign res_adder_h = alu_adder_ext_i[33:1];
79:
80: /////////////////////
81: // ALU Operand MUX //
82: /////////////////////
83:
84: // Use shared intermediate value register in id_stage for accum_window
85: assign imd_val_d_o = {1'b0,accum_window_d};
86: assign imd_val_we_o = ~multdiv_hold;
87: assign accum_window_q = imd_val_q_i[32:0];
88: assign unused_imd_val = imd_val_q_i[33];
89:
90: always_comb begin
91: alu_operand_a_o = accum_window_q;
92:
93: unique case(operator_i)
94:
95: MD_OP_MULL: begin
96: alu_operand_b_o = op_a_bw_pp;
97: end
98:
99: MD_OP_MULH: begin
100: alu_operand_b_o = (md_state_q == MD_LAST) ? op_a_bw_last_pp : op_a_bw_pp;
101: end
102:
103: MD_OP_DIV,
104: MD_OP_REM: begin
105: unique case(md_state_q)
106: MD_IDLE: begin
107: // 0 - B = 0 iff B == 0
108: alu_operand_a_o = {32'h0 , 1'b1};
109: alu_operand_b_o = {~op_b_i, 1'b1};
110: end
111: MD_ABS_A: begin
112: // ABS(A) = 0 - A
113: alu_operand_a_o = {32'h0 , 1'b1};
114: alu_operand_b_o = {~op_a_i, 1'b1};
115: end
116: MD_ABS_B: begin
117: // ABS(B) = 0 - B
118: alu_operand_a_o = {32'h0 , 1'b1};
119: alu_operand_b_o = {~op_b_i, 1'b1};
120: end
121: MD_CHANGE_SIGN: begin
122: // ABS(Quotient) = 0 - Quotient (or Reminder)
123: alu_operand_a_o = {32'h0 , 1'b1};
124: alu_operand_b_o = {~accum_window_q[31:0], 1'b1};
125: end
126: default: begin
127: // Division
128: alu_operand_a_o = {accum_window_q[31:0], 1'b1}; // it contains the remainder
129: alu_operand_b_o = {~op_b_shift_q[31:0], 1'b1}; // -denominator two's compliment
130: end
131: endcase
132: end
133: default: begin
134: alu_operand_a_o = accum_window_q;
135: alu_operand_b_o = {~op_b_shift_q[31:0], 1'b1};
136: end
137: endcase
138: end
139:
140: // Multiplier partial product calculation
141: assign b_0 = {32{op_b_shift_q[0]}};
142: assign op_a_bw_pp = { ~(op_a_shift_q[32] & op_b_shift_q[0]), (op_a_shift_q[31:0] & b_0) };
143: assign op_a_bw_last_pp = { (op_a_shift_q[32] & op_b_shift_q[0]), ~(op_a_shift_q[31:0] & b_0) };
144:
145: // Sign extend the input operands
146: assign sign_a = op_a_i[31] & signed_mode_i[0];
147: assign sign_b = op_b_i[31] & signed_mode_i[1];
148:
149: assign op_a_ext = {sign_a, op_a_i};
150: assign op_b_ext = {sign_b, op_b_i};
151:
152: // Divider calculations
153:
154: // The adder in the ALU computes Remainder - Divisor. If Remainder - Divisor >= 0,
155: // is_greater_equal is true, the next Remainder is the subtraction result and the Quotient
156: // multdiv_count_q-th bit is set to 1.
157: assign is_greater_equal = (accum_window_q[31] == op_b_shift_q[31]) ?
158: ~res_adder_h[31] : accum_window_q[31];
159:
160: assign one_shift = {32'b0, 1'b1} << multdiv_count_q;
161:
162: assign next_remainder = is_greater_equal ? res_adder_h[31:0] : accum_window_q[31:0];
163: assign next_quotient = is_greater_equal ? op_a_shift_q | one_shift : op_a_shift_q;
164:
165: assign div_change_sign = (sign_a ^ sign_b) & ~div_by_zero_q;
166: assign rem_change_sign = sign_a;
167:
168: always_comb begin
169: multdiv_count_d = multdiv_count_q;
170: accum_window_d = accum_window_q;
171: op_b_shift_d = op_b_shift_q;
172: op_a_shift_d = op_a_shift_q;
173: op_numerator_d = op_numerator_q;
174: md_state_d = md_state_q;
175: multdiv_hold = 1'b0;
176: div_by_zero_d = div_by_zero_q;
177: if (mult_sel_i || div_sel_i) begin
178: unique case(md_state_q)
179: MD_IDLE: begin
180: unique case(operator_i)
181: MD_OP_MULL: begin
182: op_a_shift_d = op_a_ext << 1;
183: accum_window_d = { ~(op_a_ext[32] & op_b_i[0]),
184: op_a_ext[31:0] & {32{op_b_i[0]}} };
185: op_b_shift_d = op_b_ext >> 1;
186: // Proceed with multiplication by 0/1 in data-independent time mode
187: md_state_d = (!data_ind_timing_i && ((op_b_ext >> 1) == 0)) ? MD_LAST : MD_COMP;
188: end
189: MD_OP_MULH: begin
190: op_a_shift_d = op_a_ext;
191: accum_window_d = { 1'b1, ~(op_a_ext[32] & op_b_i[0]),
192: op_a_ext[31:1] & {31{op_b_i[0]}} };
193: op_b_shift_d = op_b_ext >> 1;
194: md_state_d = MD_COMP;
195: end
196: MD_OP_DIV: begin
197: // Check if the denominator is 0
198: // quotient for division by 0 is specified to be -1
199: // Note with data-independent time option, the full divide operation will proceed as
200: // normal and will naturally return -1
201: accum_window_d = {33{1'b1}};
202: md_state_d = (!data_ind_timing_i && equal_to_zero_i) ? MD_FINISH : MD_ABS_A;
203: // Record that this is a div by zero to stop the sign change at the end of the
204: // division (in data_ind_timing mode).
205: div_by_zero_d = equal_to_zero_i;
206: end
207: MD_OP_REM: begin
208: // Check if the denominator is 0
209: // remainder for division by 0 is specified to be the numerator (operand a)
210: // Note with data-independent time option, the full divide operation will proceed as
211: // normal and will naturally return operand a
212: accum_window_d = op_a_ext;
213: md_state_d = (!data_ind_timing_i && equal_to_zero_i) ? MD_FINISH : MD_ABS_A;
214: end
215: default:;
216: endcase
217: multdiv_count_d = 5'd31;
218: end
219:
220: MD_ABS_A: begin
221: // quotient
222: op_a_shift_d = '0;
223: // A abs value
224: op_numerator_d = sign_a ? alu_adder_i : op_a_i;
225: md_state_d = MD_ABS_B;
226: end
227:
228: MD_ABS_B: begin
229: // remainder
230: accum_window_d = {32'h0,op_numerator_q[31]};
231: // B abs value
232: op_b_shift_d = sign_b ? {1'b0,alu_adder_i} : {1'b0,op_b_i};
233: md_state_d = MD_COMP;
234: end
235:
236: MD_COMP: begin
237: multdiv_count_d = multdiv_count_q - 5'h1;
238: unique case(operator_i)
239: MD_OP_MULL: begin
240: accum_window_d = res_adder_l;
241: op_a_shift_d = op_a_shift_q << 1;
242: op_b_shift_d = op_b_shift_q >> 1;
243: // Multiplication is complete once op_b is zero, unless in data_ind_timing mode where
244: // the maximum possible shift-add operations will be completed regardless of op_b
245: md_state_d = ((!data_ind_timing_i && (op_b_shift_d == 0)) ||
246: (multdiv_count_q == 5'd1)) ? MD_LAST : MD_COMP;
247: end
248: MD_OP_MULH: begin
249: accum_window_d = res_adder_h;
250: op_a_shift_d = op_a_shift_q;
251: op_b_shift_d = op_b_shift_q >> 1;
252: md_state_d = (multdiv_count_q == 5'd1) ? MD_LAST : MD_COMP;
253: end
254: MD_OP_DIV,
255: MD_OP_REM: begin
256: accum_window_d = {next_remainder[31:0], op_numerator_q[multdiv_count_d]};
257: op_a_shift_d = next_quotient;
258: md_state_d = (multdiv_count_q == 5'd1) ? MD_LAST : MD_COMP;
259: end
260: default: ;
261: endcase
262: end
263:
264: MD_LAST: begin
265: unique case(operator_i)
266: MD_OP_MULL: begin
267: accum_window_d = res_adder_l;
268:
269: // Note no state transition will occur if multdiv_hold is set
270: md_state_d = MD_IDLE;
271: multdiv_hold = ~multdiv_ready_id_i;
272: end
273: MD_OP_MULH: begin
274: accum_window_d = res_adder_l;
275: md_state_d = MD_IDLE;
276:
277: // Note no state transition will occur if multdiv_hold is set
278: md_state_d = MD_IDLE;
279: multdiv_hold = ~multdiv_ready_id_i;
280: end
281: MD_OP_DIV: begin
282: // this time we save the quotient in accum_window_q since we do not need anymore the
283: // remainder
284: accum_window_d = next_quotient;
285: md_state_d = MD_CHANGE_SIGN;
286: end
287: MD_OP_REM: begin
288: // this time we do not save the quotient anymore since we need only the remainder
289: accum_window_d = {1'b0, next_remainder[31:0]};
290: md_state_d = MD_CHANGE_SIGN;
291: end
292: default: ;
293: endcase
294: end
295:
296: MD_CHANGE_SIGN: begin
297: md_state_d = MD_FINISH;
298: unique case(operator_i)
299: MD_OP_DIV:
300: accum_window_d = div_change_sign ? {1'b0,alu_adder_i} : accum_window_q;
301: MD_OP_REM:
302: accum_window_d = rem_change_sign ? {1'b0,alu_adder_i} : accum_window_q;
303: default: ;
304: endcase
305: end
306:
307: MD_FINISH: begin
308: // Note no state transition will occur if multdiv_hold is set
309: md_state_d = MD_IDLE;
310: multdiv_hold = ~multdiv_ready_id_i;
311: end
312:
313: default: begin
314: md_state_d = MD_IDLE;
315: end
316: endcase // md_state_q
317: end // (mult_sel_i || div_sel_i)
318: end
319:
320: //////////////////////////////////////////
321: // Mutliplier / Divider state registers //
322: //////////////////////////////////////////
323:
324: assign multdiv_en = (mult_en_i | div_en_i) & ~multdiv_hold;
325:
326: always_ff @(posedge clk_i or negedge rst_ni) begin
327: if (!rst_ni) begin
328: multdiv_count_q <= 5'h0;
329: op_b_shift_q <= 33'h0;
330: op_a_shift_q <= 33'h0;
331: op_numerator_q <= 32'h0;
332: md_state_q <= MD_IDLE;
333: div_by_zero_q <= 1'b0;
334: end else if (multdiv_en) begin
335: multdiv_count_q <= multdiv_count_d;
336: op_b_shift_q <= op_b_shift_d;
337: op_a_shift_q <= op_a_shift_d;
338: op_numerator_q <= op_numerator_d;
339: md_state_q <= md_state_d;
340: div_by_zero_q <= div_by_zero_d;
341: end
342: end
343:
344: /////////////
345: // Outputs //
346: /////////////
347:
348: assign valid_o = (md_state_q == MD_FINISH) |
349: (md_state_q == MD_LAST &
350: (operator_i == MD_OP_MULL |
351: operator_i == MD_OP_MULH));
352:
353: assign multdiv_result_o = div_en_i ? accum_window_q[31:0] : res_adder_l[31:0];
354:
355: ////////////////
356: // Assertions //
357: ////////////////
358:
359: // State must be valid.
360: `ASSERT(IbexMultDivStateValid, md_state_q inside {
361: MD_IDLE, MD_ABS_A, MD_ABS_B, MD_COMP, MD_LAST, MD_CHANGE_SIGN, MD_FINISH
362: }, clk_i, !rst_ni)
363:
364: endmodule
365: