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.

168 lines
5.1 KiB
Python

from amaranth import *
from power_fv import pfv
from power_fv.insn.const import *
from . import InsnSpec
from .utils import iea
__all__ = ["BranchSpec"]
class BranchSpec(InsnSpec, Elaboratable):
def __init__(self, insn):
self.pfv = pfv.Interface()
self.insn = insn
def elaborate(self, platform):
m = Module()
m.d.comb += [
self.pfv.stb .eq(1),
self.pfv.insn.eq(Cat(Const(0, 32), self.insn.as_value())),
self.pfv.msr.r_mask.sf.eq(1),
]
# Raise an interrupt if the BO field is invalid.
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 += self.pfv.intr.eq(~self.insn.BO.matches(*bo_valid_patterns))
else:
m.d.comb += self.pfv.intr.eq(0)
# Is this branch taken ?
taken = Signal()
cond_bit = Signal()
cond_ok = Signal()
ctr_any = Signal()
ctr_ok = Signal()
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,
)):
# If BO(0) = 0, test CR(BI)
with m.If(self.insn.BO[4 - 0]):
m.d.comb += cond_ok.eq(1)
with m.Else():
m.d.comb += [
self.pfv.cr.r_mask[::-1].bit_select(self.insn.BI, width=1).eq(1),
cond_bit.eq(self.pfv.cr.r_data[::-1].bit_select(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:
# If BO(2) = 0, decrement CTR then test its value.
with m.If(self.insn.BO[4 - 2]):
m.d.comb += ctr_ok.eq(1)
with m.Else():
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),
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