Progress with smt2-based bmc scripts

This commit is contained in:
Clifford Wolf 2015-08-10 12:11:33 +02:00
parent 93d78f38d8
commit 12e64c7968
3 changed files with 183 additions and 21 deletions

View File

@ -1,3 +1,6 @@
debug.smt2 debug.smt2
mem_equiv_a.smt2 mem_equiv_a.smt2
mem_equiv_b.smt2 mem_equiv_b.smt2
mem_equiv_tb
mem_equiv_tb.v
mem_equiv_tb.vcd

View File

@ -1,11 +1,15 @@
#!/usr/bin/python3 #!/usr/bin/python3
import sys, getopt import os, sys, getopt
import subprocess import subprocess
steps=15 steps = 20
debug_print = False debug_print = False
debug_file = None # open("debug.smt2", "w") debug_file = open("debug.smt2", "w")
# Yices Bindings
#####################################
yices = subprocess.Popen(['yices-smt2', '--incremental'], stdin=subprocess.PIPE, yices = subprocess.Popen(['yices-smt2', '--incremental'], stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
@ -76,15 +80,44 @@ def yices_parse(stmt):
return expr, cursor return expr, cursor
return worker(stmt)[0] return worker(stmt)[0]
def yices_get(mod_name, net_name, state_name): def yices_bv2hex(v):
yices_write("(get-value ((|%s_n %s| %s)))" % (mod_name, net_name, state_name)) h = ""
while len(v) > 2:
d = 0
if len(v) > 0 and v[-1] == "1": d += 1
if len(v) > 1 and v[-2] == "1": d += 2
if len(v) > 2 and v[-3] == "1": d += 4
if len(v) > 3 and v[-4] == "1": d += 8
h = hex(d)[2:] + h
if len(v) < 4: break
v = v[:-4]
return h
def yices_bv2bin(v):
return v[2:]
def yices_get(expr):
yices_write("(get-value (%s))" % (expr))
return yices_parse(yices_read())[0][1] return yices_parse(yices_read())[0][1]
def yices_get_bool(mod_name, net_name, state_name): def yices_get_net(mod_name, net_name, state_name):
v = yices_get(mod_name, net_name, state_name) return yices_get("(|%s_n %s| %s)" % (mod_name, net_name, state_name))
def yices_get_net_bool(mod_name, net_name, state_name):
v = yices_get_net(mod_name, net_name, state_name)
assert v in ["true", "false"] assert v in ["true", "false"]
return 1 if v == "true" else 0 return 1 if v == "true" else 0
def yices_get_net_hex(mod_name, net_name, state_name):
return yices_bv2hex(yices_get_net(mod_name, net_name, state_name))
def yices_get_net_bin(mod_name, net_name, state_name):
return yices_bv2bin(yices_get_net(mod_name, net_name, state_name))
# Main Program
#####################################
yices_write("(set-logic QF_AUFBV)") yices_write("(set-logic QF_AUFBV)")
with open("mem_equiv_a.smt2", "r") as f: with open("mem_equiv_a.smt2", "r") as f:
@ -116,34 +149,55 @@ yices_write("(assert (not (|main_a_n mem_valid| a%d)))" % (steps-1))
yices_write("(assert (not (|main_b_n mem_valid| b%d)))" % (steps-1)) yices_write("(assert (not (|main_b_n mem_valid| b%d)))" % (steps-1))
yices_write("(assert (|main_a_n trap| a%d))" % (steps-1)) yices_write("(assert (|main_a_n trap| a%d))" % (steps-1))
yices_write("(assert (|main_b_n trap| b%d))" % (steps-1)) yices_write("(assert (|main_b_n trap| b%d))" % (steps-1))
yices_write("(assert (|main_a_n trap| a%d))" % (steps-2))
yices_write("(assert (|main_b_n trap| b%d))" % (steps-2))
# look for differences in memory or register file # look for differences in memory or register file
yices_write(("(assert (or (distinct (|main_a_m cpu.cpuregs| a%d) (|main_b_m cpu.cpuregs| b%d)) " + yices_write(("(assert (or (distinct (|main_a_m cpu.cpuregs| a%d) (|main_b_m cpu.cpuregs| b%d)) " +
"(distinct (|main_a_m memory| a%d) (|main_b_m memory| b%d))))") % (steps-1, steps-1, steps-1, steps-1)) "(distinct (|main_a_m memory| a%d) (|main_b_m memory| b%d))))") % (steps-1, steps-1, steps-1, steps-1))
def make_cpu_regs(step):
for i in range(1, 32):
yices_write("(define-fun a%d_r%d () (_ BitVec 32) (select (|main_a_m cpu.cpuregs| a%d) #b%s))" % (step, i, step, bin(32+i)[3:]))
yices_write("(define-fun b%d_r%d () (_ BitVec 32) (select (|main_b_m cpu.cpuregs| b%d) #b%s))" % (step, i, step, bin(32+i)[3:]))
make_cpu_regs(0)
make_cpu_regs(steps-1)
print("checking...") print("checking...")
yices_write("(check-sat)") yices_write("(check-sat)")
def print_status(mod, step): def print_status(mod, step):
resetn = yices_get_bool("main_" + mod, "resetn", "%s%d" % (mod, step)) resetn = yices_get_net_bool("main_" + mod, "resetn", "%s%d" % (mod, step))
memvld = yices_get_bool("main_" + mod, "mem_valid", "%s%d" % (mod, step)) memvld = yices_get_net_bool("main_" + mod, "mem_valid", "%s%d" % (mod, step))
domem = yices_get_bool("main_" + mod, "domem", "%s%d" % (mod, step)) domem = yices_get_net_bool("main_" + mod, "domem", "%s%d" % (mod, step))
memrdy = yices_get_bool("main_" + mod, "mem_ready", "%s%d" % (mod, step)) memrdy = yices_get_net_bool("main_" + mod, "mem_ready", "%s%d" % (mod, step))
trap = yices_get_bool("main_" + mod, "trap", "%s%d" % (mod, step)) trap = yices_get_net_bool("main_" + mod, "trap", "%s%d" % (mod, step))
print("%s[%d]: resetn=%s, memvld=%s, domem=%s, memrdy=%s, trap=%s" % (mod, step, resetn, memvld, domem, memrdy, trap)) print("status %5s: resetn=%s, memvld=%s, domem=%s, memrdy=%s, trap=%s" % ("%s[%d]" % (mod, step), resetn, memvld, domem, memrdy, trap))
def print_mem_xfer(mod, step): def print_mem_xfer(mod, step):
yices_write("(get-value ((and (|main_%s_n mem_valid| %s%d) (|main_%s_n mem_ready| %s%d))))" % (mod, mod, step, mod, mod, step)) if yices_get("(and (|main_%s_n mem_valid| %s%d) (|main_%s_n mem_ready| %s%d))" % (mod, mod, step, mod, mod, step)) == 'true':
if yices_parse(yices_read())[0][1] == 'true': mem_addr = yices_get_net_hex("main_" + mod, "mem_addr", "%s%d" % (mod, step))
mem_addr = yices_get("main_" + mod, "mem_addr", "%s%d" % (mod, step)) mem_wdata = yices_get_net_hex("main_" + mod, "mem_wdata", "%s%d" % (mod, step))
mem_wdata = yices_get("main_" + mod, "mem_wdata", "%s%d" % (mod, step)) mem_wstrb = yices_get_net_bin("main_" + mod, "mem_wstrb", "%s%d" % (mod, step))
mem_wstrb = yices_get("main_" + mod, "mem_wstrb", "%s%d" % (mod, step)) mem_rdata = yices_get_net_hex("main_" + mod, "mem_rdata", "%s%d" % (mod, step))
mem_rdata = yices_get("main_" + mod, "mem_rdata", "%s%d" % (mod, step)) print("mem %5s: addr=%s, wdata=%s, wstrb=%s, rdata=%s" % ("%s[%d]" % (mod, step), mem_addr, mem_wdata, mem_wstrb, mem_rdata))
print("%s[%d]: addr=%s, wdata=%s, wstrb=%s, rdata=%s" % (mod, step, mem_addr, mem_wdata, mem_wstrb, mem_rdata))
def print_cpu_regs(step):
for i in range(1, 32):
ra = yices_bv2hex(yices_get("a%d_r%d" % (step, i)))
rb = yices_bv2hex(yices_get("b%d_r%d" % (step, i)))
print("%3s[%d]: A=%s B=%s%s" % ("x%d" % i, step, ra, rb, " !" if ra != rb else ""))
if yices_read() == "sat": if yices_read() == "sat":
print("yices returned sat -> model check failed!") print("yices returned sat -> model check failed!")
print()
print_cpu_regs(0)
print()
print_cpu_regs(steps-1)
print() print()
for i in range(steps): for i in range(steps):
print_status("a", i) print_status("a", i)
@ -160,6 +214,108 @@ if yices_read() == "sat":
for i in range(1, steps): for i in range(1, steps):
print_mem_xfer("b", i) print_mem_xfer("b", i)
with open("mem_equiv_tb.v", "w") as f:
print()
print("writing verilog test bench...")
memory_words = 1
memory_datas = { "a": dict(), "b": dict() }
for i in range(steps-1, 0, -1):
for mod in ["a", "b"]:
if yices_get("(and (|main_%s_n mem_valid| %s%d) (|main_%s_n mem_ready| %s%d))" % (mod, mod, i, mod, mod, i)) == 'true':
mem_addr = yices_get_net_hex("main_" + mod, "mem_addr", "%s%d" % (mod, i))
mem_rdata = yices_get_net_hex("main_" + mod, "mem_rdata", "%s%d" % (mod, i))
memory_datas[mod][mem_addr] = mem_rdata
memory_words = max(int(mem_addr, 16)+1, memory_words)
memory_data = dict()
for k, v in memory_datas["a"].items(): memory_data[k] = v
for k, v in memory_datas["b"].items(): memory_data[k] = v
print("`timescale 1 ns / 1 ps", file=f)
print("", file=f)
print("module testbench;", file=f)
print(" reg clk = 1, resetn, domem_a, domem_b;", file=f)
print(" always #5 clk = ~clk;", file=f)
print("", file=f)
print(" main #(", file=f)
print(" .MEMORY_WORDS(%d)," % memory_words, file=f)
print(" .ENABLE_REGS_DUALPORT(0),", file=f)
print(" .TWO_STAGE_SHIFT(0),", file=f)
print(" .TWO_CYCLE_COMPARE(0),", file=f)
print(" .TWO_CYCLE_ALU(0)", file=f)
print(" ) main_a (", file=f)
print(" .clk(clk),", file=f)
print(" .resetn(resetn),", file=f)
print(" .domem(domem_a)", file=f)
print(" );", file=f)
print("", file=f)
print(" main #(", file=f)
print(" .MEMORY_WORDS(%d)," % memory_words, file=f)
print(" .ENABLE_REGS_DUALPORT(1),", file=f)
print(" .TWO_STAGE_SHIFT(1),", file=f)
print(" .TWO_CYCLE_COMPARE(1),", file=f)
print(" .TWO_CYCLE_ALU(1)", file=f)
print(" ) main_b (", file=f)
print(" .clk(clk),", file=f)
print(" .resetn(resetn),", file=f)
print(" .domem(domem_b)", file=f)
print(" );", file=f)
print("", file=f)
print(" task check_reg;", file=f)
print(" input [4:0] n;", file=f)
print(" begin", file=f)
print(" if (main_a.cpu.cpuregs[n] != main_b.cpu.cpuregs[n])", file=f)
print(" $display(\"Divergent values for reg %1d: A=%08x B=%08x\", n, main_a.cpu.cpuregs[n], main_b.cpu.cpuregs[n]);", file=f)
print(" end", file=f)
print(" endtask", file=f)
print("", file=f)
print(" task check_mem;", file=f)
print(" input [31:0] n;", file=f)
print(" begin", file=f)
print(" if (main_a.memory[n] != main_b.memory[n])", file=f)
print(" $display(\"Divergent values for memory addr %08x: A=%08x B=%08x\", n, main_a.cpu.cpuregs[n], main_b.cpu.cpuregs[n]);", file=f)
print(" end", file=f)
print(" endtask", file=f)
print("", file=f)
print(" initial begin", file=f)
print(" $dumpfile(\"mem_equiv_tb.vcd\");", file=f)
print(" $dumpvars(0, testbench);", file=f)
print("", file=f)
for i in range(1, 32):
ra = yices_bv2hex(yices_get("a%d_r%d" % (0, i)))
rb = yices_bv2hex(yices_get("b%d_r%d" % (0, i)))
print(" main_a.cpu.cpuregs[%2d] = 'h %s; main_b.cpu.cpuregs[%2d] = 'h %s;" % (i, ra, i, rb), file=f)
print("", file=f)
for addr, data in memory_data.items():
print(" main_a.memory['h %s] = 'h %s;" % (addr, data), file=f)
print(" main_b.memory['h %s] = 'h %s;" % (addr, data), file=f)
print("", file=f)
for i in range(steps):
print(" resetn = %d;" % yices_get_net_bool("main_a", "resetn", "a%d" % i), file=f)
print(" domem_a = %d;" % yices_get_net_bool("main_a", "domem", "a%d" % i), file=f)
print(" domem_b = %d;" % yices_get_net_bool("main_b", "domem", "b%d" % i), file=f)
print(" @(posedge clk);", file=f)
print("", file=f)
for i in range(1, 32):
print(" check_reg(%d);" % i, file=f)
for addr, data in memory_data.items():
print(" check_mem('h %s);" % addr, file=f)
print("", file=f)
print(" $finish;", file=f)
print(" end", file=f)
print("endmodule", file=f)
print("running verilog test bench...")
os.system("iverilog -o mem_equiv_tb -s testbench mem_equiv_tb.v mem_equiv.v ../../picorv32.v && ./mem_equiv_tb")
else: else:
print("yices returned unsat -> model check passed.") print("yices returned unsat -> model check passed.")
yices_write("(exit)")
yices.wait()

View File

@ -1,4 +1,7 @@
`timescale 1 ns / 1 ps
module main (input clk, resetn, domem, output trap); module main (input clk, resetn, domem, output trap);
parameter integer MEMORY_WORDS = 2**30;
parameter [0:0] ENABLE_REGS_DUALPORT = 1; parameter [0:0] ENABLE_REGS_DUALPORT = 1;
parameter [0:0] TWO_STAGE_SHIFT = 1; parameter [0:0] TWO_STAGE_SHIFT = 1;
parameter [0:0] TWO_CYCLE_COMPARE = 0; parameter [0:0] TWO_CYCLE_COMPARE = 0;
@ -28,7 +31,7 @@ module main (input clk, resetn, domem, output trap);
.mem_rdata(mem_rdata) .mem_rdata(mem_rdata)
); );
reg [31:0] memory [0:2**30-1]; reg [31:0] memory [0:MEMORY_WORDS-1];
assign mem_ready = domem && resetn && mem_valid; assign mem_ready = domem && resetn && mem_valid;
assign mem_rdata = memory[mem_addr >> 2]; assign mem_rdata = memory[mem_addr >> 2];