in the middle of rewiring FPADD to use FPADDBase
[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, Mux
6 from nmigen.lib.coding import PriorityEncoder
7 from nmigen.cli import main, verilog
8
9 from fpbase import FPNumIn, FPNumOut, FPOp, Overflow, FPBase, FPNumBase
10 from fpbase import MultiShiftRMerge, Trigger
11 #from fpbase import FPNumShiftMultiRight
12
13 class FPState(FPBase):
14 def __init__(self, state_from):
15 self.state_from = state_from
16
17 def set_inputs(self, inputs):
18 self.inputs = inputs
19 for k,v in inputs.items():
20 setattr(self, k, v)
21
22 def set_outputs(self, outputs):
23 self.outputs = outputs
24 for k,v in outputs.items():
25 setattr(self, k, v)
26
27
28 class FPGetOpMod:
29 def __init__(self, width):
30 self.in_op = FPOp(width)
31 self.out_op = Signal(width)
32 self.out_decode = Signal(reset_less=True)
33
34 def elaborate(self, platform):
35 m = Module()
36 m.d.comb += self.out_decode.eq((self.in_op.ack) & (self.in_op.stb))
37 #m.submodules.get_op_in = self.in_op
38 m.submodules.get_op_out = self.out_op
39 with m.If(self.out_decode):
40 m.d.comb += [
41 self.out_op.eq(self.in_op.v),
42 ]
43 return m
44
45
46 class FPGetOp(FPState):
47 """ gets operand
48 """
49
50 def __init__(self, in_state, out_state, in_op, width):
51 FPState.__init__(self, in_state)
52 self.out_state = out_state
53 self.mod = FPGetOpMod(width)
54 self.in_op = in_op
55 self.out_op = Signal(width)
56 self.out_decode = Signal(reset_less=True)
57
58 def setup(self, m, in_op):
59 """ links module to inputs and outputs
60 """
61 setattr(m.submodules, self.state_from, self.mod)
62 m.d.comb += self.mod.in_op.copy(in_op)
63 #m.d.comb += self.out_op.eq(self.mod.out_op)
64 m.d.comb += self.out_decode.eq(self.mod.out_decode)
65
66 def action(self, m):
67 with m.If(self.out_decode):
68 m.next = self.out_state
69 m.d.sync += [
70 self.in_op.ack.eq(0),
71 self.out_op.eq(self.mod.out_op)
72 ]
73 with m.Else():
74 m.d.sync += self.in_op.ack.eq(1)
75
76
77 class FPGet2OpMod(Trigger):
78 def __init__(self, width):
79 Trigger.__init__(self)
80 self.in_op1 = Signal(width, reset_less=True)
81 self.in_op2 = Signal(width, reset_less=True)
82 self.out_op1 = FPNumIn(None, width)
83 self.out_op2 = FPNumIn(None, width)
84
85 def elaborate(self, platform):
86 m = Trigger.elaborate(self, platform)
87 #m.submodules.get_op_in = self.in_op
88 m.submodules.get_op1_out = self.out_op1
89 m.submodules.get_op2_out = self.out_op2
90 with m.If(self.trigger):
91 m.d.comb += [
92 self.out_op1.decode(self.in_op1),
93 self.out_op2.decode(self.in_op2),
94 ]
95 return m
96
97
98 class FPGet2Op(FPState):
99 """ gets operands
100 """
101
102 def __init__(self, in_state, out_state, in_op1, in_op2, width):
103 FPState.__init__(self, in_state)
104 self.out_state = out_state
105 self.mod = FPGet2OpMod(width)
106 self.in_op1 = in_op1
107 self.in_op2 = in_op2
108 self.out_op1 = FPNumIn(None, width)
109 self.out_op2 = FPNumIn(None, width)
110 self.in_stb = Signal(reset_less=True)
111 self.out_ack = Signal(reset_less=True)
112 self.out_decode = Signal(reset_less=True)
113
114 def setup(self, m, in_op1, in_op2, in_stb):
115 """ links module to inputs and outputs
116 """
117 m.submodules.get_ops = self.mod
118 m.d.comb += self.mod.in_op1.eq(in_op1)
119 m.d.comb += self.mod.in_op2.eq(in_op2)
120 m.d.comb += self.mod.stb.eq(in_stb)
121 m.d.comb += self.out_ack.eq(self.mod.ack)
122 m.d.comb += self.out_decode.eq(self.mod.trigger)
123 #m.d.comb += self.out_op1.v.eq(self.mod.out_op1.v)
124 #m.d.comb += self.out_op2.v.eq(self.mod.out_op2.v)
125
126 def action(self, m):
127 with m.If(self.out_decode):
128 m.next = self.out_state
129 m.d.sync += [
130 self.mod.ack.eq(0),
131 #self.out_op1.v.eq(self.mod.out_op1.v),
132 #self.out_op2.v.eq(self.mod.out_op2.v),
133 self.out_op1.copy(self.mod.out_op1),
134 self.out_op2.copy(self.mod.out_op2)
135 ]
136 with m.Else():
137 m.d.sync += self.mod.ack.eq(1)
138
139
140 class FPAddSpecialCasesMod:
141 """ special cases: NaNs, infs, zeros, denormalised
142 NOTE: some of these are unique to add. see "Special Operations"
143 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
144 """
145
146 def __init__(self, width):
147 self.in_a = FPNumBase(width)
148 self.in_b = FPNumBase(width)
149 self.out_z = FPNumOut(width, False)
150 self.out_do_z = Signal(reset_less=True)
151
152 def elaborate(self, platform):
153 m = Module()
154
155 m.submodules.sc_in_a = self.in_a
156 m.submodules.sc_in_b = self.in_b
157 m.submodules.sc_out_z = self.out_z
158
159 s_nomatch = Signal()
160 m.d.comb += s_nomatch.eq(self.in_a.s != self.in_b.s)
161
162 m_match = Signal()
163 m.d.comb += m_match.eq(self.in_a.m == self.in_b.m)
164
165 # if a is NaN or b is NaN return NaN
166 with m.If(self.in_a.is_nan | self.in_b.is_nan):
167 m.d.comb += self.out_do_z.eq(1)
168 m.d.comb += self.out_z.nan(0)
169
170 # XXX WEIRDNESS for FP16 non-canonical NaN handling
171 # under review
172
173 ## if a is zero and b is NaN return -b
174 #with m.If(a.is_zero & (a.s==0) & b.is_nan):
175 # m.d.comb += self.out_do_z.eq(1)
176 # m.d.comb += z.create(b.s, b.e, Cat(b.m[3:-2], ~b.m[0]))
177
178 ## if b is zero and a is NaN return -a
179 #with m.Elif(b.is_zero & (b.s==0) & a.is_nan):
180 # m.d.comb += self.out_do_z.eq(1)
181 # m.d.comb += z.create(a.s, a.e, Cat(a.m[3:-2], ~a.m[0]))
182
183 ## if a is -zero and b is NaN return -b
184 #with m.Elif(a.is_zero & (a.s==1) & b.is_nan):
185 # m.d.comb += self.out_do_z.eq(1)
186 # m.d.comb += z.create(a.s & b.s, b.e, Cat(b.m[3:-2], 1))
187
188 ## if b is -zero and a is NaN return -a
189 #with m.Elif(b.is_zero & (b.s==1) & a.is_nan):
190 # m.d.comb += self.out_do_z.eq(1)
191 # m.d.comb += z.create(a.s & b.s, a.e, Cat(a.m[3:-2], 1))
192
193 # if a is inf return inf (or NaN)
194 with m.Elif(self.in_a.is_inf):
195 m.d.comb += self.out_do_z.eq(1)
196 m.d.comb += self.out_z.inf(self.in_a.s)
197 # if a is inf and signs don't match return NaN
198 with m.If(self.in_b.exp_128 & s_nomatch):
199 m.d.comb += self.out_z.nan(0)
200
201 # if b is inf return inf
202 with m.Elif(self.in_b.is_inf):
203 m.d.comb += self.out_do_z.eq(1)
204 m.d.comb += self.out_z.inf(self.in_b.s)
205
206 # if a is zero and b zero return signed-a/b
207 with m.Elif(self.in_a.is_zero & self.in_b.is_zero):
208 m.d.comb += self.out_do_z.eq(1)
209 m.d.comb += self.out_z.create(self.in_a.s & self.in_b.s,
210 self.in_b.e,
211 self.in_b.m[3:-1])
212
213 # if a is zero return b
214 with m.Elif(self.in_a.is_zero):
215 m.d.comb += self.out_do_z.eq(1)
216 m.d.comb += self.out_z.create(self.in_b.s, self.in_b.e,
217 self.in_b.m[3:-1])
218
219 # if b is zero return a
220 with m.Elif(self.in_b.is_zero):
221 m.d.comb += self.out_do_z.eq(1)
222 m.d.comb += self.out_z.create(self.in_a.s, self.in_a.e,
223 self.in_a.m[3:-1])
224
225 # if a equal to -b return zero (+ve zero)
226 with m.Elif(s_nomatch & m_match & (self.in_a.e == self.in_b.e)):
227 m.d.comb += self.out_do_z.eq(1)
228 m.d.comb += self.out_z.zero(0)
229
230 # Denormalised Number checks
231 with m.Else():
232 m.d.comb += self.out_do_z.eq(0)
233
234 return m
235
236
237 class FPID:
238 def __init__(self, id_wid):
239 self.id_wid = id_wid
240 if self.id_wid:
241 self.in_mid = Signal(id_wid, reset_less=True)
242 self.out_mid = Signal(id_wid, reset_less=True)
243 else:
244 self.in_mid = None
245 self.out_mid = None
246
247 def idsync(self, m):
248 if self.id_wid is not None:
249 m.d.sync += self.out_mid.eq(self.in_mid)
250
251
252 class FPAddSpecialCases(FPState, FPID):
253 """ special cases: NaNs, infs, zeros, denormalised
254 NOTE: some of these are unique to add. see "Special Operations"
255 https://steve.hollasch.net/cgindex/coding/ieeefloat.html
256 """
257
258 def __init__(self, width, id_wid):
259 FPState.__init__(self, "special_cases")
260 FPID.__init__(self, id_wid)
261 self.mod = FPAddSpecialCasesMod(width)
262 self.out_z = FPNumOut(width, False)
263 self.out_do_z = Signal(reset_less=True)
264
265 def setup(self, m, in_a, in_b, in_mid):
266 """ links module to inputs and outputs
267 """
268 m.submodules.specialcases = self.mod
269 m.d.comb += self.mod.in_a.copy(in_a)
270 m.d.comb += self.mod.in_b.copy(in_b)
271 #m.d.comb += self.out_z.v.eq(self.mod.out_z.v)
272 m.d.comb += self.out_do_z.eq(self.mod.out_do_z)
273 if self.in_mid is not None:
274 m.d.comb += self.in_mid.eq(in_mid)
275
276 def action(self, m):
277 self.idsync(m)
278 with m.If(self.out_do_z):
279 m.d.sync += self.out_z.v.eq(self.mod.out_z.v) # only take the output
280 m.next = "put_z"
281 with m.Else():
282 m.next = "denormalise"
283
284
285 class FPAddDeNormMod(FPState):
286
287 def __init__(self, width):
288 self.in_a = FPNumBase(width)
289 self.in_b = FPNumBase(width)
290 self.out_a = FPNumBase(width)
291 self.out_b = FPNumBase(width)
292
293 def elaborate(self, platform):
294 m = Module()
295 m.submodules.denorm_in_a = self.in_a
296 m.submodules.denorm_in_b = self.in_b
297 m.submodules.denorm_out_a = self.out_a
298 m.submodules.denorm_out_b = self.out_b
299 # hmmm, don't like repeating identical code
300 m.d.comb += self.out_a.copy(self.in_a)
301 with m.If(self.in_a.exp_n127):
302 m.d.comb += self.out_a.e.eq(self.in_a.N126) # limit a exponent
303 with m.Else():
304 m.d.comb += self.out_a.m[-1].eq(1) # set top mantissa bit
305
306 m.d.comb += self.out_b.copy(self.in_b)
307 with m.If(self.in_b.exp_n127):
308 m.d.comb += self.out_b.e.eq(self.in_b.N126) # limit a exponent
309 with m.Else():
310 m.d.comb += self.out_b.m[-1].eq(1) # set top mantissa bit
311
312 return m
313
314
315 class FPAddDeNorm(FPState, FPID):
316
317 def __init__(self, width, id_wid):
318 FPState.__init__(self, "denormalise")
319 FPID.__init__(self, id_wid)
320 self.mod = FPAddDeNormMod(width)
321 self.out_a = FPNumBase(width)
322 self.out_b = FPNumBase(width)
323
324 def setup(self, m, in_a, in_b, in_mid):
325 """ links module to inputs and outputs
326 """
327 m.submodules.denormalise = self.mod
328 m.d.comb += self.mod.in_a.copy(in_a)
329 m.d.comb += self.mod.in_b.copy(in_b)
330 if self.in_mid is not None:
331 m.d.comb += self.in_mid.eq(in_mid)
332
333 def action(self, m):
334 self.idsync(m)
335 # Denormalised Number checks
336 m.next = "align"
337 m.d.sync += self.out_a.copy(self.mod.out_a)
338 m.d.sync += self.out_b.copy(self.mod.out_b)
339
340
341 class FPAddAlignMultiMod(FPState):
342
343 def __init__(self, width):
344 self.in_a = FPNumBase(width)
345 self.in_b = FPNumBase(width)
346 self.out_a = FPNumIn(None, width)
347 self.out_b = FPNumIn(None, width)
348 self.exp_eq = Signal(reset_less=True)
349
350 def elaborate(self, platform):
351 # This one however (single-cycle) will do the shift
352 # in one go.
353
354 m = Module()
355
356 m.submodules.align_in_a = self.in_a
357 m.submodules.align_in_b = self.in_b
358 m.submodules.align_out_a = self.out_a
359 m.submodules.align_out_b = self.out_b
360
361 # NOTE: this does *not* do single-cycle multi-shifting,
362 # it *STAYS* in the align state until exponents match
363
364 # exponent of a greater than b: shift b down
365 m.d.comb += self.exp_eq.eq(0)
366 m.d.comb += self.out_a.copy(self.in_a)
367 m.d.comb += self.out_b.copy(self.in_b)
368 agtb = Signal(reset_less=True)
369 altb = Signal(reset_less=True)
370 m.d.comb += agtb.eq(self.in_a.e > self.in_b.e)
371 m.d.comb += altb.eq(self.in_a.e < self.in_b.e)
372 with m.If(agtb):
373 m.d.comb += self.out_b.shift_down(self.in_b)
374 # exponent of b greater than a: shift a down
375 with m.Elif(altb):
376 m.d.comb += self.out_a.shift_down(self.in_a)
377 # exponents equal: move to next stage.
378 with m.Else():
379 m.d.comb += self.exp_eq.eq(1)
380 return m
381
382
383 class FPAddAlignMulti(FPState, FPID):
384
385 def __init__(self, width, id_wid):
386 FPID.__init__(self, id_wid)
387 FPState.__init__(self, "align")
388 self.mod = FPAddAlignMultiMod(width)
389 self.out_a = FPNumIn(None, width)
390 self.out_b = FPNumIn(None, width)
391 self.exp_eq = Signal(reset_less=True)
392
393 def setup(self, m, in_a, in_b, in_mid):
394 """ links module to inputs and outputs
395 """
396 m.submodules.align = self.mod
397 m.d.comb += self.mod.in_a.copy(in_a)
398 m.d.comb += self.mod.in_b.copy(in_b)
399 #m.d.comb += self.out_a.copy(self.mod.out_a)
400 #m.d.comb += self.out_b.copy(self.mod.out_b)
401 m.d.comb += self.exp_eq.eq(self.mod.exp_eq)
402 if self.in_mid is not None:
403 m.d.comb += self.in_mid.eq(in_mid)
404
405 def action(self, m):
406 self.idsync(m)
407 m.d.sync += self.out_a.copy(self.mod.out_a)
408 m.d.sync += self.out_b.copy(self.mod.out_b)
409 with m.If(self.exp_eq):
410 m.next = "add_0"
411
412
413 class FPAddAlignSingleMod:
414
415 def __init__(self, width):
416 self.width = width
417 self.in_a = FPNumBase(width)
418 self.in_b = FPNumBase(width)
419 self.out_a = FPNumIn(None, width)
420 self.out_b = FPNumIn(None, width)
421
422 def elaborate(self, platform):
423 """ Aligns A against B or B against A, depending on which has the
424 greater exponent. This is done in a *single* cycle using
425 variable-width bit-shift
426
427 the shifter used here is quite expensive in terms of gates.
428 Mux A or B in (and out) into temporaries, as only one of them
429 needs to be aligned against the other
430 """
431 m = Module()
432
433 m.submodules.align_in_a = self.in_a
434 m.submodules.align_in_b = self.in_b
435 m.submodules.align_out_a = self.out_a
436 m.submodules.align_out_b = self.out_b
437
438 # temporary (muxed) input and output to be shifted
439 t_inp = FPNumBase(self.width)
440 t_out = FPNumIn(None, self.width)
441 espec = (len(self.in_a.e), True)
442 msr = MultiShiftRMerge(self.in_a.m_width, espec)
443 m.submodules.align_t_in = t_inp
444 m.submodules.align_t_out = t_out
445 m.submodules.multishift_r = msr
446
447 ediff = Signal(espec, reset_less=True)
448 ediffr = Signal(espec, reset_less=True)
449 tdiff = Signal(espec, reset_less=True)
450 elz = Signal(reset_less=True)
451 egz = Signal(reset_less=True)
452
453 # connect multi-shifter to t_inp/out mantissa (and tdiff)
454 m.d.comb += msr.inp.eq(t_inp.m)
455 m.d.comb += msr.diff.eq(tdiff)
456 m.d.comb += t_out.m.eq(msr.m)
457 m.d.comb += t_out.e.eq(t_inp.e + tdiff)
458 m.d.comb += t_out.s.eq(t_inp.s)
459
460 m.d.comb += ediff.eq(self.in_a.e - self.in_b.e)
461 m.d.comb += ediffr.eq(self.in_b.e - self.in_a.e)
462 m.d.comb += elz.eq(self.in_a.e < self.in_b.e)
463 m.d.comb += egz.eq(self.in_a.e > self.in_b.e)
464
465 # default: A-exp == B-exp, A and B untouched (fall through)
466 m.d.comb += self.out_a.copy(self.in_a)
467 m.d.comb += self.out_b.copy(self.in_b)
468 # only one shifter (muxed)
469 #m.d.comb += t_out.shift_down_multi(tdiff, t_inp)
470 # exponent of a greater than b: shift b down
471 with m.If(egz):
472 m.d.comb += [t_inp.copy(self.in_b),
473 tdiff.eq(ediff),
474 self.out_b.copy(t_out),
475 self.out_b.s.eq(self.in_b.s), # whoops forgot sign
476 ]
477 # exponent of b greater than a: shift a down
478 with m.Elif(elz):
479 m.d.comb += [t_inp.copy(self.in_a),
480 tdiff.eq(ediffr),
481 self.out_a.copy(t_out),
482 self.out_a.s.eq(self.in_a.s), # whoops forgot sign
483 ]
484 return m
485
486
487 class FPAddAlignSingle(FPState, FPID):
488
489 def __init__(self, width, id_wid):
490 FPState.__init__(self, "align")
491 FPID.__init__(self, id_wid)
492 self.mod = FPAddAlignSingleMod(width)
493 self.out_a = FPNumIn(None, width)
494 self.out_b = FPNumIn(None, width)
495
496 def setup(self, m, in_a, in_b, in_mid):
497 """ links module to inputs and outputs
498 """
499 m.submodules.align = self.mod
500 m.d.comb += self.mod.in_a.copy(in_a)
501 m.d.comb += self.mod.in_b.copy(in_b)
502 if self.in_mid is not None:
503 m.d.comb += self.in_mid.eq(in_mid)
504
505 def action(self, m):
506 self.idsync(m)
507 # NOTE: could be done as comb
508 m.d.sync += self.out_a.copy(self.mod.out_a)
509 m.d.sync += self.out_b.copy(self.mod.out_b)
510 m.next = "add_0"
511
512
513 class FPAddStage0Mod:
514
515 def __init__(self, width):
516 self.in_a = FPNumBase(width)
517 self.in_b = FPNumBase(width)
518 self.in_z = FPNumBase(width, False)
519 self.out_z = FPNumBase(width, False)
520 self.out_tot = Signal(self.out_z.m_width + 4, reset_less=True)
521
522 def elaborate(self, platform):
523 m = Module()
524 m.submodules.add0_in_a = self.in_a
525 m.submodules.add0_in_b = self.in_b
526 m.submodules.add0_out_z = self.out_z
527
528 m.d.comb += self.out_z.e.eq(self.in_a.e)
529
530 # store intermediate tests (and zero-extended mantissas)
531 seq = Signal(reset_less=True)
532 mge = Signal(reset_less=True)
533 am0 = Signal(len(self.in_a.m)+1, reset_less=True)
534 bm0 = Signal(len(self.in_b.m)+1, reset_less=True)
535 m.d.comb += [seq.eq(self.in_a.s == self.in_b.s),
536 mge.eq(self.in_a.m >= self.in_b.m),
537 am0.eq(Cat(self.in_a.m, 0)),
538 bm0.eq(Cat(self.in_b.m, 0))
539 ]
540 # same-sign (both negative or both positive) add mantissas
541 with m.If(seq):
542 m.d.comb += [
543 self.out_tot.eq(am0 + bm0),
544 self.out_z.s.eq(self.in_a.s)
545 ]
546 # a mantissa greater than b, use a
547 with m.Elif(mge):
548 m.d.comb += [
549 self.out_tot.eq(am0 - bm0),
550 self.out_z.s.eq(self.in_a.s)
551 ]
552 # b mantissa greater than a, use b
553 with m.Else():
554 m.d.comb += [
555 self.out_tot.eq(bm0 - am0),
556 self.out_z.s.eq(self.in_b.s)
557 ]
558 return m
559
560
561 class FPAddStage0(FPState, FPID):
562 """ First stage of add. covers same-sign (add) and subtract
563 special-casing when mantissas are greater or equal, to
564 give greatest accuracy.
565 """
566
567 def __init__(self, width, id_wid):
568 FPState.__init__(self, "add_0")
569 FPID.__init__(self, id_wid)
570 self.mod = FPAddStage0Mod(width)
571 self.out_z = FPNumBase(width, False)
572 self.out_tot = Signal(self.out_z.m_width + 4, reset_less=True)
573
574 def setup(self, m, in_a, in_b, in_mid):
575 """ links module to inputs and outputs
576 """
577 m.submodules.add0 = self.mod
578 m.d.comb += self.mod.in_a.copy(in_a)
579 m.d.comb += self.mod.in_b.copy(in_b)
580 if self.in_mid is not None:
581 m.d.comb += self.in_mid.eq(in_mid)
582
583 def action(self, m):
584 self.idsync(m)
585 # NOTE: these could be done as combinatorial (merge add0+add1)
586 m.d.sync += self.out_z.copy(self.mod.out_z)
587 m.d.sync += self.out_tot.eq(self.mod.out_tot)
588 m.next = "add_1"
589
590
591 class FPAddStage1Mod(FPState):
592 """ Second stage of add: preparation for normalisation.
593 detects when tot sum is too big (tot[27] is kinda a carry bit)
594 """
595
596 def __init__(self, width):
597 self.out_norm = Signal(reset_less=True)
598 self.in_z = FPNumBase(width, False)
599 self.in_tot = Signal(self.in_z.m_width + 4, reset_less=True)
600 self.out_z = FPNumBase(width, False)
601 self.out_of = Overflow()
602
603 def elaborate(self, platform):
604 m = Module()
605 #m.submodules.norm1_in_overflow = self.in_of
606 #m.submodules.norm1_out_overflow = self.out_of
607 #m.submodules.norm1_in_z = self.in_z
608 #m.submodules.norm1_out_z = self.out_z
609 m.d.comb += self.out_z.copy(self.in_z)
610 # tot[27] gets set when the sum overflows. shift result down
611 with m.If(self.in_tot[-1]):
612 m.d.comb += [
613 self.out_z.m.eq(self.in_tot[4:]),
614 self.out_of.m0.eq(self.in_tot[4]),
615 self.out_of.guard.eq(self.in_tot[3]),
616 self.out_of.round_bit.eq(self.in_tot[2]),
617 self.out_of.sticky.eq(self.in_tot[1] | self.in_tot[0]),
618 self.out_z.e.eq(self.in_z.e + 1)
619 ]
620 # tot[27] zero case
621 with m.Else():
622 m.d.comb += [
623 self.out_z.m.eq(self.in_tot[3:]),
624 self.out_of.m0.eq(self.in_tot[3]),
625 self.out_of.guard.eq(self.in_tot[2]),
626 self.out_of.round_bit.eq(self.in_tot[1]),
627 self.out_of.sticky.eq(self.in_tot[0])
628 ]
629 return m
630
631
632 class FPAddStage1(FPState, FPID):
633
634 def __init__(self, width, id_wid):
635 FPState.__init__(self, "add_1")
636 FPID.__init__(self, id_wid)
637 self.mod = FPAddStage1Mod(width)
638 self.out_z = FPNumBase(width, False)
639 self.out_of = Overflow()
640 self.norm_stb = Signal()
641
642 def setup(self, m, in_tot, in_z, in_mid):
643 """ links module to inputs and outputs
644 """
645 m.submodules.add1 = self.mod
646 m.submodules.add1_out_overflow = self.out_of
647
648 m.d.comb += self.mod.in_z.copy(in_z)
649 m.d.comb += self.mod.in_tot.eq(in_tot)
650
651 m.d.sync += self.norm_stb.eq(0) # sets to zero when not in add1 state
652
653 if self.in_mid is not None:
654 m.d.comb += self.in_mid.eq(in_mid)
655
656 def action(self, m):
657 self.idsync(m)
658 m.d.sync += self.out_of.copy(self.mod.out_of)
659 m.d.sync += self.out_z.copy(self.mod.out_z)
660 m.d.sync += self.norm_stb.eq(1)
661 m.next = "normalise_1"
662
663
664 class FPNorm1ModSingle:
665
666 def __init__(self, width):
667 self.width = width
668 self.in_select = Signal(reset_less=True)
669 self.out_norm = Signal(reset_less=True)
670 self.in_z = FPNumBase(width, False)
671 self.in_of = Overflow()
672 self.temp_z = FPNumBase(width, False)
673 self.temp_of = Overflow()
674 self.out_z = FPNumBase(width, False)
675 self.out_of = Overflow()
676
677 def elaborate(self, platform):
678 m = Module()
679
680 mwid = self.out_z.m_width+2
681 pe = PriorityEncoder(mwid)
682 m.submodules.norm_pe = pe
683
684 m.submodules.norm1_out_z = self.out_z
685 m.submodules.norm1_out_overflow = self.out_of
686 m.submodules.norm1_temp_z = self.temp_z
687 m.submodules.norm1_temp_of = self.temp_of
688 m.submodules.norm1_in_z = self.in_z
689 m.submodules.norm1_in_overflow = self.in_of
690
691 in_z = FPNumBase(self.width, False)
692 in_of = Overflow()
693 m.submodules.norm1_insel_z = in_z
694 m.submodules.norm1_insel_overflow = in_of
695
696 espec = (len(in_z.e), True)
697 ediff_n126 = Signal(espec, reset_less=True)
698 msr = MultiShiftRMerge(mwid, espec)
699 m.submodules.multishift_r = msr
700
701 # select which of temp or in z/of to use
702 with m.If(self.in_select):
703 m.d.comb += in_z.copy(self.in_z)
704 m.d.comb += in_of.copy(self.in_of)
705 with m.Else():
706 m.d.comb += in_z.copy(self.temp_z)
707 m.d.comb += in_of.copy(self.temp_of)
708 # initialise out from in (overridden below)
709 m.d.comb += self.out_z.copy(in_z)
710 m.d.comb += self.out_of.copy(in_of)
711 # normalisation increase/decrease conditions
712 decrease = Signal(reset_less=True)
713 increase = Signal(reset_less=True)
714 m.d.comb += decrease.eq(in_z.m_msbzero & in_z.exp_gt_n126)
715 m.d.comb += increase.eq(in_z.exp_lt_n126)
716 m.d.comb += self.out_norm.eq(0) # loop-end condition
717 # decrease exponent
718 with m.If(decrease):
719 # *sigh* not entirely obvious: count leading zeros (clz)
720 # with a PriorityEncoder: to find from the MSB
721 # we reverse the order of the bits.
722 temp_m = Signal(mwid, reset_less=True)
723 temp_s = Signal(mwid+1, reset_less=True)
724 clz = Signal((len(in_z.e), True), reset_less=True)
725 # make sure that the amount to decrease by does NOT
726 # go below the minimum non-INF/NaN exponent
727 limclz = Mux(in_z.exp_sub_n126 > pe.o, pe.o,
728 in_z.exp_sub_n126)
729 m.d.comb += [
730 # cat round and guard bits back into the mantissa
731 temp_m.eq(Cat(in_of.round_bit, in_of.guard, in_z.m)),
732 pe.i.eq(temp_m[::-1]), # inverted
733 clz.eq(limclz), # count zeros from MSB down
734 temp_s.eq(temp_m << clz), # shift mantissa UP
735 self.out_z.e.eq(in_z.e - clz), # DECREASE exponent
736 self.out_z.m.eq(temp_s[2:]), # exclude bits 0&1
737 self.out_of.m0.eq(temp_s[2]), # copy of mantissa[0]
738 # overflow in bits 0..1: got shifted too (leave sticky)
739 self.out_of.guard.eq(temp_s[1]), # guard
740 self.out_of.round_bit.eq(temp_s[0]), # round
741 ]
742 # increase exponent
743 with m.Elif(increase):
744 temp_m = Signal(mwid+1, reset_less=True)
745 m.d.comb += [
746 temp_m.eq(Cat(in_of.sticky, in_of.round_bit, in_of.guard,
747 in_z.m)),
748 ediff_n126.eq(in_z.N126 - in_z.e),
749 # connect multi-shifter to inp/out mantissa (and ediff)
750 msr.inp.eq(temp_m),
751 msr.diff.eq(ediff_n126),
752 self.out_z.m.eq(msr.m[3:]),
753 self.out_of.m0.eq(temp_s[3]), # copy of mantissa[0]
754 # overflow in bits 0..1: got shifted too (leave sticky)
755 self.out_of.guard.eq(temp_s[2]), # guard
756 self.out_of.round_bit.eq(temp_s[1]), # round
757 self.out_of.sticky.eq(temp_s[0]), # sticky
758 self.out_z.e.eq(in_z.e + ediff_n126),
759 ]
760
761 return m
762
763
764 class FPNorm1ModMulti:
765
766 def __init__(self, width, single_cycle=True):
767 self.width = width
768 self.in_select = Signal(reset_less=True)
769 self.out_norm = Signal(reset_less=True)
770 self.in_z = FPNumBase(width, False)
771 self.in_of = Overflow()
772 self.temp_z = FPNumBase(width, False)
773 self.temp_of = Overflow()
774 self.out_z = FPNumBase(width, False)
775 self.out_of = Overflow()
776
777 def elaborate(self, platform):
778 m = Module()
779
780 m.submodules.norm1_out_z = self.out_z
781 m.submodules.norm1_out_overflow = self.out_of
782 m.submodules.norm1_temp_z = self.temp_z
783 m.submodules.norm1_temp_of = self.temp_of
784 m.submodules.norm1_in_z = self.in_z
785 m.submodules.norm1_in_overflow = self.in_of
786
787 in_z = FPNumBase(self.width, False)
788 in_of = Overflow()
789 m.submodules.norm1_insel_z = in_z
790 m.submodules.norm1_insel_overflow = in_of
791
792 # select which of temp or in z/of to use
793 with m.If(self.in_select):
794 m.d.comb += in_z.copy(self.in_z)
795 m.d.comb += in_of.copy(self.in_of)
796 with m.Else():
797 m.d.comb += in_z.copy(self.temp_z)
798 m.d.comb += in_of.copy(self.temp_of)
799 # initialise out from in (overridden below)
800 m.d.comb += self.out_z.copy(in_z)
801 m.d.comb += self.out_of.copy(in_of)
802 # normalisation increase/decrease conditions
803 decrease = Signal(reset_less=True)
804 increase = Signal(reset_less=True)
805 m.d.comb += decrease.eq(in_z.m_msbzero & in_z.exp_gt_n126)
806 m.d.comb += increase.eq(in_z.exp_lt_n126)
807 m.d.comb += self.out_norm.eq(decrease | increase) # loop-end
808 # decrease exponent
809 with m.If(decrease):
810 m.d.comb += [
811 self.out_z.e.eq(in_z.e - 1), # DECREASE exponent
812 self.out_z.m.eq(in_z.m << 1), # shift mantissa UP
813 self.out_z.m[0].eq(in_of.guard), # steal guard (was tot[2])
814 self.out_of.guard.eq(in_of.round_bit), # round (was tot[1])
815 self.out_of.round_bit.eq(0), # reset round bit
816 self.out_of.m0.eq(in_of.guard),
817 ]
818 # increase exponent
819 with m.Elif(increase):
820 m.d.comb += [
821 self.out_z.e.eq(in_z.e + 1), # INCREASE exponent
822 self.out_z.m.eq(in_z.m >> 1), # shift mantissa DOWN
823 self.out_of.guard.eq(in_z.m[0]),
824 self.out_of.m0.eq(in_z.m[1]),
825 self.out_of.round_bit.eq(in_of.guard),
826 self.out_of.sticky.eq(in_of.sticky | in_of.round_bit)
827 ]
828
829 return m
830
831
832 class FPNorm1(FPState, FPID):
833
834 def __init__(self, width, id_wid, single_cycle=True):
835 FPID.__init__(self, id_wid)
836 FPState.__init__(self, "normalise_1")
837 if single_cycle:
838 self.mod = FPNorm1ModSingle(width)
839 else:
840 self.mod = FPNorm1ModMulti(width)
841 self.stb = Signal(reset_less=True)
842 self.ack = Signal(reset=0, reset_less=True)
843 self.out_norm = Signal(reset_less=True)
844 self.in_accept = Signal(reset_less=True)
845 self.temp_z = FPNumBase(width)
846 self.temp_of = Overflow()
847 self.out_z = FPNumBase(width)
848 self.out_roundz = Signal(reset_less=True)
849
850 def setup(self, m, in_z, in_of, norm_stb, in_mid):
851 """ links module to inputs and outputs
852 """
853 m.submodules.normalise_1 = self.mod
854
855 m.d.comb += self.mod.in_z.copy(in_z)
856 m.d.comb += self.mod.in_of.copy(in_of)
857
858 m.d.comb += self.mod.in_select.eq(self.in_accept)
859 m.d.comb += self.mod.temp_z.copy(self.temp_z)
860 m.d.comb += self.mod.temp_of.copy(self.temp_of)
861
862 m.d.comb += self.out_z.copy(self.mod.out_z)
863 m.d.comb += self.out_norm.eq(self.mod.out_norm)
864
865 m.d.comb += self.stb.eq(norm_stb)
866 m.d.sync += self.ack.eq(0) # sets to zero when not in normalise_1 state
867
868 if self.in_mid is not None:
869 m.d.comb += self.in_mid.eq(in_mid)
870
871 def action(self, m):
872 self.idsync(m)
873 m.d.comb += self.in_accept.eq((~self.ack) & (self.stb))
874 m.d.sync += self.temp_of.copy(self.mod.out_of)
875 m.d.sync += self.temp_z.copy(self.out_z)
876 with m.If(self.out_norm):
877 with m.If(self.in_accept):
878 m.d.sync += [
879 self.ack.eq(1),
880 ]
881 with m.Else():
882 m.d.sync += self.ack.eq(0)
883 with m.Else():
884 # normalisation not required (or done).
885 m.next = "round"
886 m.d.sync += self.ack.eq(1)
887 m.d.sync += self.out_roundz.eq(self.mod.out_of.roundz)
888
889
890 class FPRoundMod:
891
892 def __init__(self, width):
893 self.in_roundz = Signal(reset_less=True)
894 self.in_z = FPNumBase(width, False)
895 self.out_z = FPNumBase(width, False)
896
897 def elaborate(self, platform):
898 m = Module()
899 m.d.comb += self.out_z.copy(self.in_z)
900 with m.If(self.in_roundz):
901 m.d.comb += self.out_z.m.eq(self.in_z.m + 1) # mantissa rounds up
902 with m.If(self.in_z.m == self.in_z.m1s): # all 1s
903 m.d.comb += self.out_z.e.eq(self.in_z.e + 1) # exponent up
904 return m
905
906
907 class FPRound(FPState, FPID):
908
909 def __init__(self, width, id_wid):
910 FPState.__init__(self, "round")
911 FPID.__init__(self, id_wid)
912 self.mod = FPRoundMod(width)
913 self.out_z = FPNumBase(width)
914
915 def setup(self, m, in_z, roundz, in_mid):
916 """ links module to inputs and outputs
917 """
918 m.submodules.roundz = self.mod
919
920 m.d.comb += self.mod.in_z.copy(in_z)
921 m.d.comb += self.mod.in_roundz.eq(roundz)
922 if self.in_mid is not None:
923 m.d.comb += self.in_mid.eq(in_mid)
924
925 def action(self, m):
926 self.idsync(m)
927 m.d.sync += self.out_z.copy(self.mod.out_z)
928 m.next = "corrections"
929
930
931 class FPCorrectionsMod:
932
933 def __init__(self, width):
934 self.in_z = FPNumOut(width, False)
935 self.out_z = FPNumOut(width, False)
936
937 def elaborate(self, platform):
938 m = Module()
939 m.submodules.corr_in_z = self.in_z
940 m.submodules.corr_out_z = self.out_z
941 m.d.comb += self.out_z.copy(self.in_z)
942 with m.If(self.in_z.is_denormalised):
943 m.d.comb += self.out_z.e.eq(self.in_z.N127)
944 return m
945
946
947 class FPCorrections(FPState, FPID):
948
949 def __init__(self, width, id_wid):
950 FPState.__init__(self, "corrections")
951 FPID.__init__(self, id_wid)
952 self.mod = FPCorrectionsMod(width)
953 self.out_z = FPNumBase(width)
954
955 def setup(self, m, in_z, in_mid):
956 """ links module to inputs and outputs
957 """
958 m.submodules.corrections = self.mod
959 m.d.comb += self.mod.in_z.copy(in_z)
960 if self.in_mid is not None:
961 m.d.comb += self.in_mid.eq(in_mid)
962
963 def action(self, m):
964 self.idsync(m)
965 m.d.sync += self.out_z.copy(self.mod.out_z)
966 m.next = "pack"
967
968
969 class FPPackMod:
970
971 def __init__(self, width):
972 self.in_z = FPNumOut(width, False)
973 self.out_z = FPNumOut(width, False)
974
975 def elaborate(self, platform):
976 m = Module()
977 m.submodules.pack_in_z = self.in_z
978 with m.If(self.in_z.is_overflowed):
979 m.d.comb += self.out_z.inf(self.in_z.s)
980 with m.Else():
981 m.d.comb += self.out_z.create(self.in_z.s, self.in_z.e, self.in_z.m)
982 return m
983
984
985 class FPPack(FPState, FPID):
986
987 def __init__(self, width, id_wid):
988 FPState.__init__(self, "pack")
989 FPID.__init__(self, id_wid)
990 self.mod = FPPackMod(width)
991 self.out_z = FPNumOut(width, False)
992
993 def setup(self, m, in_z, in_mid):
994 """ links module to inputs and outputs
995 """
996 m.submodules.pack = self.mod
997 m.d.comb += self.mod.in_z.copy(in_z)
998 if self.in_mid is not None:
999 m.d.comb += self.in_mid.eq(in_mid)
1000
1001 def action(self, m):
1002 self.idsync(m)
1003 m.d.sync += self.out_z.v.eq(self.mod.out_z.v)
1004 m.next = "pack_put_z"
1005
1006
1007 class FPPutZ(FPState):
1008
1009 def __init__(self, state, in_z, out_z, in_mid, out_mid):
1010 FPState.__init__(self, state)
1011 self.in_z = in_z
1012 self.out_z = out_z
1013 self.in_mid = in_mid
1014 self.out_mid = out_mid
1015
1016 def action(self, m):
1017 if self.in_mid is not None:
1018 m.d.sync += self.out_mid.eq(self.in_mid)
1019 m.d.sync += [
1020 self.out_z.v.eq(self.in_z.v)
1021 ]
1022 with m.If(self.out_z.stb & self.out_z.ack):
1023 m.d.sync += self.out_z.stb.eq(0)
1024 m.next = "get_ops"
1025 with m.Else():
1026 m.d.sync += self.out_z.stb.eq(1)
1027
1028
1029 class FPADDBaseMod(FPID):
1030
1031 def __init__(self, width, id_wid=None, single_cycle=False):
1032 """ IEEE754 FP Add
1033
1034 * width: bit-width of IEEE754. supported: 16, 32, 64
1035 * id_wid: an identifier that is sync-connected to the input
1036 * single_cycle: True indicates each stage to complete in 1 clock
1037 """
1038 FPID.__init__(self, id_wid)
1039 self.width = width
1040 self.single_cycle = single_cycle
1041
1042 self.in_t = Trigger()
1043 self.in_a = Signal(width)
1044 self.in_b = Signal(width)
1045 self.out_z = FPOp(width)
1046
1047 self.states = []
1048
1049 def add_state(self, state):
1050 self.states.append(state)
1051 return state
1052
1053 def get_fragment(self, platform=None):
1054 """ creates the HDL code-fragment for FPAdd
1055 """
1056 m = Module()
1057 m.submodules.out_z = self.out_z
1058 m.submodules.in_t = self.in_t
1059
1060 get = self.add_state(FPGet2Op("get_ops", "special_cases",
1061 self.in_a, self.in_b, self.width))
1062 get.setup(m, self.in_a, self.in_b, self.in_t.stb)
1063 m.d.comb += self.in_t.ack.eq(get.mod.ack)
1064 a = get.out_op1
1065 b = get.out_op2
1066
1067 sc = self.add_state(FPAddSpecialCases(self.width, self.id_wid))
1068 sc.setup(m, a, b, self.in_mid)
1069
1070 dn = self.add_state(FPAddDeNorm(self.width, self.id_wid))
1071 dn.setup(m, a, b, sc.in_mid)
1072
1073 if self.single_cycle:
1074 alm = self.add_state(FPAddAlignSingle(self.width, self.id_wid))
1075 alm.setup(m, dn.out_a, dn.out_b, dn.in_mid)
1076 else:
1077 alm = self.add_state(FPAddAlignMulti(self.width, self.id_wid))
1078 alm.setup(m, dn.out_a, dn.out_b, dn.in_mid)
1079
1080 add0 = self.add_state(FPAddStage0(self.width, self.id_wid))
1081 add0.setup(m, alm.out_a, alm.out_b, alm.in_mid)
1082
1083 add1 = self.add_state(FPAddStage1(self.width, self.id_wid))
1084 add1.setup(m, add0.out_tot, add0.out_z, add0.in_mid)
1085
1086 n1 = self.add_state(FPNorm1(self.width, self.id_wid))
1087 n1.setup(m, add1.out_z, add1.out_of, add1.norm_stb, add0.in_mid)
1088
1089 rn = self.add_state(FPRound(self.width, self.id_wid))
1090 rn.setup(m, n1.out_z, n1.out_roundz, n1.in_mid)
1091
1092 cor = self.add_state(FPCorrections(self.width, self.id_wid))
1093 cor.setup(m, rn.out_z, rn.in_mid)
1094
1095 pa = self.add_state(FPPack(self.width, self.id_wid))
1096 pa.setup(m, cor.out_z, rn.in_mid)
1097
1098 ppz = self.add_state(FPPutZ("pack_put_z", pa.out_z, self.out_z,
1099 pa.in_mid, self.out_mid))
1100
1101 pz = self.add_state(FPPutZ("put_z", sc.out_z, self.out_z,
1102 pa.in_mid, self.out_mid))
1103
1104 with m.FSM() as fsm:
1105
1106 for state in self.states:
1107 with m.State(state.state_from):
1108 state.action(m)
1109
1110 return m
1111
1112 class FPADDBase(FPID):
1113
1114 def __init__(self, width, id_wid=None, single_cycle=False):
1115 """ IEEE754 FP Add
1116
1117 * width: bit-width of IEEE754. supported: 16, 32, 64
1118 * id_wid: an identifier that is sync-connected to the input
1119 * single_cycle: True indicates each stage to complete in 1 clock
1120 """
1121 FPID.__init__(self, id_wid)
1122 self.width = width
1123 self.single_cycle = single_cycle
1124 self.mod = FPADDBaseMod(width, id_wid, single_cycle)
1125
1126 self.in_t = Trigger()
1127 self.in_a = Signal(width)
1128 self.in_b = Signal(width)
1129 self.out_z = FPOp(width)
1130
1131 self.in_accept = Signal(reset_less=True)
1132 self.stb = Signal(reset_less=True)
1133 self.ack = Signal(reset=0, reset_less=True)
1134
1135 def setup(self, a, b, add_stb):
1136 m.d.comb += [self.in_a.eq(a),
1137 self.in_b.eq(b),
1138 self.in_mid.eq(self.in_mod),
1139
1140 ]
1141
1142 m.d.comb += self.stb.eq(add_stb)
1143 m.d.sync += self.ack.eq(0) # sets to zero when not in normalise_1 state
1144
1145 m.submodules.add = ab
1146
1147 def action(self, m):
1148
1149 m.d.comb += self.in_accept.eq((~self.ack) & (self.stb))
1150
1151 with m.If(self.out_norm):
1152 with m.If(self.in_accept):
1153 m.d.sync += [
1154 self.ack.eq(1),
1155 ]
1156 with m.Else():
1157 m.d.sync += self.ack.eq(0)
1158 with m.Else():
1159 # normalisation not required (or done).
1160 m.next = "round"
1161 m.d.sync += self.ack.eq(1)
1162 m.d.sync += self.out_roundz.eq(self.mod.out_of.roundz)
1163
1164 if self.in_mid is not None:
1165 m.d.sync += self.out_mid.eq(self.in_mid)
1166
1167 m.d.sync += [
1168 self.out_z.v.eq(self.in_z.v)
1169 ]
1170 # move to output state on detecting z
1171 with m.If(self.out_z.stb & self.out_z.ack):
1172 m.d.sync += self.out_z.stb.eq(0)
1173 m.next = "put_z"
1174 with m.Else():
1175 m.d.sync += self.out_z.stb.eq(1)
1176
1177
1178 class FPADD(FPID):
1179 """ FPADD: stages as follows:
1180
1181 FPGetOp (a)
1182 |
1183 FPGetOp (b)
1184 |
1185 FPAddBase---> GetOps->Specials->Align->Add1/2->Norm->Round/Pack->PutZ
1186 |
1187 PutZ
1188
1189 FPAddBase is tricky: it is both a stage and *has* stages.
1190 """
1191
1192 def __init__(self, width, id_wid=None, single_cycle=False):
1193 """ IEEE754 FP Add
1194
1195 * width: bit-width of IEEE754. supported: 16, 32, 64
1196 * id_wid: an identifier that is sync-connected to the input
1197 * single_cycle: True indicates each stage to complete in 1 clock
1198 """
1199 FPID.__init__(self, id_wid)
1200 self.width = width
1201 self.id_wid = id_wid
1202 self.single_cycle = single_cycle
1203
1204 self.in_a = FPOp(width)
1205 self.in_b = FPOp(width)
1206 self.out_z = FPOp(width)
1207
1208 self.states = []
1209
1210 def add_state(self, state):
1211 self.states.append(state)
1212 return state
1213
1214 def get_fragment(self, platform=None):
1215 """ creates the HDL code-fragment for FPAdd
1216 """
1217 m = Module()
1218 m.submodules.in_a = self.in_a
1219 m.submodules.in_b = self.in_b
1220 m.submodules.out_z = self.out_z
1221
1222 geta = self.add_state(FPGetOp("get_a", "get_b",
1223 self.in_a, self.width))
1224 geta.setup(m, self.in_a)
1225 a = geta.out_op
1226
1227 getb = self.add_state(FPGetOp("get_b", "add",
1228 self.in_b, self.width))
1229 getb.setup(m, self.in_b)
1230 b = getb.out_op
1231
1232 ab = FPADDBase(self.width, self.id_wid, self.single_cycle))
1233 ab = self.add_state("add", ab)
1234
1235 pz = self.add_state(FPPutZ("put_z", ab.out_z, self.out_z,
1236 ab.out_mid, self.out_mid))
1237
1238 with m.FSM() as fsm:
1239
1240 for state in self.states:
1241 with m.State(state.state_from):
1242 state.action(m)
1243
1244 return m
1245
1246
1247 if __name__ == "__main__":
1248 alu = FPADDBase(width=32, id_wid=5, single_cycle=True)
1249 main(alu, ports=[alu.in_a, alu.in_b] + \
1250 alu.in_t.ports() + \
1251 alu.out_z.ports() + \
1252 [alu.in_mid, alu.out_mid])
1253
1254
1255 # works... but don't use, just do "python fname.py convert -t v"
1256 #print (verilog.convert(alu, ports=[
1257 # ports=alu.in_a.ports() + \
1258 # alu.in_b.ports() + \
1259 # alu.out_z.ports())