You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

190 lines
6.6 KiB
Python

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