|
|
|
from amaranth import *
|
|
|
|
from amaranth.lib.coding import Encoder
|
|
|
|
from amaranth.asserts import *
|
|
|
|
|
|
|
|
from power_fv.check import PowerFVCheck
|
|
|
|
from power_fv.check._timer import Timer
|
|
|
|
|
|
|
|
|
|
|
|
__all__ = ["GPRCheck"]
|
|
|
|
|
|
|
|
|
|
|
|
class GPRCheck(PowerFVCheck, name=("gpr",)):
|
|
|
|
"""GPR consistency check.
|
|
|
|
|
|
|
|
Checks that:
|
|
|
|
* 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 conflict
|
|
|
|
with each other.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def testbench(self):
|
|
|
|
return GPRTestbench(self)
|
|
|
|
|
|
|
|
|
|
|
|
class GPRTestbench(Elaboratable):
|
|
|
|
def __init__(self, check):
|
|
|
|
if not isinstance(check, GPRCheck):
|
|
|
|
raise TypeError("Check must be an instance of GPRCheck, not {!r}"
|
|
|
|
.format(check))
|
|
|
|
self.check = check
|
|
|
|
self.name = "gpr_tb"
|
|
|
|
|
|
|
|
def elaborate(self, platform):
|
|
|
|
m = Module()
|
|
|
|
|
|
|
|
m.submodules.t_post = t_post = Timer(self.check.depth - 1)
|
|
|
|
m.submodules.dut = dut = self.check.dut
|
|
|
|
|
|
|
|
spec_order = AnyConst(dut.pfv.order.shape())
|
|
|
|
spec_index = AnyConst(unsigned(5))
|
|
|
|
|
|
|
|
gpr_write = Record([("ra", 1), ("rb", 1), ("rs", 1), ("rt", 1)])
|
|
|
|
gpr_read = Record.like(gpr_write)
|
|
|
|
|
|
|
|
m.d.comb += [
|
|
|
|
gpr_write.ra.eq(dut.pfv.ra.w_stb & (dut.pfv.ra.index == spec_index)),
|
|
|
|
gpr_write.rb.eq(dut.pfv.rb.w_stb & (dut.pfv.rb.index == spec_index)),
|
|
|
|
gpr_write.rs.eq(dut.pfv.rs.w_stb & (dut.pfv.rs.index == spec_index)),
|
|
|
|
gpr_write.rt.eq(dut.pfv.rt.w_stb & (dut.pfv.rt.index == spec_index)),
|
|
|
|
|
|
|
|
gpr_read .ra.eq(dut.pfv.ra.r_stb & (dut.pfv.ra.index == spec_index)),
|
|
|
|
gpr_read .rb.eq(dut.pfv.rb.r_stb & (dut.pfv.rb.index == spec_index)),
|
|
|
|
gpr_read .rs.eq(dut.pfv.rs.r_stb & (dut.pfv.rs.index == spec_index)),
|
|
|
|
gpr_read .rt.eq(dut.pfv.rt.r_stb & (dut.pfv.rt.index == spec_index)),
|
|
|
|
]
|
|
|
|
|
|
|
|
gpr_conflict = Record([("prev", 1), ("curr", 1)])
|
|
|
|
gpr_written = Record([("prev", 1)])
|
|
|
|
gpr_shadow = Record([("prev", 64)])
|
|
|
|
|
|
|
|
m.submodules.write_enc = write_enc = Encoder(len(gpr_write))
|
|
|
|
m.d.comb += write_enc.i.eq(gpr_write)
|
|
|
|
|
|
|
|
with m.If(dut.pfv.stb & write_enc.i.any() & write_enc.n):
|
|
|
|
# Write conflict: more then one bit of `gpr_write` is asserted.
|
|
|
|
m.d.comb += gpr_conflict.curr.eq(1)
|
|
|
|
m.d.sync += gpr_conflict.prev.eq(dut.pfv.order < spec_order)
|
|
|
|
|
|
|
|
with m.If(dut.pfv.stb & (dut.pfv.order < spec_order) & ~dut.pfv.skip):
|
|
|
|
with m.If(gpr_write.any()):
|
|
|
|
m.d.sync += gpr_written.prev.eq(1)
|
|
|
|
with m.If(gpr_write.ra):
|
|
|
|
m.d.sync += gpr_shadow.prev.eq(dut.pfv.ra.w_data)
|
|
|
|
with m.If(gpr_write.rb):
|
|
|
|
m.d.sync += gpr_shadow.prev.eq(dut.pfv.rb.w_data)
|
|
|
|
with m.If(gpr_write.rs):
|
|
|
|
m.d.sync += gpr_shadow.prev.eq(dut.pfv.rs.w_data)
|
|
|
|
with m.If(gpr_write.rt):
|
|
|
|
m.d.sync += gpr_shadow.prev.eq(dut.pfv.rt.w_data)
|
|
|
|
|
|
|
|
with m.If(t_post.zero):
|
|
|
|
m.d.comb += [
|
|
|
|
Assume(dut.pfv.stb),
|
|
|
|
Assume(dut.pfv.order == spec_order),
|
|
|
|
Assume(~dut.pfv.skip),
|
|
|
|
Assert(~gpr_conflict.curr),
|
|
|
|
Assert(~gpr_conflict.prev),
|
|
|
|
]
|
|
|
|
with m.If(gpr_written.prev):
|
|
|
|
with m.If(gpr_read.ra):
|
|
|
|
m.d.comb += Assert(dut.pfv.ra.r_data == gpr_shadow.prev)
|
|
|
|
with m.If(gpr_read.rb):
|
|
|
|
m.d.comb += Assert(dut.pfv.rb.r_data == gpr_shadow.prev)
|
|
|
|
with m.If(gpr_read.rs):
|
|
|
|
m.d.comb += Assert(dut.pfv.rs.r_data == gpr_shadow.prev)
|
|
|
|
with m.If(gpr_read.rt):
|
|
|
|
m.d.comb += Assert(dut.pfv.rt.r_data == gpr_shadow.prev)
|
|
|
|
|
|
|
|
m.d.comb += Assert(~Past(t_post.zero))
|
|
|
|
|
|
|
|
return m
|