Refactor to facilitate integration with CLIs and config files.

* Checks are now split in two modules: checks.cons for consistency
  checks, checks.insn for instructions.

* Checks are derived from PowerFVCheck and have a shorthand (e.g.
  "insn_b"). PowerFVCheck holds a mapping between its subclasses and
  their shorthands.

* Instruction checks definitions have been simplified to one-liners,
  and grouped into a single file.

* A Trigger class has been added to define testbench triggers.
main
Jean-François Nguyen 2 years ago
parent 5c097b9474
commit 9ea58a47a9

@ -1,11 +1,11 @@
import argparse import argparse
import importlib
import os import os
import multiprocessing import multiprocessing


from amaranth import * from amaranth import *


from power_fv.tb import Testbench from power_fv.checks import *
from power_fv.checks.all import *
from power_fv.build import SymbiYosysPlatform from power_fv.build import SymbiYosysPlatform


from _wrapper import MicrowattWrapper from _wrapper import MicrowattWrapper
@ -58,22 +58,23 @@ def microwatt_files():
yield top_filename, top_contents yield top_filename, top_contents




def run_check(module_name, pre, post): def run_check(*args):
module = importlib.import_module(f".{module_name}", package="power_fv.checks") check_name, tb_args = args
check = module.Check()
cpu = MicrowattWrapper() check = PowerFVCheck.registry[check_name]()
top = Testbench(check, cpu, t_pre=pre, t_post=post) dut = MicrowattWrapper()
tb = check.get_testbench(dut, **tb_args)


platform = SymbiYosysPlatform() platform = SymbiYosysPlatform()
for filename, contents in microwatt_files(): for filename, contents in microwatt_files():
platform.add_file(filename, contents) platform.add_file(filename, contents)


top_name = "{}_tb".format(module_name) tb_name = "{}_tb".format(check_name)
build_dir = "build/{}".format(top_name) build_dir = "build/{}".format(tb_name)


platform.build( platform.build(
top = top, top = tb,
name = top_name, name = tb_name,
build_dir = build_dir, build_dir = build_dir,
mode = "bmc", mode = "bmc",
ghdl_opts = "--std=08", ghdl_opts = "--std=08",
@ -92,28 +93,26 @@ if __name__ == "__main__":
args = parser.parse_args() args = parser.parse_args()


checks = [ checks = [
# name pre post ("cons_unique", {"post": 15, "pre": 12}),
("unique", 12, 15), ("cons_ia_fwd", {"post": 15}),
("ia_fwd", None, 15), ("cons_gpr", {"post": 15}),
("gpr", None, 15), ("cons_cr", {"post": 15}),
("cr", None, 15), ("cons_spr", {"post": 15}),
("spr", None, 15),

("insn_b", {"post": 15}),
("insn_b", None, 15), ("insn_ba", {"post": 15}),
("insn_ba", None, 15), ("insn_bl", {"post": 15}),
("insn_bl", None, 15), ("insn_bla", {"post": 15}),
("insn_bla", None, 15), ("insn_bc", {"post": 15}),
("insn_bc", None, 15), ("insn_bca", {"post": 15}),
("insn_bca", None, 15), ("insn_bcl", {"post": 15}),
("insn_bcl", None, 15), ("insn_bcla", {"post": 15}),
("insn_bcla", None, 15), ("insn_bclr", {"post": 15}),

("insn_bclrl", {"post": 15}),
("insn_bclr", None, 15), ("insn_bcctr", {"post": 15}),
("insn_bclrl", None, 15), ("insn_bcctrl", {"post": 15}),
("insn_bcctr", None, 15), ("insn_bctar", {"post": 15}),
("insn_bcctrl", None, 15), ("insn_bctarl", {"post": 15}),
("insn_bctar", None, 15),
("insn_bctarl", None, 15),
] ]


with multiprocessing.Pool(processes=args.jobs) as pool: with multiprocessing.Pool(processes=args.jobs) as pool:

@ -116,7 +116,7 @@ class SymbiYosysPlatform(TemplatedPlatform):
if not isinstance(top, tb.Testbench): if not isinstance(top, tb.Testbench):
raise TypeError("Top-level must be an instance of power_fv.tb.Testbench") raise TypeError("Top-level must be an instance of power_fv.tb.Testbench")


skip = str(top.t_post) depth = str(top.bmc_depth)
depth = str(top.t_post + 1) skip = str(top.bmc_depth - 1)


return super().build(top, mode=mode, depth=depth, skip=skip, **kwargs) return super().build(top, mode=mode, depth=depth, skip=skip, **kwargs)

@ -1,5 +1,12 @@
from .cr import * class PowerFVCheck:
from .gpr import * registry = {}
from .ia_fwd import * name = None
from .spr import *
from .unique import * def __init_subclass__(cls, name):
if name in cls.registry:
raise ValueError("Check name {!r} is already registered".format(name))
cls.registry[name] = cls
cls.name = name

def get_testbench(self, dut, *args, **kwargs):
raise NotImplementedError

@ -0,0 +1,2 @@
from .cons.all import *
from .insn.all import *

@ -0,0 +1,5 @@
from .unique import *
from .ia_fwd import *
from .gpr import *
from .cr import *
from .spr import *

@ -1,23 +1,24 @@
from amaranth import * from amaranth import *
from amaranth.asserts import * from amaranth.asserts import *


from .. import pfv from .. import PowerFVCheck
from ... import pfv, tb




__all__ = ["Check"] __all__ = ["CRSpec", "CRCheck"]




class Check(Elaboratable): class CRSpec(Elaboratable):
"""Condition Register check. """Condition Register consistency specification.


Checks that reads from CR fields are consistent with the last value that was written to them. Checks that reads from CR fields return the last value that was written to them.
""" """
def __init__(self): def __init__(self, post):
self.pfv = pfv.Interface() self.pfv = pfv.Interface()
self.trig = Record([ self.post = tb.Trigger(cycle=post)
("pre", 1),
("post", 1), def triggers(self):
]) yield self.post


def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()
@ -56,7 +57,7 @@ class Check(Elaboratable):
cr_field.shadow .eq(cr_field.pfv.w_data), cr_field.shadow .eq(cr_field.pfv.w_data),
] ]


with m.If(self.trig.post): with m.If(self.post.stb):
m.d.sync += [ m.d.sync += [
Assume(Past(self.pfv.stb)), Assume(Past(self.pfv.stb)),
Assume(Past(self.pfv.order) == spec_order), Assume(Past(self.pfv.order) == spec_order),
@ -68,3 +69,10 @@ class Check(Elaboratable):
m.d.sync += Assert(Past(cr_field.pfv.r_data) == Past(cr_field.shadow)) m.d.sync += Assert(Past(cr_field.pfv.r_data) == Past(cr_field.shadow))


return m return m


class CRCheck(PowerFVCheck, name="cons_cr"):
def get_testbench(self, dut, post):
tb_spec = CRSpec(post)
tb_top = tb.Testbench(tb_spec, dut)
return tb_top

@ -1,26 +1,27 @@
from amaranth import * from amaranth import *
from amaranth.asserts import * from amaranth.asserts import *


from .. import pfv from .. import PowerFVCheck
from ... import pfv, tb




__all__ = ["Check"] __all__ = ["GPRSpec", "GPRCheck"]




class Check(Elaboratable): class GPRSpec(Elaboratable):
"""General Purpose Registers check. """GPR consistency specification.


Checks that: Checks that:
* reads from a GPR are consistent with the last value that was written to it; * reads from a GPR return the last value that was written to it;
* writes to multiple GPRs by a single instruction (e.g. load with update) do not target the * writes to multiple GPRs by a single instruction (e.g. load with update) do not target the
same register. same register.
""" """
def __init__(self): def __init__(self, post):
self.pfv = pfv.Interface() self.pfv = pfv.Interface()
self.trig = Record([ self.post = tb.Trigger(cycle=post)
("pre", 1),
("post", 1), def triggers(self):
]) yield self.post


def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()
@ -58,7 +59,7 @@ class Check(Elaboratable):
with m.Else(): with m.Else():
m.d.sync += gpr_shadow.eq(self.pfv.rt.w_data) m.d.sync += gpr_shadow.eq(self.pfv.rt.w_data)


with m.If(self.trig.post): with m.If(self.post.stb):
m.d.sync += [ m.d.sync += [
Assume(Past(self.pfv.stb)), Assume(Past(self.pfv.stb)),
Assume(Past(self.pfv.order) == spec_order), Assume(Past(self.pfv.order) == spec_order),
@ -75,3 +76,10 @@ class Check(Elaboratable):
m.d.sync += Assert(Past(gpr_shadow) == Past(self.pfv.rt.r_data)) m.d.sync += Assert(Past(gpr_shadow) == Past(self.pfv.rt.r_data))


return m return m


class GPRCheck(PowerFVCheck, name="cons_gpr"):
def get_testbench(self, dut, post):
tb_spec = GPRSpec(post)
tb_top = tb.Testbench(tb_spec, dut)
return tb_top

@ -1,24 +1,25 @@
from amaranth import * from amaranth import *
from amaranth.asserts import * from amaranth.asserts import *


from .. import pfv from .. import PowerFVCheck
from ... import pfv, tb




__all__ = ["Check"] __all__ = ["IAForwardSpec", "IAForwardCheck"]




class Check(Elaboratable): class IAForwardSpec(Elaboratable):
"""IA forward check. """IA forward specification.


Given two instructions retiring in order, check that the NIA of the first matches the CIA Given two instructions retiring in order, the NIA of the first must match the CIA
of the second. of the second.
""" """
def __init__(self): def __init__(self, post):
self.pfv = pfv.Interface() self.pfv = pfv.Interface()
self.trig = Record([ self.post = tb.Trigger(cycle=post)
("pre", 1),
("post", 1), def triggers(self):
]) yield self.post


def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()
@ -33,7 +34,7 @@ class Check(Elaboratable):
pred_nia.eq(self.pfv.nia) pred_nia.eq(self.pfv.nia)
] ]


with m.If(self.trig.post): with m.If(self.post.stb):
m.d.sync += [ m.d.sync += [
Assume(self.pfv.stb), Assume(self.pfv.stb),
Assume(self.pfv.order == pred_order + 1), Assume(self.pfv.order == pred_order + 1),
@ -42,3 +43,10 @@ class Check(Elaboratable):
] ]


return m return m


class IAForwardCheck(PowerFVCheck, name="cons_ia_fwd"):
def get_testbench(self, dut, post):
tb_spec = IAForwardSpec(post)
tb_top = tb.Testbench(tb_spec, dut)
return tb_top

@ -3,24 +3,25 @@ from collections import OrderedDict
from amaranth import * from amaranth import *
from amaranth.asserts import * from amaranth.asserts import *


from .. import pfv from .. import PowerFVCheck
from ... import pfv, tb




__all__ = ["Check"] __all__ = ["SPRSpec", "SPRCheck"]




class Check(Elaboratable): class SPRSpec(Elaboratable):
"""Special Purpose Registers check. """SPR consistency specification.


Checks that reads from supported SPRs are consistent with the last value that was written to Checks that reads from supported SPRs are the last value that was written to
them. them.
""" """
def __init__(self): def __init__(self, post):
self.pfv = pfv.Interface() self.pfv = pfv.Interface()
self.trig = Record([ self.post = tb.Trigger(cycle=post)
("pre", 1),
("post", 1), def triggers(self):
]) yield self.post


def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()
@ -46,7 +47,7 @@ class Check(Elaboratable):
spr.shadow .eq(pfv_spr.w_data), spr.shadow .eq(pfv_spr.w_data),
] ]


with m.If(self.trig.post): with m.If(self.post.stb):
m.d.sync += [ m.d.sync += [
Assume(Past(self.pfv.stb)), Assume(Past(self.pfv.stb)),
Assume(Past(self.pfv.order) == spec_order), Assume(Past(self.pfv.order) == spec_order),
@ -59,3 +60,10 @@ class Check(Elaboratable):
m.d.sync += Assert(Past(spr.shadow) == Past(pfv_spr.r_data)) m.d.sync += Assert(Past(spr.shadow) == Past(pfv_spr.r_data))


return m return m


class SPRCheck(PowerFVCheck, name="cons_spr"):
def get_testbench(self, dut, post):
tb_spec = SPRSpec(post)
tb_top = tb.Testbench(tb_spec, dut)
return tb_top

@ -0,0 +1,49 @@
from amaranth import *
from amaranth.asserts import *

from .. import PowerFVCheck
from ... import pfv, tb


__all__ = ["UniquenessSpec", "UniquenessCheck"]


class UniquenessSpec(Elaboratable):
"""Uniqueness specification.

A core cannot retire two instructions that share the same `pfv.order` identifier.
"""
def __init__(self, pre, post):
self.pfv = pfv.Interface()
self.pre = tb.Trigger(cycle=pre)
self.post = tb.Trigger(cycle=post)

def triggers(self):
yield from (self.pre, self.post)

def elaborate(self, platform):
m = Module()

spec_order = AnyConst(self.pfv.order.shape())
duplicate = Signal()

with m.If(self.pre.stb):
m.d.sync += [
Assume(self.pfv.stb),
Assume(spec_order == self.pfv.order),
Assume(~duplicate),
]
with m.Elif(self.pfv.stb & (self.pfv.order == spec_order)):
m.d.sync += duplicate.eq(1)

with m.If(self.post.stb):
m.d.sync += Assert(~duplicate)

return m


class UniquenessCheck(PowerFVCheck, name="cons_unique"):
def get_testbench(self, dut, pre, post):
tb_spec = UniquenessSpec(pre, post)
tb_top = tb.Testbench(tb_spec, dut)
return tb_top

@ -1,33 +1,32 @@
from amaranth import * from amaranth import *
from amaranth.asserts import * from amaranth.asserts import *


from .. import pfv from .. import PowerFVCheck
from ..insn import * from ... import pfv, tb
from ..utils import iea_mask from ...utils import iea_mask


from ._fmt import *
from ._insn import *


__all__ = ["Check"]


__all__ = ["BranchSpec", "BranchCheck"]


class Check(Elaboratable):
_insn_cls = None


def __init_subclass__(cls, *, insn_cls): class BranchSpec(Elaboratable):
cls._insn_cls = insn_cls def __init__(self, insn_cls, post):
self.insn_cls = insn_cls
self.pfv = pfv.Interface()
self.post = tb.Trigger(cycle=post)


def __init__(self): def triggers(self):
self.pfv = pfv.Interface() yield self.post
self.trig = Record([
("pre", 1),
("post", 1),
])


def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()


spec_insn = self._insn_cls() spec_insn = self.insn_cls()


with m.If(self.trig.post): with m.If(self.post.stb):
m.d.sync += [ m.d.sync += [
Assume(self.pfv.stb), Assume(self.pfv.stb),
Assume(self.pfv.insn[32:] == spec_insn), Assume(self.pfv.insn[32:] == spec_insn),
@ -37,7 +36,7 @@ class Check(Elaboratable):
msr_w_sf = Signal() msr_w_sf = Signal()
m.d.comb += msr_w_sf.eq(self.pfv.msr.w_data[63]) m.d.comb += msr_w_sf.eq(self.pfv.msr.w_data[63])


if isinstance(spec_insn, (Instruction_B, Instruction_XL_b)): if isinstance(spec_insn, (Instruction_B, Instruction_XL_bc)):
bo_valid_patterns = [ bo_valid_patterns = [
"001--", "001--",
"011--", "011--",
@ -57,7 +56,7 @@ class Check(Elaboratable):
bo_invalid = Signal() bo_invalid = Signal()
m.d.comb += bo_invalid.eq(~spec_insn.bo.matches(*bo_valid_patterns)) m.d.comb += bo_invalid.eq(~spec_insn.bo.matches(*bo_valid_patterns))


with m.If(self.trig.post): with m.If(self.post.stb):
m.d.sync += Assert(bo_invalid.implies(self.pfv.intr)) m.d.sync += Assert(bo_invalid.implies(self.pfv.intr))


# NIA # NIA
@ -73,7 +72,7 @@ class Check(Elaboratable):
offset.eq(spec_insn.li) offset.eq(spec_insn.li)
] ]


elif isinstance(spec_insn, (Instruction_B, Instruction_XL_b)): elif isinstance(spec_insn, (Instruction_B, Instruction_XL_bc)):
cond_bit = Signal() cond_bit = Signal()
ctr_any = Signal() ctr_any = Signal()
cond_ok = Signal() cond_ok = Signal()
@ -116,7 +115,7 @@ class Check(Elaboratable):


m.d.comb += spec_nia.eq(iea_mask(target, msr_w_sf)) m.d.comb += spec_nia.eq(iea_mask(target, msr_w_sf))


with m.If(self.trig.post & ~self.pfv.intr): with m.If(self.post.stb & ~self.pfv.intr):
m.d.sync += Assert(self.pfv.nia == spec_nia) m.d.sync += Assert(self.pfv.nia == spec_nia)


# CR # CR
@ -125,12 +124,12 @@ class Check(Elaboratable):


if isinstance(spec_insn, Instruction_I): if isinstance(spec_insn, Instruction_I):
m.d.comb += spec_cr_r_stb.eq(0) m.d.comb += spec_cr_r_stb.eq(0)
elif isinstance(spec_insn, (Instruction_B, Instruction_XL_b)): elif isinstance(spec_insn, (Instruction_B, Instruction_XL_bc)):
m.d.comb += spec_cr_r_stb[::-1].bit_select(spec_insn.bi[2:], width=1).eq(1) m.d.comb += spec_cr_r_stb[::-1].bit_select(spec_insn.bi[2:], width=1).eq(1)
else: else:
assert False assert False


with m.If(self.trig.post & ~self.pfv.intr): with m.If(self.post.stb & ~self.pfv.intr):
for i, spec_cr_r_stb_bit in enumerate(spec_cr_r_stb): for i, spec_cr_r_stb_bit in enumerate(spec_cr_r_stb):
pfv_cr_r_stb_bit = self.pfv.cr.r_stb[i] 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)) m.d.sync += Assert(spec_cr_r_stb_bit.implies(pfv_cr_r_stb_bit))
@ -143,7 +142,7 @@ class Check(Elaboratable):


if isinstance(spec_insn, (Instruction_I, Instruction_B)): if isinstance(spec_insn, (Instruction_I, Instruction_B)):
m.d.comb += spec_lr_r_stb.eq(0) m.d.comb += spec_lr_r_stb.eq(0)
elif isinstance(spec_insn, (Instruction_XL_b)): elif isinstance(spec_insn, (Instruction_XL_bc)):
if isinstance(spec_insn, (BCLR, BCLRL)): if isinstance(spec_insn, (BCLR, BCLRL)):
m.d.comb += spec_lr_r_stb.eq(1) m.d.comb += spec_lr_r_stb.eq(1)
else: else:
@ -159,7 +158,7 @@ class Check(Elaboratable):
spec_lr_w_data.eq(iea_mask(cia_4, msr_w_sf)), spec_lr_w_data.eq(iea_mask(cia_4, msr_w_sf)),
] ]


with m.If(self.trig.post & ~self.pfv.intr): with m.If(self.post.stb & ~self.pfv.intr):
m.d.sync += [ m.d.sync += [
Assert(self.pfv.lr.r_stb == spec_lr_r_stb), 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 == spec_lr_w_stb),
@ -176,7 +175,7 @@ class Check(Elaboratable):
m.d.comb += spec_ctr_r_stb.eq(0) m.d.comb += spec_ctr_r_stb.eq(0)
elif isinstance(spec_insn, Instruction_B): elif isinstance(spec_insn, Instruction_B):
m.d.comb += spec_ctr_r_stb.eq(~spec_insn.bo[4-2]) m.d.comb += spec_ctr_r_stb.eq(~spec_insn.bo[4-2])
elif isinstance(spec_insn, Instruction_XL_b): elif isinstance(spec_insn, Instruction_XL_bc):
if isinstance(spec_insn, (BCCTR, BCCTRL)): if isinstance(spec_insn, (BCCTR, BCCTRL)):
m.d.comb += spec_ctr_r_stb.eq(1) m.d.comb += spec_ctr_r_stb.eq(1)
else: else:
@ -188,7 +187,7 @@ class Check(Elaboratable):
m.d.comb += spec_ctr_w_stb.eq(0) m.d.comb += spec_ctr_w_stb.eq(0)
elif isinstance(spec_insn, Instruction_B): elif isinstance(spec_insn, Instruction_B):
m.d.comb += spec_ctr_w_stb.eq(~spec_insn.bo[4-2]) m.d.comb += spec_ctr_w_stb.eq(~spec_insn.bo[4-2])
elif isinstance(spec_insn, Instruction_XL_b): elif isinstance(spec_insn, Instruction_XL_bc):
if isinstance(spec_insn, (BCCTR, BCCTRL)): if isinstance(spec_insn, (BCCTR, BCCTRL)):
m.d.comb += spec_ctr_w_stb.eq(0) m.d.comb += spec_ctr_w_stb.eq(0)
else: else:
@ -198,7 +197,7 @@ class Check(Elaboratable):


m.d.comb += spec_ctr_w_data.eq(self.pfv.ctr.r_data - 1) m.d.comb += spec_ctr_w_data.eq(self.pfv.ctr.r_data - 1)


with m.If(self.trig.post & ~self.pfv.intr): with m.If(self.post.stb & ~self.pfv.intr):
m.d.sync += [ m.d.sync += [
Assert(self.pfv.ctr.r_stb == spec_ctr_r_stb), 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 == spec_ctr_w_stb),
@ -211,7 +210,7 @@ class Check(Elaboratable):


if isinstance(spec_insn, (Instruction_I, Instruction_B)): if isinstance(spec_insn, (Instruction_I, Instruction_B)):
m.d.comb += spec_tar_r_stb.eq(0) m.d.comb += spec_tar_r_stb.eq(0)
elif isinstance(spec_insn, (Instruction_XL_b)): elif isinstance(spec_insn, (Instruction_XL_bc)):
if isinstance(spec_insn, (BCTAR, BCTARL)): if isinstance(spec_insn, (BCTAR, BCTARL)):
m.d.comb += spec_tar_r_stb.eq(1) m.d.comb += spec_tar_r_stb.eq(1)
else: else:
@ -219,9 +218,20 @@ class Check(Elaboratable):
else: else:
assert False assert False


with m.If(self.trig.post & ~self.pfv.intr): with m.If(self.post.stb & ~self.pfv.intr):
m.d.sync += [ m.d.sync += [
Assert(self.pfv.tar.r_stb == spec_tar_r_stb), Assert(self.pfv.tar.r_stb == spec_tar_r_stb),
] ]


return m return m


class BranchCheck(PowerFVCheck, name=None):
def __init_subclass__(cls, name, insn_cls):
super().__init_subclass__(name)
cls.insn_cls = insn_cls

def get_testbench(self, dut, post):
tb_spec = BranchSpec(self.insn_cls, post)
tb_top = tb.Testbench(tb_spec, dut)
return tb_top

@ -3,6 +3,9 @@ from amaranth.asserts import AnyConst
from amaranth.hdl.ast import ValueCastable from amaranth.hdl.ast import ValueCastable




__all__ = ["Instruction_I", "Instruction_B", "Instruction_XL_bc"]


class Instruction_I(ValueCastable): class Instruction_I(ValueCastable):
po = None po = None
li = None li = None
@ -45,7 +48,7 @@ class Instruction_B(ValueCastable):
return Cat(self.lk, self.aa, self.bd, self.bi, self.bo, self.po) return Cat(self.lk, self.aa, self.bd, self.bi, self.bo, self.po)




class Instruction_XL_b(ValueCastable): class Instruction_XL_bc(ValueCastable):
po = None po = None
bo = None bo = None
bi = None bi = None
@ -68,21 +71,3 @@ class Instruction_XL_b(ValueCastable):
@ValueCastable.lowermethod @ValueCastable.lowermethod
def as_value(self): def as_value(self):
return Cat(self.lk, self.xo, self.bh, self._0, self.bi, self.bo, self.po) 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

@ -0,0 +1,20 @@
from . import _fmt

# Branches

class B (_fmt.Instruction_I, po=18, aa=0, lk=0): pass
class BA (_fmt.Instruction_I, po=18, aa=1, lk=0): pass
class BL (_fmt.Instruction_I, po=18, aa=0, lk=1): pass
class BLA (_fmt.Instruction_I, po=18, aa=1, lk=1): pass

class BC (_fmt.Instruction_B, po=16, aa=0, lk=0): pass
class BCA (_fmt.Instruction_B, po=16, aa=1, lk=0): pass
class BCL (_fmt.Instruction_B, po=16, aa=0, lk=1): pass
class BCLA (_fmt.Instruction_B, po=16, aa=1, lk=1): pass

class BCLR (_fmt.Instruction_XL_bc, po=19, xo= 16, lk=0): pass
class BCLRL (_fmt.Instruction_XL_bc, po=19, xo= 16, lk=1): pass
class BCCTR (_fmt.Instruction_XL_bc, po=19, xo=528, lk=0): pass
class BCCTRL (_fmt.Instruction_XL_bc, po=19, xo=528, lk=1): pass
class BCTAR (_fmt.Instruction_XL_bc, po=19, xo=560, lk=0): pass
class BCTARL (_fmt.Instruction_XL_bc, po=19, xo=560, lk=1): pass

@ -0,0 +1,21 @@
from . import _insn
from ._branch import BranchCheck

# Branches

class B (BranchCheck, name="insn_b", insn_cls=_insn.B, ): pass
class BA (BranchCheck, name="insn_ba", insn_cls=_insn.BA ): pass
class BL (BranchCheck, name="insn_bl", insn_cls=_insn.BL ): pass
class BLA (BranchCheck, name="insn_bla", insn_cls=_insn.BLA ): pass

class BC (BranchCheck, name="insn_bc", insn_cls=_insn.BC ): pass
class BCA (BranchCheck, name="insn_bca", insn_cls=_insn.BCA ): pass
class BCL (BranchCheck, name="insn_bcl", insn_cls=_insn.BCL ): pass
class BCLA (BranchCheck, name="insn_bcla", insn_cls=_insn.BCLA ): pass

class BCLR (BranchCheck, name="insn_bclr", insn_cls=_insn.BCLR ): pass
class BCLRL (BranchCheck, name="insn_bclrl", insn_cls=_insn.BCLRL ): pass
class BCCTR (BranchCheck, name="insn_bcctr", insn_cls=_insn.BCCTR ): pass
class BCCTRL (BranchCheck, name="insn_bcctrl", insn_cls=_insn.BCCTRL): pass
class BCTAR (BranchCheck, name="insn_bctar", insn_cls=_insn.BCTAR ): pass
class BCTARL (BranchCheck, name="insn_bctarl", insn_cls=_insn.BCTARL): pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.B):
pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BA):
pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BC):
pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BCA):
pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BCCTR):
pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BCCTRL):
pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BCL):
pass

@ -1,10 +0,0 @@
from . import _branch

from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BCLA):
pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BCLR):
pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BCLRL):
pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BCTAR):
pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BCTARL):
pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BL):
pass

@ -1,9 +0,0 @@
from . import _branch
from .. import insn


__all__ = ["Check"]


class Check(_branch.Check, insn_cls=insn.BLA):
pass

@ -1,40 +0,0 @@
from amaranth import *
from amaranth.asserts import *

from .. import pfv


__all__ = ["Check"]


class Check(Elaboratable):
"""Uniqueness check.

Check that the core cannot retire two instructions with the same `pfv.order` identifier.
"""
def __init__(self):
self.pfv = pfv.Interface()
self.trig = Record([
("pre", 1),
("post", 1),
])

def elaborate(self, platform):
m = Module()

spec_order = AnyConst(self.pfv.order.shape())
duplicate = Signal()

with m.If(self.trig.pre):
m.d.sync += [
Assume(self.pfv.stb),
Assume(spec_order == self.pfv.order),
Assume(~duplicate),
]
with m.Elif(self.pfv.stb & (self.pfv.order == spec_order)):
m.d.sync += duplicate.eq(1)

with m.If(self.trig.post):
m.d.sync += Assert(~duplicate)

return m

@ -1,51 +1,78 @@
from collections import OrderedDict

from amaranth import * from amaranth import *
from amaranth.asserts import * from amaranth.asserts import *
from amaranth.utils import bits_for



__all__ = ["Trigger", "Testbench"]


__all__ = ["Testbench"]


class Trigger:
def __init__(self, cycle, name=None, src_loc_at=0):
if not isinstance(cycle, int) or cycle < 0:
raise ValueError("Clock cycle must be a non-negative integer, not {!r}"
.format(cycle))


def _check_triggers(t_start, t_pre, t_post): self.stb = Signal(name=name, src_loc_at=1 + src_loc_at)
if not isinstance(t_start, int) or t_start <= 0: self.cycle = cycle
raise ValueError("t_start must be a positive integer, not {!r}"
.format(t_start))
if not isinstance(t_post, int) or t_post < t_start:
raise ValueError("t_post must be an integer greater than or equal to t_start ({}), not {!r}"
.format(t_start, t_post))
if not isinstance(t_pre, int) or t_pre > t_post or t_pre < t_start:
raise ValueError("t_pre must be an integer between t_start and t_post (i.e. [{},{}]), "
"not {!r}".format(t_start, t_post, t_pre))




class Testbench(Elaboratable): class Testbench(Elaboratable):
def __init__(self, check, dut, *, t_start=1, t_pre=None, t_post=20): def __init__(self, spec, dut, start=0):
if t_pre is None and t_post is not None: self.spec = spec
t_pre = t_post self.dut = dut
self.start = Trigger(cycle=start)

self._triggers = OrderedDict()
self._bmc_depth = None
self._frozen = False

self.add_trigger(self.start)
for trigger in spec.triggers():
self.add_trigger(trigger)

def freeze(self):
if not self._frozen:
self._bmc_depth = max(t.cycle for t in self.triggers()) + 1
self._frozen = True


_check_triggers(t_start, t_pre, t_post) def add_trigger(self, trigger):
if self._frozen:
raise ValueError("Testbench is frozen.")
if not isinstance(trigger, Trigger):
raise TypeError("Trigger must be an instance of Trigger, not {!r}"
.format(trig))
self._triggers[id(trigger)] = trigger


self.check = check def triggers(self):
self.dut = dut yield from self._triggers.values()
self.t_start = t_start
self.t_pre = t_pre @property
self.t_post = t_post def bmc_depth(self):
self.freeze()
return self._bmc_depth


def elaborate(self, platform): def elaborate(self, platform):
m = Module() m = Module()


timer = Signal(range(self.t_post + 2), reset=1) cycle = Signal(range(self.bmc_depth), reset=0)

with m.If(cycle < self.bmc_depth):
m.d.sync += cycle.eq(cycle + 1)

for trigger in self.triggers():
m.d.comb += trigger.stb.eq(cycle == trigger.cycle)

spec_rst = Signal(reset=1)


with m.If(timer <= self.t_post): with m.If(self.start.stb):
m.d.sync += timer.eq(timer + 1) m.d.sync += spec_rst.eq(0)


m.submodules.check = ResetInserter(timer < self.t_start)(self.check) m.submodules.spec = ResetInserter(spec_rst)(self.spec)
m.submodules.dut = self.dut m.submodules.dut = self.dut


m.d.comb += [ m.d.comb += self.spec.pfv.eq(self.dut.pfv)
self.check.pfv.eq(self.dut.pfv),
self.check.trig.pre .eq(timer == self.t_pre),
self.check.trig.post.eq(timer == self.t_post),
]


m.d.comb += Assume(ResetSignal() == Initial()) m.d.comb += Assume(ResetSignal() == Initial())



Loading…
Cancel
Save