checks: add checks for branch instructions.
							parent
							
								
									5c9bc3e68c
								
							
						
					
					
						commit
						58bef1a741
					
				@ -0,0 +1,210 @@
 | 
			
		||||
from amaranth import *
 | 
			
		||||
from amaranth.asserts import *
 | 
			
		||||
 | 
			
		||||
from .. import pfv
 | 
			
		||||
from ..insn import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(Elaboratable):
 | 
			
		||||
    _insn_cls = None
 | 
			
		||||
 | 
			
		||||
    def __init_subclass__(cls, *, insn_cls):
 | 
			
		||||
        cls._insn_cls = insn_cls
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.pfv  = pfv.Interface()
 | 
			
		||||
        self.trig = Record([
 | 
			
		||||
            ("pre",  1),
 | 
			
		||||
            ("post", 1),
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
    def elaborate(self, platform):
 | 
			
		||||
        m = Module()
 | 
			
		||||
 | 
			
		||||
        # TODO:
 | 
			
		||||
        # - support MSR (stop assuming 64-bit mode)
 | 
			
		||||
        # - support interrupts (detect illegal forms)
 | 
			
		||||
 | 
			
		||||
        spec_insn = self._insn_cls()
 | 
			
		||||
 | 
			
		||||
        with m.If(self.trig.post):
 | 
			
		||||
            m.d.sync += [
 | 
			
		||||
                Assume(self.pfv.stb),
 | 
			
		||||
                Assume(self.pfv.insn[32:] == spec_insn),
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        if isinstance(spec_insn, (Instruction_B, Instruction_XL_b)):
 | 
			
		||||
            bo_valid_patterns = [
 | 
			
		||||
                "0000-",
 | 
			
		||||
                "0001-",
 | 
			
		||||
                "001--",
 | 
			
		||||
                "0100-",
 | 
			
		||||
                "0101-",
 | 
			
		||||
                "011--",
 | 
			
		||||
                "1-00-",
 | 
			
		||||
                "1-01-"
 | 
			
		||||
            ]
 | 
			
		||||
            if not isinstance(spec_insn, Instruction_B):
 | 
			
		||||
                # "Branch always" mnemonics are undefined for B-form conditional branches.
 | 
			
		||||
                # (Appendix C.2.2, Book I)
 | 
			
		||||
                bo_valid_patterns.append("1-1--")
 | 
			
		||||
 | 
			
		||||
            bo_valid = Signal()
 | 
			
		||||
            m.d.comb += bo_valid.eq(spec_insn.bo.matches(*bo_valid_patterns))
 | 
			
		||||
 | 
			
		||||
            # FIXME(microwatt,interrupts): turn this into an assert
 | 
			
		||||
            with m.If(self.trig.post):
 | 
			
		||||
                m.d.sync += Assume(bo_valid | self.pfv.intr)
 | 
			
		||||
 | 
			
		||||
        # NIA
 | 
			
		||||
 | 
			
		||||
        spec_nia = Signal(unsigned(64))
 | 
			
		||||
        taken    = Signal()
 | 
			
		||||
        offset   = Signal(signed(62))
 | 
			
		||||
 | 
			
		||||
        if isinstance(spec_insn, Instruction_I):
 | 
			
		||||
            m.d.comb += [
 | 
			
		||||
                taken .eq(1),
 | 
			
		||||
                offset.eq(spec_insn.li)
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        elif isinstance(spec_insn, (Instruction_B, Instruction_XL_b)):
 | 
			
		||||
            cond_bit = Signal()
 | 
			
		||||
            ctr_any  = Signal()
 | 
			
		||||
            cond_ok  = Signal()
 | 
			
		||||
            ctr_ok   = Signal()
 | 
			
		||||
 | 
			
		||||
            m.d.comb += [
 | 
			
		||||
                cond_bit.eq(self.pfv.cr.r_data[::-1].bit_select(spec_insn.bi, width=1)),
 | 
			
		||||
                ctr_any .eq(self.pfv.ctr.w_data.any()),
 | 
			
		||||
                cond_ok .eq(spec_insn.bo[4] | (spec_insn.bo[3] == cond_bit)),
 | 
			
		||||
                ctr_ok  .eq(spec_insn.bo[2] | (ctr_any ^ spec_insn.bo[1])),
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
            if isinstance(spec_insn, Instruction_XL_b) and spec_insn.xo.value == 528: # bcctr/bcctrl
 | 
			
		||||
                m.d.comb += taken.eq(cond_ok)
 | 
			
		||||
            else:
 | 
			
		||||
                m.d.comb += taken.eq(cond_ok & ctr_ok)
 | 
			
		||||
 | 
			
		||||
            if isinstance(spec_insn, Instruction_B):
 | 
			
		||||
                m.d.comb += offset.eq(spec_insn.bd)
 | 
			
		||||
            elif spec_insn.xo.value ==  16: # bclr/bclrl
 | 
			
		||||
                m.d.comb += offset.eq(self.pfv.lr .r_data[:61])
 | 
			
		||||
            elif spec_insn.xo.value == 528: # bcctr/bcctrl
 | 
			
		||||
                m.d.comb += offset.eq(self.pfv.ctr.r_data[:61])
 | 
			
		||||
            elif spec_insn.xo.value == 560: # bctar/bctarl
 | 
			
		||||
                m.d.comb += offset.eq(self.pfv.tar.r_data[:61])
 | 
			
		||||
            else:
 | 
			
		||||
                assert False
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            assert False
 | 
			
		||||
 | 
			
		||||
        with m.If(taken):
 | 
			
		||||
            if isinstance(spec_insn, (Instruction_I, Instruction_B)) and spec_insn.aa.value == 0:
 | 
			
		||||
                m.d.comb += spec_nia.eq(self.pfv.cia + (offset << 2))
 | 
			
		||||
            else:
 | 
			
		||||
                m.d.comb += spec_nia.eq(offset << 2)
 | 
			
		||||
        with m.Else():
 | 
			
		||||
            m.d.comb += spec_nia.eq(self.pfv.cia + 4)
 | 
			
		||||
 | 
			
		||||
        with m.If(self.trig.post & ~self.pfv.intr):
 | 
			
		||||
            m.d.sync += Assert(self.pfv.nia == spec_nia)
 | 
			
		||||
 | 
			
		||||
        # CR
 | 
			
		||||
 | 
			
		||||
        spec_cr_r_stb = Signal(8)
 | 
			
		||||
 | 
			
		||||
        if isinstance(spec_insn, Instruction_I):
 | 
			
		||||
            m.d.comb += spec_cr_r_stb.eq(0)
 | 
			
		||||
        elif isinstance(spec_insn, (Instruction_B, Instruction_XL_b)):
 | 
			
		||||
            m.d.comb += spec_cr_r_stb[::-1].bit_select(spec_insn.bi[2:], width=1).eq(1)
 | 
			
		||||
        else:
 | 
			
		||||
            assert False
 | 
			
		||||
 | 
			
		||||
        with m.If(self.trig.post & ~self.pfv.intr):
 | 
			
		||||
            for i, spec_cr_r_stb_bit in enumerate(spec_cr_r_stb):
 | 
			
		||||
                pfv_cr_r_stb_bit = self.pfv.cr.r_stb[i]
 | 
			
		||||
                m.d.sync += Assert(spec_cr_r_stb_bit.implies(pfv_cr_r_stb_bit))
 | 
			
		||||
 | 
			
		||||
        # LR
 | 
			
		||||
 | 
			
		||||
        spec_lr_r_stb  = Signal()
 | 
			
		||||
        spec_lr_w_stb  = Signal()
 | 
			
		||||
        spec_lr_w_data = Signal(64)
 | 
			
		||||
 | 
			
		||||
        if isinstance(spec_insn, (Instruction_I, Instruction_B)):
 | 
			
		||||
            m.d.comb += spec_lr_r_stb.eq(0)
 | 
			
		||||
        elif isinstance(spec_insn, (Instruction_XL_b)):
 | 
			
		||||
            m.d.comb += spec_lr_r_stb.eq(spec_insn.xo == 16) # bclr/bclrl
 | 
			
		||||
        else:
 | 
			
		||||
            assert False
 | 
			
		||||
 | 
			
		||||
        m.d.comb += [
 | 
			
		||||
            spec_lr_w_stb .eq(spec_insn.lk),
 | 
			
		||||
            spec_lr_w_data.eq(self.pfv.cia + 4),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        with m.If(self.trig.post & ~self.pfv.intr):
 | 
			
		||||
            m.d.sync += [
 | 
			
		||||
                Assert(self.pfv.lr.r_stb == spec_lr_r_stb),
 | 
			
		||||
                Assert(self.pfv.lr.w_stb == spec_lr_w_stb),
 | 
			
		||||
                Assert(self.pfv.lr.w_stb.implies(self.pfv.lr.w_data == spec_lr_w_data)),
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        # CTR
 | 
			
		||||
 | 
			
		||||
        spec_ctr_r_stb  = Signal()
 | 
			
		||||
        spec_ctr_w_stb  = Signal()
 | 
			
		||||
        spec_ctr_w_data = Signal(64)
 | 
			
		||||
 | 
			
		||||
        if isinstance(spec_insn, Instruction_I):
 | 
			
		||||
            m.d.comb += spec_ctr_r_stb.eq(0)
 | 
			
		||||
        elif isinstance(spec_insn, Instruction_B):
 | 
			
		||||
            m.d.comb += spec_ctr_r_stb.eq(~spec_insn.bo[2])
 | 
			
		||||
        elif isinstance(spec_insn, (Instruction_B, Instruction_XL_b)):
 | 
			
		||||
            m.d.comb += spec_ctr_r_stb.eq(1)
 | 
			
		||||
        else:
 | 
			
		||||
            assert False
 | 
			
		||||
 | 
			
		||||
        if isinstance(spec_insn, Instruction_I):
 | 
			
		||||
            m.d.comb += spec_ctr_w_stb.eq(0)
 | 
			
		||||
        elif isinstance(spec_insn, Instruction_B):
 | 
			
		||||
            m.d.comb += spec_ctr_w_stb.eq(~spec_insn.bo[2])
 | 
			
		||||
        elif isinstance(spec_insn, Instruction_XL_b):
 | 
			
		||||
            if spec_insn.xo.value == 528: # bcctr/bcctrl
 | 
			
		||||
                m.d.comb += spec_ctr_w_stb.eq(0)
 | 
			
		||||
            else:
 | 
			
		||||
                m.d.comb += spec_ctr_w_stb.eq(~spec_insn.bo[2])
 | 
			
		||||
        else:
 | 
			
		||||
            assert False
 | 
			
		||||
 | 
			
		||||
        m.d.comb += spec_ctr_w_data.eq(self.pfv.ctr.r_data - 1)
 | 
			
		||||
 | 
			
		||||
        with m.If(self.trig.post & ~self.pfv.intr):
 | 
			
		||||
            m.d.sync += [
 | 
			
		||||
                Assert(self.pfv.ctr.r_stb == spec_ctr_r_stb),
 | 
			
		||||
                Assert(self.pfv.ctr.w_stb == spec_ctr_w_stb),
 | 
			
		||||
                Assert(self.pfv.ctr.w_stb.implies(self.pfv.ctr.w_data == spec_ctr_w_data)),
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        # TAR
 | 
			
		||||
 | 
			
		||||
        spec_tar_r_stb = Signal()
 | 
			
		||||
 | 
			
		||||
        if isinstance(spec_insn, (Instruction_I, Instruction_B)):
 | 
			
		||||
            m.d.comb += spec_tar_r_stb.eq(0)
 | 
			
		||||
        elif isinstance(spec_insn, (Instruction_XL_b)):
 | 
			
		||||
            m.d.comb += spec_tar_r_stb.eq(spec_insn.xo == 560) # bctar/bctarl
 | 
			
		||||
        else:
 | 
			
		||||
            assert False
 | 
			
		||||
 | 
			
		||||
        with m.If(self.trig.post & ~self.pfv.intr):
 | 
			
		||||
            m.d.sync += [
 | 
			
		||||
                Assert(self.pfv.tar.r_stb == spec_tar_r_stb),
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
        return m
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.B):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BA):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BC):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BCA):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BCCTR):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BCTRL):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BCL):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,10 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BCLA):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BCLR):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BCLRL):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BCTAR):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BCTARL):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BL):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
from . import _branch
 | 
			
		||||
from .. import insn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Check"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Check(_branch.Check, insn_cls=insn.BLA):
 | 
			
		||||
    pass
 | 
			
		||||
@ -0,0 +1,88 @@
 | 
			
		||||
from amaranth import *
 | 
			
		||||
from amaranth.asserts import AnyConst
 | 
			
		||||
from amaranth.hdl.ast import ValueCastable
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Instruction_I(ValueCastable):
 | 
			
		||||
    po = None
 | 
			
		||||
    li = None
 | 
			
		||||
    aa = None
 | 
			
		||||
    lk = None
 | 
			
		||||
 | 
			
		||||
    def __init_subclass__(cls, *, po, aa, lk):
 | 
			
		||||
        cls.po = Const(po, unsigned(6))
 | 
			
		||||
        cls.aa = Const(aa, 1)
 | 
			
		||||
        cls.lk = Const(lk, 1)
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.li = AnyConst(signed(24))
 | 
			
		||||
 | 
			
		||||
    @ValueCastable.lowermethod
 | 
			
		||||
    def as_value(self):
 | 
			
		||||
        return Cat(self.lk, self.aa, self.li, self.po)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Instruction_B(ValueCastable):
 | 
			
		||||
    po = None
 | 
			
		||||
    bo = None
 | 
			
		||||
    bi = None
 | 
			
		||||
    bd = None
 | 
			
		||||
    aa = None
 | 
			
		||||
    lk = None
 | 
			
		||||
 | 
			
		||||
    def __init_subclass__(cls, *, po, aa, lk):
 | 
			
		||||
        cls.po = Const(po, unsigned(6))
 | 
			
		||||
        cls.aa = Const(aa, 1)
 | 
			
		||||
        cls.lk = Const(lk, 1)
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.bo = AnyConst(unsigned( 5))
 | 
			
		||||
        self.bi = AnyConst(unsigned( 5))
 | 
			
		||||
        self.bd = AnyConst(  signed(14))
 | 
			
		||||
 | 
			
		||||
    @ValueCastable.lowermethod
 | 
			
		||||
    def as_value(self):
 | 
			
		||||
        return Cat(self.lk, self.aa, self.bd, self.bi, self.bo, self.po)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Instruction_XL_b(ValueCastable):
 | 
			
		||||
    po = None
 | 
			
		||||
    bo = None
 | 
			
		||||
    bi = None
 | 
			
		||||
    _0 = None
 | 
			
		||||
    bh = None
 | 
			
		||||
    xo = None
 | 
			
		||||
    lk = None
 | 
			
		||||
 | 
			
		||||
    def __init_subclass__(cls, *, po, xo, lk):
 | 
			
		||||
        cls.po = Const(po, unsigned(6))
 | 
			
		||||
        cls.xo = Const(xo, unsigned(9))
 | 
			
		||||
        cls.lk = Const(1)
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.bo = AnyConst(unsigned(5))
 | 
			
		||||
        self.bi = AnyConst(unsigned(5))
 | 
			
		||||
        self._0 = AnyConst(unsigned(3))
 | 
			
		||||
        self.bh = AnyConst(unsigned(2))
 | 
			
		||||
 | 
			
		||||
    @ValueCastable.lowermethod
 | 
			
		||||
    def as_value(self):
 | 
			
		||||
        return Cat(self.lk, self.xo, self.bh, self._0, self.bi, self.bo, self.po)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class B      (Instruction_I, po=18, aa=0, lk=0): pass
 | 
			
		||||
class BA     (Instruction_I, po=18, aa=1, lk=0): pass
 | 
			
		||||
class BL     (Instruction_I, po=18, aa=0, lk=1): pass
 | 
			
		||||
class BLA    (Instruction_I, po=18, aa=1, lk=1): pass
 | 
			
		||||
 | 
			
		||||
class BC     (Instruction_B, po=16, aa=0, lk=0): pass
 | 
			
		||||
class BCA    (Instruction_B, po=16, aa=1, lk=0): pass
 | 
			
		||||
class BCL    (Instruction_B, po=16, aa=0, lk=1): pass
 | 
			
		||||
class BCLA   (Instruction_B, po=16, aa=1, lk=1): pass
 | 
			
		||||
 | 
			
		||||
class BCLR   (Instruction_XL_b, po=19, xo= 16, lk=0): pass
 | 
			
		||||
class BCLRL  (Instruction_XL_b, po=19, xo= 16, lk=1): pass
 | 
			
		||||
class BCCTR  (Instruction_XL_b, po=19, xo=528, lk=0): pass
 | 
			
		||||
class BCCTRL (Instruction_XL_b, po=19, xo=528, lk=1): pass
 | 
			
		||||
class BCTAR  (Instruction_XL_b, po=19, xo=560, lk=0): pass
 | 
			
		||||
class BCTARL (Instruction_XL_b, po=19, xo=560, lk=1): pass
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue