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: