Merge branch 'master' of git.libre-soc.org:soc
[soc.git] / src / soc / fu / branch / main_stage.py
1 # License: LGPLv3
2 # Copyright (C) 2020 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3 # Copyright (C) 2020 Michael Nolan <mtnolan2640@gmail.com>
4
5 """Branch Pipeline
6
7 This stage is intended to do most of the work of executing branch
8 instructions. This is OP_B, OP_B, OP_BCREG
9
10 Note: it is PARTICULARLY important to pay attention to PowerDecode2
11 more specifically DecodeRA etc. as these work closely in conjunction
12 with the Branch pipeline, here.
13
14 The Branch pipeline itself does not and cannot read registers: it can
15 only process data and produce results. Therefore, something else needs
16 to know that BC needs CTR, and that one of the outputs from here is to
17 go into LR, and so on. Encoding of which registers are read and written
18 is the responsibility of PowerDecode2 and because some of those decisions
19 are conditional (based on BO2 for example) PowerDecode2 has to duplicate
20 some of that bitlevel operand field decoding.
21
22 It us therefore quite critical to read this code in conjunction side by
23 side with power_decode2.py
24
25 Links:
26 * https://bugs.libre-soc.org/show_bug.cgi?id=313
27 * https://bugs.libre-soc.org/show_bug.cgi?id=335
28 * https://libre-soc.org/openpower/isa/branch/
29 """
30
31 from nmigen import (Module, Signal, Cat, Mux, Const, Array)
32 from nmutil.pipemodbase import PipeModBase
33 from nmutil.extend import exts
34 from soc.fu.branch.pipe_data import BranchInputData, BranchOutputData
35 from soc.decoder.power_enums import MicrOp
36
37 from soc.decoder.power_fields import DecodeFields
38 from soc.decoder.power_fieldsn import SignalBitRange
39
40
41 def br_ext(bd):
42 """computes sign-extended NIA (assumes word-alignment)
43 """
44 return Cat(Const(0, 2), exts(bd, bd.shape().width, 64 - 2))
45
46
47 """
48 Notes on BO Field:
49
50 BO Description
51 0000z Decrement the CTR, then branch if decremented CTR[M:63]!=0 and CR[BI]=0
52 0001z Decrement the CTR, then branch if decremented CTR[M:63]=0 and CR[BI]=0
53 001at Branch if CR[BI]=0
54 0100z Decrement the CTR, then branch if decremented CTR[M:63]!=0 and CR[BI]=1
55 0101z Decrement the CTR, then branch if decremented CTR[M:63]=0 and CR[BI]=1
56 011at Branch if CR[BI]=1
57 1a00t Decrement the CTR, then branch if decremented CTR[M:63]!=0
58 1a01t Decrement the CTR, then branch if decremented CTR[M:63]=0
59 1z1zz Branch always
60 """
61
62 class BranchMainStage(PipeModBase):
63 def __init__(self, pspec):
64 super().__init__(pspec, "main")
65 self.fields = DecodeFields(SignalBitRange, [self.i.ctx.op.insn])
66 self.fields.create_specs()
67
68 def ispec(self):
69 return BranchInputData(self.pspec)
70
71 def ospec(self):
72 return BranchOutputData(self.pspec) # TODO: ALUIntermediateData
73
74 def elaborate(self, platform):
75 m = Module()
76 comb = m.d.comb
77 op = self.i.ctx.op
78 lk = op.lk # see PowerDecode2 as to why this is done
79 cr, cia, ctr, fast1 = self.i.cr, op.cia, self.i.ctr, self.i.fast1
80 fast2 = self.i.fast2
81 nia_o, lr_o, ctr_o = self.o.nia, self.o.lr, self.o.ctr
82
83 # obtain relevant instruction field AA, "Absolute Address" mode
84 i_fields = self.fields.FormI
85 AA = i_fields.AA[0:-1]
86
87 br_imm_addr = Signal(64, reset_less=True)
88 br_addr = Signal(64, reset_less=True)
89 br_taken = Signal(reset_less=True)
90
91 # Handle absolute or relative branches
92 with m.If(AA | (op.insn_type == MicrOp.OP_BCREG)):
93 comb += br_addr.eq(br_imm_addr)
94 with m.Else():
95 comb += br_addr.eq(br_imm_addr + cia)
96
97 # fields for conditional branches (BO and BI are same for BC and BCREG)
98 b_fields = self.fields.FormB
99 BO = b_fields.BO[0:-1]
100 BI = b_fields.BI[0:-1][0:2] # CR0-7 selected already in PowerDecode2.
101
102 cr_bits = Array([cr[3-i] for i in range(4)]) # invert. Because POWER.
103
104 # The bit of CR selected by BI
105 bi = Signal(2, reset_less=True)
106 cr_bit = Signal(reset_less=True)
107 comb += bi.eq(BI) # reduces gate-count due to pmux
108 comb += cr_bit.eq(cr_bits[bi])
109
110 # Whether ctr is written to on a conditional branch
111 ctr_write = Signal(reset_less=True)
112 comb += ctr_write.eq(0)
113
114 # Whether the conditional branch should be taken
115 bc_taken = Signal(reset_less=True)
116 with m.If(BO[2]):
117 comb += bc_taken.eq((cr_bit == BO[3]) | BO[4])
118 with m.Else():
119 # decrement the counter and place into output
120 ctr_n = Signal(64, reset_less=True)
121 comb += ctr_n.eq(ctr - 1)
122 comb += ctr_o.data.eq(ctr_n)
123 comb += ctr_write.eq(1)
124 # take either all 64 bits or only 32 of post-incremented counter
125 ctr_m = Signal(64, reset_less=True)
126 with m.If(op.is_32bit):
127 comb += ctr_m.eq(ctr[:32])
128 with m.Else():
129 comb += ctr_m.eq(ctr)
130 # check CTR zero/non-zero against BO[1]
131 ctr_zero_bo1 = Signal(reset_less=True) # BO[1] == (ctr==0)
132 comb += ctr_zero_bo1.eq(BO[1] ^ ctr_m.any())
133 with m.If(BO[3:5] == 0b00):
134 comb += bc_taken.eq(ctr_zero_bo1 & ~cr_bit)
135 with m.Elif(BO[3:5] == 0b01):
136 comb += bc_taken.eq(ctr_zero_bo1 & cr_bit)
137 with m.Elif(BO[4] == 1):
138 comb += bc_taken.eq(ctr_zero_bo1)
139
140 ### Main Switch Statement ###
141 with m.Switch(op.insn_type):
142 #### branch ####
143 with m.Case(MicrOp.OP_B):
144 LI = i_fields.LI[0:-1]
145 comb += br_imm_addr.eq(br_ext(LI))
146 comb += br_taken.eq(1)
147 #### branch conditional ####
148 with m.Case(MicrOp.OP_BC):
149 BD = b_fields.BD[0:-1]
150 comb += br_imm_addr.eq(br_ext(BD))
151 comb += br_taken.eq(bc_taken)
152 comb += ctr_o.ok.eq(ctr_write)
153 #### branch conditional reg ####
154 with m.Case(MicrOp.OP_BCREG):
155 xo = self.fields.FormXL.XO[0:-1]
156 with m.If(xo[9] & ~xo[5]):
157 comb += br_imm_addr.eq(Cat(Const(0, 2), fast1[2:]))
158 with m.Else():
159 comb += br_imm_addr.eq(Cat(Const(0, 2), fast2[2:]))
160 comb += br_taken.eq(bc_taken)
161 comb += ctr_o.ok.eq(ctr_write)
162
163 # output next instruction address
164 comb += nia_o.data.eq(br_addr)
165 comb += nia_o.ok.eq(br_taken)
166
167 # link register - only activate on operations marked as "lk"
168 with m.If(lk):
169 # ctx.op.lk is the AND of the insn LK field *and* whether the
170 # op is to "listen" to the link field
171 comb += lr_o.data.eq(cia + 4)
172 comb += lr_o.ok.eq(1)
173
174 # and context
175 comb += self.o.ctx.eq(self.i.ctx)
176
177 return m