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