Translate put_z verilog case into nmigen
[ieee754fpu.git] / src / add / nmigen_add_experiment.py
1 # IEEE Floating Point Adder (Single Precision)
2 # Copyright (C) Jonathan P Dawson 2013
3 # 2013-12-12
4
5 from nmigen import Module, Signal, Cat
6 from nmigen.cli import main
7
8
9 class FPNum:
10 """ Floating-point Number Class, variable-width TODO (currently 32-bit)
11
12 Contains signals for an incoming copy of the value, decoded into
13 sign / exponent / mantissa.
14 Also contains encoding functions, creation and recognition of
15 zero, NaN and inf (all signed)
16
17 Four extra bits are included in the mantissa: the top bit
18 (m[-1]) is effectively a carry-overflow. The other three are
19 guard (m[2]), round (m[1]), and sticky (m[0])
20 """
21 def __init__(self, width, m_width=None):
22 self.width = width
23 if m_width is None:
24 m_width = width - 5 # mantissa extra bits (top,guard,round)
25 self.v = Signal(width) # Latched copy of value
26 self.m = Signal(m_width) # Mantissa
27 self.e = Signal((10, True)) # Exponent: 10 bits, signed
28 self.s = Signal() # Sign bit
29
30 def decode(self):
31 """ decodes a latched value into sign / exponent / mantissa
32
33 bias is subtracted here, from the exponent.
34 """
35 v = self.v
36 return [self.m.eq(Cat(0, 0, 0, v[0:23])), # mantissa
37 self.e.eq(Cat(v[23:31]) - 127), # exponent (take off bias)
38 self.s.eq(Cat(v[31])), # sign
39 ]
40
41 def create(self, s, e, m):
42 """ creates a value from sign / exponent / mantissa
43
44 bias is added here, to the exponent
45 """
46 return [
47 self.v[31].eq(s), # sign
48 self.v[23:31].eq(e + 127), # exp (add on bias)
49 self.v[0:23].eq(m) # mantissa
50 ]
51
52 def shift_down(self):
53 """ shifts a mantissa down by one. exponent is increased to compensate
54
55 accuracy is lost as a result in the mantissa however there are 3
56 guard bits (the latter of which is the "sticky" bit)
57 """
58 return self.create(self.s,
59 self.e + 1,
60 Cat(self.m[0] | self.m[1], self.m[1:-5], 0))
61
62 def nan(self, s):
63 return self.create(s, 0x80, 1<<22)
64
65 def inf(self, s):
66 return self.create(s, 0x80, 0)
67
68 def zero(self, s):
69 return self.create(s, -127, 0)
70
71 def is_nan(self):
72 return (self.e == 128) & (self.m != 0)
73
74 def is_inf(self):
75 return (self.e == 128) & (self.m == 0)
76
77 def is_zero(self):
78 return (self.e == -127) & (self.m == 0)
79
80 def is_overflowed(self):
81 return (self.e < 127)
82
83
84 class FPADD:
85 def __init__(self, width):
86 self.width = width
87
88 self.in_a = Signal(width)
89 self.in_a_stb = Signal()
90 self.in_a_ack = Signal()
91
92 self.in_b = Signal(width)
93 self.in_b_stb = Signal()
94 self.in_b_ack = Signal()
95
96 self.out_z = Signal(width)
97 self.out_z_stb = Signal()
98 self.out_z_ack = Signal()
99
100 def get_fragment(self, platform):
101 m = Module()
102
103 # Latches
104 a = FPNum(self.width)
105 b = FPNum(self.width)
106 z = FPNum(self.width, 24)
107
108 tot = Signal(28) # sticky/round/guard bits, 23 result, 1 overflow
109
110 guard = Signal() # tot[2]
111 round_bit = Signal() # tot[1]
112 sticky = Signal() # tot[0]
113
114 with m.FSM() as fsm:
115
116 # ******
117 # gets operand a
118
119 with m.State("get_a"):
120 with m.If((self.in_a_ack) & (self.in_a_stb)):
121 m.next = "get_b"
122 m.d.sync += [
123 a.v.eq(self.in_a),
124 self.in_a_ack.eq(0)
125 ]
126 with m.Else():
127 m.d.sync += self.in_a_ack.eq(1)
128
129 # ******
130 # gets operand b
131
132 with m.State("get_b"):
133 with m.If((self.in_b_ack) & (self.in_b_stb)):
134 m.next = "get_a"
135 m.d.sync += [
136 b.v.eq(self.in_b),
137 self.in_b_ack.eq(0)
138 ]
139 with m.Else():
140 m.d.sync += self.in_b_ack.eq(1)
141
142 # ******
143 # unpacks operands into sign, mantissa and exponent
144
145 with m.State("unpack"):
146 m.next = "special_cases"
147 m.d.sync += a.decode()
148 m.d.sync += b.decode()
149
150 # ******
151 # special cases: NaNs, infs, zeros, denormalised
152
153 with m.State("special_cases"):
154
155 # if a is NaN or b is NaN return NaN
156 with m.If(a.is_nan() | b.is_nan()):
157 m.next = "put_z"
158 m.d.sync += z.nan(1)
159
160 # if a is inf return inf (or NaN)
161 with m.Elif(a.is_inf()):
162 m.next = "put_z"
163 m.d.sync += z.inf(a.s)
164 # if a is inf and signs don't match return NaN
165 with m.If((b.e == 128) & (a.s != b.s)):
166 m.d.sync += z.nan(b.s)
167
168 # if b is inf return inf
169 with m.Elif(b.is_inf()):
170 m.next = "put_z"
171 m.d.sync += z.inf(b.s)
172
173 # if a is zero and b zero return signed-a/b
174 with m.Elif(a.is_zero() & b.is_zero()):
175 m.next = "put_z"
176 m.d.sync += z.create(a.s & b.s, b.e[0:8], b.m[3:26])
177
178 # if a is zero return b
179 with m.Elif(a.is_zero()):
180 m.next = "put_z"
181 m.d.sync += z.create(b.s, b.e[0:8], b.m[3:26])
182
183 # if b is zero return a
184 with m.Elif(b.is_zero()):
185 m.next = "put_z"
186 m.d.sync += z.create(a.s, a.e[0:8], a.m[3:26])
187
188 # Denormalised Number checks
189 with m.Else():
190 m.next = "align"
191 # denormalise a check
192 with m.If(a.e == -127):
193 m.d.sync += a.e.eq(-126) # limit a exponent
194 with m.Else():
195 m.d.sync += a.m[26].eq(1) # set highest mantissa bit
196 # denormalise b check
197 with m.If(b.e == -127):
198 m.d.sync += b.e.eq(-126) # limit b exponent
199 with m.Else():
200 m.d.sync += b.m[26].eq(1) # set highest mantissa bit
201
202 # ******
203 # align. NOTE: this does *not* do single-cycle multi-shifting,
204 # it *STAYS* in the align state until the exponents match
205
206 with m.State("align"):
207 # exponent of a greater than b: increment b exp, shift b mant
208 with m.If(a.e > b.e):
209 m.d.sync += b.shift_down()
210 # exponent of b greater than a: increment a exp, shift a mant
211 with m.Elif(a.e < b.e):
212 m.d.sync += a.shift_down()
213 # exponents equal: move to next stage.
214 with m.Else():
215 m.next = "add_0"
216
217 # ******
218 # First stage of add. covers same-sign (add) and subtract
219 # special-casing when mantissas are greater or equal, to
220 # give greatest accuracy.
221
222 with m.State("add_0"):
223 m.next = "add_1"
224 m.d.sync += z.e.eq(a.e)
225 # same-sign (both negative or both positive) add mantissas
226 with m.If(a.s == b.s):
227 m.d.sync += [
228 tot.eq(a.m + b.m),
229 z.s.eq(a.s)
230 ]
231 # a mantissa greater than b, use a
232 with m.Elif(a.m >= b.m):
233 m.d.sync += [
234 tot.eq(a.m - b.m),
235 z.s.eq(a.s)
236 ]
237 # b mantissa greater than a, use b
238 with m.Else():
239 m.d.sync += [
240 tot.eq(b.m - a.m),
241 z.s.eq(b.s)
242 ]
243
244 # ******
245 # Second stage of add: preparation for normalisation.
246 # detects when tot sum is too big (tot[27] is kinda a carry bit)
247
248 with m.State("add_1"):
249 m.next = "normalise_1"
250 # tot[27] gets set when the sum overflows. shift result down
251 with m.If(tot[27]):
252 m.d.sync += [
253 z.m.eq(tot[4:28]),
254 guard.eq(tot[3]),
255 round_bit.eq(tot[2]),
256 sticky.eq(tot[1] | tot[0]),
257 z.e.eq(z.e + 1)
258 ]
259 # tot[27] zero case
260 with m.Else():
261 m.d.sync += [
262 z.m.eq(tot[3:27]),
263 guard.eq(tot[2]),
264 round_bit.eq(tot[1]),
265 sticky.eq(tot[0])
266 ]
267
268 # ******
269 # First stage of normalisation.
270 # NOTE: just like "align", this one keeps going round every clock
271 # until the result's exponent is within acceptable "range"
272 # NOTE: the weirdness of reassigning guard and round is due to
273 # the extra mantissa bits coming from tot[0..2]
274
275 with m.State("normalise_1"):
276 with m.If((z.m[23] == 0) & (z.e > -126)):
277 m.d.sync +=[
278 z.e.eq(z.e - 1), # DECREASE exponent
279 z.m.eq(z.m << 1), # shift mantissa UP
280 z.m[0].eq(guard), # steal guard bit (was tot[2])
281 guard.eq(round_bit), # steal round_bit (was tot[1])
282 ]
283 with m.Else():
284 m.next = "normalize_2"
285
286 # ******
287 # Second stage of normalisation.
288 # NOTE: just like "align", this one keeps going round every clock
289 # until the result's exponent is within acceptable "range"
290 # NOTE: the weirdness of reassigning guard and round is due to
291 # the extra mantissa bits coming from tot[0..2]
292
293 with m.State("normalise_2"):
294 with m.If(z.e < -126):
295 m.d.sync +=[
296 z.e.eq(z.e + 1), # INCREASE exponent
297 z.m.eq(z.m >> 1), # shift mantissa DOWN
298 guard.eq(z.m[0]),
299 round_bit.eq(guard),
300 sticky.eq(sticky | round_bit)
301 ]
302 with m.Else():
303 m.next = "round"
304
305 # ******
306 # rounding stage
307
308 with m.State("round"):
309 m.next = "pack"
310 with m.If(guard & (round_bit | sticky | z.m[0])):
311 m.d.sync += z.m.eq(z.m + 1) # mantissa rounds up
312 with m.If(z.m == 0xffffff): # all 1s
313 m.d.sync += z.e.eq(z.e + 1) # exponent rounds up
314
315 # ******
316 # pack stage
317
318 with m.State("pack"):
319 m.next = "put_z"
320 m.d.sync += [
321 z.v[0:22].eq(z.m[0:22]),
322 z.v[22:31].eq(z.e[0:7]),
323 z.v[31].eq(z.s)
324 ]
325 with m.If((z.e == -126) & (z.m[23] == 0)):
326 m.d.sync += z.v[23:31].eq(0)
327 with m.If(z.is_overflowed()):
328 m.d.sync += z.inf(0)
329
330 # ******
331 # put_z stage
332
333 with m.State("put_z"):
334 m.next = "get_a"
335 m.d.sync += [
336 s_out_z_stb.eq(1),
337 s_out_z.eq(z)
338 ]
339 with m.If(s_out_z_stb & out_z_ack):
340 m.d.sync += [
341 s_out_z_stb.eq(0)
342 ]
343
344 return m
345
346 """
347 always @(posedge clk)
348 begin
349
350 case(state)
351
352 get_a:
353 begin
354 s_in_a_ack <= 1;
355 if (s_in_a_ack && in_a_stb) begin
356 a <= in_a;
357 s_in_a_ack <= 0;
358 state <= get_b;
359 end
360 end
361
362 get_b:
363 begin
364 s_in_b_ack <= 1;
365 if (s_in_b_ack && in_b_stb) begin
366 b <= in_b;
367 s_in_b_ack <= 0;
368 state <= unpack;
369 end
370 end
371
372 unpack:
373 begin
374 a_m <= {a[22 : 0], 3'd0};
375 b_m <= {b[22 : 0], 3'd0};
376 a_e <= a[30 : 23] - 127;
377 b_e <= b[30 : 23] - 127;
378 a_s <= a[31];
379 b_s <= b[31];
380 state <= special_cases;
381 end
382
383 special_cases:
384 begin
385 //if a is NaN or b is NaN return NaN
386 if ((a_e == 128 && a_m != 0) || (b_e == 128 && b_m != 0)) begin
387 z[31] <= 1;
388 z[30:23] <= 255;
389 z[22] <= 1;
390 z[21:0] <= 0;
391 state <= put_z;
392 //if a is inf return inf
393 end else if (a_e == 128) begin
394 z[31] <= a_s;
395 z[30:23] <= 255;
396 z[22:0] <= 0;
397 //if a is inf and signs don't match return nan
398 if ((b_e == 128) && (a_s != b_s)) begin
399 z[31] <= b_s;
400 z[30:23] <= 255;
401 z[22] <= 1;
402 z[21:0] <= 0;
403 end
404 state <= put_z;
405 //if b is inf return inf
406 end else if (b_e == 128) begin
407 z[31] <= b_s;
408 z[30:23] <= 255;
409 z[22:0] <= 0;
410 state <= put_z;
411 //if a is zero return b
412 end else if ((($signed(a_e) == -127) && (a_m == 0)) && (($signed(b_e) == -127) && (b_m == 0))) begin
413 z[31] <= a_s & b_s;
414 z[30:23] <= b_e[7:0] + 127;
415 z[22:0] <= b_m[26:3];
416 state <= put_z;
417 //if a is zero return b
418 end else if (($signed(a_e) == -127) && (a_m == 0)) begin
419 z[31] <= b_s;
420 z[30:23] <= b_e[7:0] + 127;
421 z[22:0] <= b_m[26:3];
422 state <= put_z;
423 //if b is zero return a
424 end else if (($signed(b_e) == -127) && (b_m == 0)) begin
425 z[31] <= a_s;
426 z[30:23] <= a_e[7:0] + 127;
427 z[22:0] <= a_m[26:3];
428 state <= put_z;
429 end else begin
430 //Denormalised Number
431 if ($signed(a_e) == -127) begin
432 a_e <= -126;
433 end else begin
434 a_m[26] <= 1;
435 end
436 //Denormalised Number
437 if ($signed(b_e) == -127) begin
438 b_e <= -126;
439 end else begin
440 b_m[26] <= 1;
441 end
442 state <= align;
443 end
444 end
445
446 align:
447 begin
448 if ($signed(a_e) > $signed(b_e)) begin
449 b_e <= b_e + 1;
450 b_m <= b_m >> 1;
451 b_m[0] <= b_m[0] | b_m[1];
452 end else if ($signed(a_e) < $signed(b_e)) begin
453 a_e <= a_e + 1;
454 a_m <= a_m >> 1;
455 a_m[0] <= a_m[0] | a_m[1];
456 end else begin
457 state <= add_0;
458 end
459 end
460
461 add_0:
462 begin
463 z_e <= a_e;
464 if (a_s == b_s) begin
465 tot <= a_m + b_m;
466 z_s <= a_s;
467 end else begin
468 if (a_m >= b_m) begin
469 tot <= a_m - b_m;
470 z_s <= a_s;
471 end else begin
472 tot <= b_m - a_m;
473 z_s <= b_s;
474 end
475 end
476 state <= add_1;
477 end
478
479 add_1:
480 begin
481 if (tot[27]) begin
482 z_m <= tot[27:4];
483 guard <= tot[3];
484 round_bit <= tot[2];
485 sticky <= tot[1] | tot[0];
486 z_e <= z_e + 1;
487 end else begin
488 z_m <= tot[26:3];
489 guard <= tot[2];
490 round_bit <= tot[1];
491 sticky <= tot[0];
492 end
493 state <= normalise_1;
494 end
495
496 normalise_1:
497 begin
498 if (z_m[23] == 0 && $signed(z_e) > -126) begin
499 z_e <= z_e - 1;
500 z_m <= z_m << 1;
501 z_m[0] <= guard;
502 guard <= round_bit;
503 round_bit <= 0;
504 end else begin
505 state <= normalise_2;
506 end
507 end
508
509 normalise_2:
510 begin
511 if ($signed(z_e) < -126) begin
512 z_e <= z_e + 1;
513 z_m <= z_m >> 1;
514 guard <= z_m[0];
515 round_bit <= guard;
516 sticky <= sticky | round_bit;
517 end else begin
518 state <= round;
519 end
520 end
521
522 round:
523 begin
524 if (guard && (round_bit | sticky | z_m[0])) begin
525 z_m <= z_m + 1;
526 if (z_m == 24'hffffff) begin
527 z_e <=z_e + 1;
528 end
529 end
530 state <= pack;
531 end
532
533 pack:
534 begin
535 z[22 : 0] <= z_m[22:0];
536 z[30 : 23] <= z_e[7:0] + 127;
537 z[31] <= z_s;
538 if ($signed(z_e) == -126 && z_m[23] == 0) begin
539 z[30 : 23] <= 0;
540 end
541 if ($signed(z_e) == -126 && z_m[23:0] == 24'h0) begin
542 z[31] <= 1'b0; // FIX SIGN BUG: -a + a = +0.
543 end
544 //if overflow occurs, return inf
545 if ($signed(z_e) > 127) begin
546 z[22 : 0] <= 0;
547 z[30 : 23] <= 255;
548 z[31] <= z_s;
549 end
550 state <= put_z;
551 end
552
553 put_z:
554 begin
555 s_out_z_stb <= 1;
556 s_out_z <= z;
557 if (s_out_z_stb && out_z_ack) begin
558 s_out_z_stb <= 0;
559 state <= get_a;
560 end
561 end
562
563 endcase
564
565 if (rst == 1) begin
566 state <= get_a;
567 s_in_a_ack <= 0;
568 s_in_b_ack <= 0;
569 s_out_z_stb <= 0;
570 end
571
572 end
573 assign in_a_ack = s_in_a_ack;
574 assign in_b_ack = s_in_b_ack;
575 assign out_z_stb = s_out_z_stb;
576 assign out_z = s_out_z;
577
578 endmodule
579 """
580
581 if __name__ == "__main__":
582 alu = FPADD(width=32)
583 main(alu, ports=[
584 alu.in_a, alu.in_a_stb, alu.in_a_ack,
585 alu.in_b, alu.in_b_stb, alu.in_b_ack,
586 alu.out_z, alu.out_z_stb, alu.out_z_ack,
587 ])
588
589
590 """
591 print(verilog.convert(alu, ports=[in_a, in_a_stb, in_a_ack, #doesnt work for some reason
592 in_b, in_b_stb, in_b_ack,
593 out_z, out_z_stb, out_z_ack]))
594 """