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: