from amaranth import * from power_fv import pfv from power_fv.insn.const import * from power_fv.intr import * from . import InsnSpec from .utils import iea, msr_to_srr1 __all__ = ["BranchSpec"] class BranchSpec(InsnSpec, Elaboratable): def elaborate(self, platform): m = Module() m.d.comb += [ self.insn .eq(self.pfv.insn[32:]), self.pfv.stb .eq(self.insn.is_valid() & ~self.pfv.insn[:32].any()), self.pfv.msr.r_mask.sf.eq(1), ] # Raise an interrupt if BO is invalid illegal_insn = Record([("bo", 1)]) if isinstance(self.insn, ( BC , BCA , BCL , BCLA , BCLR , BCLRL , BCTAR, BCTARL, BCCTR, BCCTRL, )): bo_valid_patterns = [ "001--", "011--", "1-1--", ] # BO(2)=0 ("decrement and test CTR") is illegal for bcctr/bcctrl. if not isinstance(self.insn, (BCCTR, BCCTRL)): bo_valid_patterns += [ "0000-", "0001-", "0100-", "0101-", "1-00-", "1-01-", ] m.d.comb += illegal_insn.bo.eq(~self.insn.BO.matches(*bo_valid_patterns)) with m.If(illegal_insn.any()): if self.pfv.illegal_insn_heai: raise NotImplementedError m.d.comb += [ self.pfv.intr.eq(1), self.pfv.nia .eq(INTR_PROGRAM.vector_addr), INTR_PROGRAM.write_msr(self.pfv.msr), self.pfv.srr0.w_mask.eq(Repl(1, 64)), self.pfv.srr0.w_data.eq(iea(self.pfv.cia, self.pfv.msr.r_data.sf)), self.pfv.srr1.w_mask[63-36:64-33].eq(Repl(1, 4)), self.pfv.srr1.w_data[63-36:64-33].eq(0), self.pfv.srr1.w_mask[63-42].eq(1), self.pfv.srr1.w_data[63-42].eq(0), self.pfv.srr1.w_mask[63-46:64-43].eq(Repl(1, 4)), self.pfv.srr1.w_data[63-46:64-43].eq(0b0100), # Illegal Instruction type (deprecated) self.pfv.srr1.w_mask[63-47].eq(1), self.pfv.srr1.w_data[63-47].eq(0), msr_to_srr1(self.pfv.msr, self.pfv.srr1, 0, 32), msr_to_srr1(self.pfv.msr, self.pfv.srr1, 37, 41), msr_to_srr1(self.pfv.msr, self.pfv.srr1, 48, 63), ] with m.Else(): taken = Signal() cond_bit = Signal() cond_ok = Signal() ctr_any = Signal() ctr_ok = Signal() # Is this branch taken ? if isinstance(self.insn, (B, BA, BL, BLA)): m.d.comb += taken.eq(1) elif isinstance(self.insn, ( BC , BCA , BCL , BCLA , BCLR , BCLRL , BCTAR, BCTARL, BCCTR, BCCTRL, )): with m.If(self.insn.BO[4 - 0]): m.d.comb += cond_ok.eq(1) with m.Else(): # BO(0) = 0, test CR(BI) m.d.comb += self.pfv.cr.r_mask.bit_select(31-self.insn.BI, width=1).eq(1) m.d.comb += [ cond_bit.eq(self.pfv.cr.r_data.bit_select(31-self.insn.BI, width=1)), cond_ok .eq(cond_bit == self.insn.BO[4 - 1]), ] if isinstance(self.insn, (BCCTR, BCCTRL)): m.d.comb += taken.eq(cond_ok) else: with m.If(self.insn.BO[4 - 2]): m.d.comb += ctr_ok.eq(1) with m.Else(): # BO(2) = 0, decrement CTR then test its value. m.d.comb += [ self.pfv.ctr.r_mask.eq(Repl(1, 64)), self.pfv.ctr.w_mask.eq(Repl(1, 64)), self.pfv.ctr.w_data.eq(self.pfv.ctr.r_data - 1), ] m.d.comb += [ ctr_any.eq(iea(self.pfv.ctr.w_data, self.pfv.msr.r_data.sf).any()), ctr_ok .eq(ctr_any ^ self.insn.BO[4 - 3]), ] m.d.comb += taken.eq(cond_ok & ctr_ok) else: assert False # Compute the target address target = Signal(unsigned(64)) base = Signal(unsigned(64)) offset = Signal( signed(62)) # base : CIA if AA=0, 0 otherwise if isinstance(self.insn, (B, BL, BC, BCL)): m.d.comb += base.eq(self.pfv.cia) elif isinstance(self.insn, ( BA , BLA , BCA , BCLA , BCLR, BCLRL, BCCTR, BCCTRL, BCTAR, BCTARL, )): m.d.comb += base.eq(0) else: assert False # offset : LI or BD or LR>>2 or CTR>>2 or TAR>>2 if isinstance(self.insn, (B, BA, BL, BLA)): m.d.comb += offset.eq(self.insn.LI) elif isinstance(self.insn, (BC, BCA, BCL, BCLA)): m.d.comb += offset.eq(self.insn.BD) elif isinstance(self.insn, (BCLR, BCLRL)): m.d.comb += [ self.pfv.lr.r_mask[2:].eq(Repl(1, 62)), offset.eq(self.pfv.lr.r_data[2:]), ] elif isinstance(self.insn, (BCCTR, BCCTRL)): m.d.comb += [ self.pfv.ctr.r_mask[2:].eq(Repl(1, 62)), offset.eq(self.pfv.ctr.r_data[2:]), ] elif isinstance(self.insn, (BCTAR, BCTARL)): m.d.comb += [ self.pfv.tar.r_mask[2:].eq(Repl(1, 62)), offset.eq(self.pfv.tar.r_data[2:]), ] else: assert False # target : base + offset<<2 m.d.comb += target.eq(base + Cat(Const(0, 2), offset)) # Update NIA with m.If(taken): m.d.comb += self.pfv.nia.eq(iea(target, self.pfv.msr.r_data.sf)) with m.Else(): m.d.comb += self.pfv.nia.eq(iea(self.pfv.cia + 4, self.pfv.msr.r_data.sf)) # Write the return address to LR if LK=1 if isinstance(self.insn, ( BL , BLA , BCL , BCLA, BCLRL, BCCTRL, BCTARL, )): m.d.comb += [ self.pfv.lr.w_mask.eq(Repl(1, 64)), self.pfv.lr.w_data.eq(iea(self.pfv.cia + 4, self.pfv.msr.r_data.sf)), ] return m