Handle carry out in alu
[soc.git] / src / soc / fu / alu / output_stage.py
1 # This stage is intended to handle the gating of carry and overflow
2 # out, summary overflow generation, and updating the condition
3 # register
4 from nmigen import (Module, Signal, Cat, Repl)
5 from nmutil.pipemodbase import PipeModBase
6 from soc.fu.alu.pipe_data import ALUInputData, ALUOutputData
7 from ieee754.part.partsig import PartitionedSignal
8 from soc.decoder.power_enums import InternalOp
9
10
11 class ALUOutputStage(PipeModBase):
12 def __init__(self, pspec):
13 super().__init__(pspec, "output")
14
15 def ispec(self):
16 return ALUOutputData(self.pspec) # TODO: ALUIntermediateData
17
18 def ospec(self):
19 return ALUOutputData(self.pspec)
20
21 def elaborate(self, platform):
22 m = Module()
23 comb = m.d.comb
24 op = self.i.ctx.op
25
26 # op requests inversion of the output
27 o = Signal.like(self.i.o)
28 with m.If(op.invert_out):
29 comb += o.eq(~self.i.o)
30 with m.Else():
31 comb += o.eq(self.i.o)
32
33 # target register if 32-bit is only the 32 LSBs
34 target = Signal(64, reset_less=True)
35 with m.If(op.is_32bit):
36 comb += target.eq(o[:32])
37 with m.Else():
38 comb += target.eq(o)
39
40 # Handle carry_out
41 with m.If(self.i.ctx.op.output_carry):
42 comb += self.o.carry_out.eq(self.i.carry_out)
43
44 # create condition register cr0 and sticky-overflow
45 is_zero = Signal(reset_less=True)
46 is_positive = Signal(reset_less=True)
47 is_negative = Signal(reset_less=True)
48 msb_test = Signal(reset_less=True) # set equal to MSB, invert if OP=CMP
49 is_cmp = Signal(reset_less=True) # true if OP=CMP
50 so = Signal(reset_less=True)
51
52 # TODO: if o[63] is XORed with "operand == OP_CMP"
53 # that can be used as a test
54 # see https://bugs.libre-soc.org/show_bug.cgi?id=305#c60
55
56 comb += is_cmp.eq(op.insn_type == InternalOp.OP_CMP)
57 comb += msb_test.eq(target[-1] ^ is_cmp)
58 comb += is_zero.eq(target == 0)
59 comb += is_positive.eq(~is_zero & ~msb_test)
60 comb += is_negative.eq(~is_zero & msb_test)
61 comb += so.eq(self.i.so | self.i.ov)
62
63 with m.If(op.insn_type != InternalOp.OP_CMPEQB):
64 comb += self.o.cr0.eq(Cat(so, is_zero, is_positive, is_negative))
65 with m.Else():
66 comb += self.o.cr0.eq(self.i.cr0)
67
68 # copy [inverted] output, sticky-overflow and context out
69 comb += self.o.o.eq(o)
70 comb += self.o.so.eq(so)
71 comb += self.o.ctx.eq(self.i.ctx)
72
73 return m