convert branch pipeline to use msr/cia as immediates
[soc.git] / src / soc / fu / branch / main_stage.py
index 6efc5745b33e092dec40ac0036a36a9f68841cc5..39631afdca281c1b280765281cbae5cb000b35b3 100644 (file)
@@ -1,20 +1,44 @@
-# This stage is intended to do most of the work of executing Logical
-# instructions. This is OR, AND, XOR, POPCNT, PRTY, CMPB, BPERMD, CNTLZ
-# however input and output stages also perform bit-negation on input(s)
-# and output, as well as carry and overflow generation.
-# This module however should not gate the carry or overflow, that's up
-# to the output stage
-
-from nmigen import (Module, Signal, Cat, Repl, Mux, Const, Array)
+"""Branch Pipeline
+
+This stage is intended to do most of the work of executing branch
+instructions. This is OP_B, OP_B, OP_BCREG
+
+Note: it is PARTICULARLY important to pay attention to PowerDecode2
+more specifically DecodeRA etc. as these work closely in conjunction
+with the Branch pipeline, here.
+
+The Branch pipeline itself does not and cannot read registers: it can
+only process data and produce results.  Therefore, something else needs
+to know that BC needs CTR, and that one of the outputs from here is to
+go into LR, and so on.  Encoding of which registers are read and written
+is the responsibility of PowerDecode2 and because some of those decisions
+are conditional (based on BO2 for example) PowerDecode2 has to duplicate
+some of that bitlevel operand field decoding.
+
+It us therefore quite critical to read this code in conjunction side by
+side with power_decode2.py
+
+Links:
+* https://bugs.libre-soc.org/show_bug.cgi?id=313
+* https://bugs.libre-soc.org/show_bug.cgi?id=335
+* https://libre-soc.org/openpower/isa/branch/
+"""
+
+from nmigen import (Module, Signal, Cat, Mux, Const, Array)
 from nmutil.pipemodbase import PipeModBase
+from nmutil.extend import exts
 from soc.fu.branch.pipe_data import BranchInputData, BranchOutputData
-from soc.decoder.power_enums import InternalOp
+from soc.decoder.power_enums import MicrOp
 
 from soc.decoder.power_fields import DecodeFields
 from soc.decoder.power_fieldsn import SignalBitRange
 
+
 def br_ext(bd):
-    return Cat(Const(0, 2), bd, Repl(bd[-1], 64-(bd.shape().width + 2)))
+    """computes sign-extended NIA (assumes word-alignment)
+    """
+    return Cat(Const(0, 2), exts(bd, bd.shape().width, 64 - 2))
+
 
 """
 Notes on BO Field:
@@ -48,34 +72,40 @@ class BranchMainStage(PipeModBase):
         comb = m.d.comb
         op = self.i.ctx.op
         lk = op.lk # see PowerDecode2 as to why this is done
-        nia_o, lr_o = self.o.nia, self.o.lr
+        cr, cia, ctr, fast1 = self.i.cr, op.cia, self.i.ctr, self.i.fast1
+        fast2 = self.i.fast2
+        nia_o, lr_o, ctr_o = self.o.nia, self.o.lr, self.o.ctr
 
-        # obtain relevant instruction fields
+        # obtain relevant instruction field AA, "Absolute Address" mode
         i_fields = self.fields.FormI
-        aa = Signal(i_fields.AA[0:-1].shape())
-        comb += aa.eq(i_fields.AA[0:-1])
+        AA = i_fields.AA[0:-1]
 
         br_imm_addr = Signal(64, reset_less=True)
         br_addr = Signal(64, reset_less=True)
         br_taken = Signal(reset_less=True)
 
         # Handle absolute or relative branches
-        with m.If(aa):
+        with m.If(AA | (op.insn_type == MicrOp.OP_BCREG)):
             comb += br_addr.eq(br_imm_addr)
         with m.Else():
-            comb += br_addr.eq(br_imm_addr + self.i.cia)
+            comb += br_addr.eq(br_imm_addr + cia)
 
         # fields for conditional branches (BO and BI are same for BC and BCREG)
-        # NOTE: here, BO and BI we would like be treated as CR regfile
-        # selectors (similar to RA, RB, RS, RT).  see comment here:
-        # https://bugs.libre-soc.org/show_bug.cgi?id=313#c2
         b_fields = self.fields.FormB
         BO = b_fields.BO[0:-1]
-        BI = b_fields.BI[0:-1]
+        BI = b_fields.BI[0:-1][0:2] # CR0-7 selected already in PowerDecode2.
+
+        cr_bits = Array([cr[3-i] for i in range(4)]) # invert. Because POWER.
 
         # The bit of CR selected by BI
+        bi = Signal(2, reset_less=True)
         cr_bit = Signal(reset_less=True)
-        comb += cr_bit.eq((self.i.cr & (1<<(31-BI))) != 0)
+        comb += bi.eq(BI)                 # reduces gate-count due to pmux
+        comb += cr_bit.eq(cr_bits[bi])
+
+        # Whether ctr is written to on a conditional branch
+        ctr_write = Signal(reset_less=True)
+        comb += ctr_write.eq(0)
 
         # Whether the conditional branch should be taken
         bc_taken = Signal(reset_less=True)
@@ -83,10 +113,10 @@ class BranchMainStage(PipeModBase):
             comb += bc_taken.eq((cr_bit == BO[3]) | BO[4])
         with m.Else():
             # decrement the counter and place into output
-            ctr = Signal(64, reset_less=True)
-            comb += ctr.eq(self.i.ctr - 1)
-            comb += self.o.ctr.data.eq(ctr)
-            comb += self.o.ctr.ok.eq(1)
+            ctr_n = Signal(64, reset_less=True)
+            comb += ctr_n.eq(ctr - 1)
+            comb += ctr_o.data.eq(ctr_n)
+            comb += ctr_write.eq(1)
             # take either all 64 bits or only 32 of post-incremented counter
             ctr_m = Signal(64, reset_less=True)
             with m.If(op.is_32bit):
@@ -106,34 +136,38 @@ class BranchMainStage(PipeModBase):
         ### Main Switch Statement ###
         with m.Switch(op.insn_type):
             #### branch ####
-            with m.Case(InternalOp.OP_B):
+            with m.Case(MicrOp.OP_B):
                 LI = i_fields.LI[0:-1]
                 comb += br_imm_addr.eq(br_ext(LI))
                 comb += br_taken.eq(1)
             #### branch conditional ####
-            with m.Case(InternalOp.OP_BC):
+            with m.Case(MicrOp.OP_BC):
                 BD = b_fields.BD[0:-1]
                 comb += br_imm_addr.eq(br_ext(BD))
                 comb += br_taken.eq(bc_taken)
+                comb += ctr_o.ok.eq(ctr_write)
             #### branch conditional reg ####
-            with m.Case(InternalOp.OP_BCREG):
-                comb += br_imm_addr.eq(self.i.spr1) # SPR1 is set by decode unit
+            with m.Case(MicrOp.OP_BCREG):
+                xo = self.fields.FormXL.XO[0:-1]
+                with m.If(xo[9] & ~xo[5]):
+                    comb += br_imm_addr.eq(Cat(Const(0, 2), fast1[2:]))
+                with m.Else():
+                    comb += br_imm_addr.eq(Cat(Const(0, 2), fast2[2:]))
                 comb += br_taken.eq(bc_taken)
+                comb += ctr_o.ok.eq(ctr_write)
 
-        ###### output next instruction address #####
-
+        # output next instruction address
         comb += nia_o.data.eq(br_addr)
         comb += nia_o.ok.eq(br_taken)
 
-        ###### link register - only activate on operations marked as "lk" #####
-
+        # link register - only activate on operations marked as "lk"
         with m.If(lk):
             # ctx.op.lk is the AND of the insn LK field *and* whether the
             # op is to "listen" to the link field
-            comb += lr_o.data.eq(self.i.cia + 4)
+            comb += lr_o.data.eq(cia + 4)
             comb += lr_o.ok.eq(1)
 
-        ###### and context #####
+        # and context
         comb += self.o.ctx.eq(self.i.ctx)
 
         return m