From be42efd36534428aa87b853a21342bfc194f6cf0 Mon Sep 17 00:00:00 2001 From: Michael Nolan Date: Tue, 28 Jan 2020 11:43:21 -0500 Subject: [PATCH] Add rudimentary proof to fpmax --- src/ieee754/fpmax/formal/proof.sby | 13 ++++ src/ieee754/fpmax/formal/proof_fmax_mod.py | 81 ++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/ieee754/fpmax/formal/proof.sby create mode 100644 src/ieee754/fpmax/formal/proof_fmax_mod.py diff --git a/src/ieee754/fpmax/formal/proof.sby b/src/ieee754/fpmax/formal/proof.sby new file mode 100644 index 00000000..6c26b0f6 --- /dev/null +++ b/src/ieee754/fpmax/formal/proof.sby @@ -0,0 +1,13 @@ +[options] +mode bmc +depth 5 + +[engines] +smtbmc yices + +[script] +read_ilang proof.il +prep -top top + +[files] +proof.il diff --git a/src/ieee754/fpmax/formal/proof_fmax_mod.py b/src/ieee754/fpmax/formal/proof_fmax_mod.py new file mode 100644 index 00000000..386cd0fd --- /dev/null +++ b/src/ieee754/fpmax/formal/proof_fmax_mod.py @@ -0,0 +1,81 @@ +# Proof of correctness for FPMAX module +# Copyright (C) 2020 Michael Nolan + +from nmigen import Module, Signal, Elaboratable +from nmigen.asserts import Assert, Assume +from nmigen.cli import rtlil + +from ieee754.fpcommon.fpbase import FPNumDecode, FPNumBaseRecord +from ieee754.fpmax.fpmax import FPMAXPipeMod +from ieee754.pipeline import PipelineSpec +import subprocess + + +# This defines a module to drive the device under test and assert +# properties about its outputs +class FPMAXDriver(Elaboratable): + def __init__(self, pspec): + # inputs and outputs + self.pspec = pspec + self.a = Signal(pspec.width) + self.b = Signal(pspec.width) + self.z = Signal(pspec.width) + self.opc = Signal(pspec.op_wid) + self.muxid = Signal(pspec.id_wid) + + def elaborate(self, platform): + m = Module() + + m.submodules.dut = dut = FPMAXPipeMod(self.pspec) + + a1 = FPNumBaseRecord(self.pspec.width, False) + b1 = FPNumBaseRecord(self.pspec.width, False) + z1 = FPNumBaseRecord(self.pspec.width, False) + m.submodules.sc_decode_a = a1 = FPNumDecode(None, a1) + m.submodules.sc_decode_b = b1 = FPNumDecode(None, b1) + m.submodules.sc_decode_z = z1 = FPNumDecode(None, z1) + + m.d.comb += [a1.v.eq(self.a), + b1.v.eq(self.b), + z1.v.eq(self.z)] + + m.d.comb += Assert((z1.v == a1.v) | (z1.v == b1.v) | (z1.v == a1.fp.nan2(0))) + + # connect up the inputs and outputs. I think these could + # theoretically be $anyconst/$anysync but I'm not sure nmigen + # has support for that + m.d.comb += dut.i.a.eq(self.a) + m.d.comb += dut.i.b.eq(self.b) + m.d.comb += dut.i.ctx.op.eq(self.opc) + m.d.comb += dut.i.muxid.eq(self.muxid) + m.d.comb += self.z.eq(dut.o.z) + + + return m + + def ports(self): + return [self.a, self.b, self.z, self.opc, self.muxid] + + +def run_test(bits=32): + m = FPMAXDriver(PipelineSpec(bits, 2, 1)) + + il = rtlil.convert(m, ports=m.ports()) + with open("proof.il", "w") as f: + f.write(il) + p = subprocess.Popen(['sby', '-f', 'proof.sby'], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + if p.wait() == 0: + out, _ = p.communicate() + print("Proof successful!") + print(out.decode('utf-8')) + else: + print("Proof failed") + out, err = p.communicate() + print(out.decode('utf-8')) + print(err.decode('utf-8')) + + +if __name__ == "__main__": + run_test(bits=32) -- 2.30.2