def formal(cls) -> Tuple[Module, List[Signal]]: m = Module() ph = ClockDomain("ph") clk = ClockSignal("ph") m.domains += ph m.d.sync += clk.eq(~clk) s = IC_7416374(clk="ph") m.submodules += s with m.If(s.n_oe): m.d.comb += Assert(s.q == 0) with m.If(~s.n_oe & Rose(clk)): m.d.comb += Assert(s.q == Past(s.d)) with m.If(~s.n_oe & Fell(clk) & ~Past(s.n_oe)): m.d.comb += Assert(s.q == Past(s.q)) sync_clk = ClockSignal("sync") sync_rst = ResetSignal("sync") # Make sure the clock is clocking m.d.comb += Assume(sync_clk == ~Past(sync_clk)) # Don't want to test what happens when we reset. m.d.comb += Assume(~sync_rst) m.d.comb += Assume(~ResetSignal("ph")) return m, [sync_clk, sync_rst, s.n_oe, s.q, s.d]
def cover_pending_counter(bld: Builder): m = Module() m.submodules.pc = pc = PendingCounter(3, 5) was_full = Signal() was_emptied = Signal() m.d.comb += pc.i_remove.eq(AnySeq(1)) m.d.comb += Assume(~(pc.i_remove & ~pc.o_any)) m.d.comb += Assume(~(~pc.i_remove & pc.o_full)) with m.If(pc.o_full): m.d.sync += was_full.eq(1) with m.If(~pc.o_any & was_full): m.d.sync += was_emptied.eq(1) m.d.comb += Cover(was_emptied) with bld.temp_open("formal.il") as f: il_text = rtlil.convert(m, ports=[pc.pending, pc.timer]) f.write(il_text) sby.verify( bld, "formal.sby", "formal.il", sby.Task("sby", "cover", depth=40, engines=["smtbmc", "yices"]), )
def elaborate(self, platform): m = Module() MAX_AMOUNT = Const(22) with m.If(self.i_reset): m.d.sync += self.counter.eq(0) with m.Elif(self.i_start_signal & (self.counter == 0)): m.d.sync += self.counter.eq(MAX_AMOUNT - 1) with m.Elif(self.counter != 0): m.d.sync += self.counter.eq(self.counter - 1) m.d.sync += self.o_busy.eq((~self.i_reset) & \ ((self.i_start_signal & (self.counter == 0)) | \ (self.counter > 1))) if self.fv_mode: f_past_valid = Signal(1, reset=0) m.d.sync += f_past_valid.eq(1) m.d.comb += Assert(self.counter < MAX_AMOUNT) with m.If(f_past_valid & Past(self.i_start_signal) & Past(self.o_busy)): m.d.comb += Assume(self.i_start_signal) with m.Elif(f_past_valid & Past(self.i_start_signal) & ~Past(self.o_busy)): m.d.comb += Assume(~self.i_start_signal) m.d.comb += Assert(self.o_busy == (self.counter != 0)) with m.If(f_past_valid & Past(self.counter) != 0): m.d.comb += Assert(self.counter < Past(self.counter)) return m
def elaborate(self, platform): m = Module() m.submodules.dut = dut = _DelayLine(self.delay) m.d.comb += Assume(~ResetSignal("sync")) m.d.comb += Assert( dut.o == Sample(expr=dut.i, clocks=self.delay, domain="sync")) return m
def formal_ripple(cls) -> Tuple[Module, List[Signal]]: """Formal verification for a bunch of ALUs in ripple-carry mode.""" m = Module() alus = [None] * 8 m.submodules.alu0 = alus[0] = IC_74181() m.submodules.alu1 = alus[1] = IC_74181() m.submodules.alu2 = alus[2] = IC_74181() m.submodules.alu3 = alus[3] = IC_74181() m.submodules.alu4 = alus[4] = IC_74181() m.submodules.alu5 = alus[5] = IC_74181() m.submodules.alu6 = alus[6] = IC_74181() m.submodules.alu7 = alus[7] = IC_74181() a = Signal(32) b = Signal(32) f = Signal(32) cin = Signal() cout = Signal() s = Signal(4) mt = Signal() for x in range(8): m.d.comb += alus[x].a.eq(a[x * 4:x * 4 + 4]) m.d.comb += alus[x].b.eq(b[x * 4:x * 4 + 4]) m.d.comb += f[x * 4:x * 4 + 4].eq(alus[x].f) m.d.comb += alus[x].m.eq(mt) m.d.comb += alus[x].s.eq(s) for x in range(7): m.d.comb += alus[x + 1].n_carryin.eq(alus[x].n_carryout) m.d.comb += alus[0].n_carryin.eq(~cin) m.d.comb += cout.eq(~alus[7].n_carryout) add_mode = (s == 9) & (mt == 0) sub_mode = (s == 6) & (mt == 0) m.d.comb += Assume(add_mode | sub_mode) y = Signal(33) with m.If(add_mode): m.d.comb += y.eq(a + b + cin) m.d.comb += Assert(f == y[:32]) m.d.comb += Assert(cout == y[32]) with m.Elif(sub_mode): m.d.comb += y.eq(a - b - ~cin) m.d.comb += Assert(f == y[:32]) m.d.comb += Assert(cout == ~y[32]) # Check how equality, unsigned gt, and unsigned gte comparisons work. with m.If(cin == 0): all_eq = Cat(*[i.a_eq_b for i in alus]).all() m.d.comb += Assert(all_eq == (a == b)) m.d.comb += Assert(cout == (a > b)) with m.Else(): m.d.comb += Assert(cout == (a >= b)) return m, [a, b, f, cin, cout, s, mt, y]
def bmc_pending_counter(bld: Builder): m = Module() m.submodules.pc = pc = PendingCounter(3, 5) m.d.comb += pc.i_remove.eq(AnySeq(1)) m.d.comb += Assume(~(pc.i_remove & ~pc.o_any)) m.d.comb += Assume(~(~pc.i_remove & pc.o_full)) with bld.temp_open("formal.il") as f: il_text = rtlil.convert(m, ports=[pc.pending, pc.timer]) f.write(il_text) sby.verify( bld, "formal.sby", "formal.il", sby.Task("sby", "bmc", depth=40, engines=["smtbmc", "yices"]), )
def verify_jtype(self, m): sig = self.build_signal(m, "U", [(0, 6, "1001011"), (7, 11, "10000"), (12, 19, "00100010"), (20, 20, "1"), (21, 30, "1011000111"), (31, 31, "1")]) j = JType("jtype") j.elaborate(m.d.comb, sig) m.d.comb += Assert(j.opcode == Const(0b1001011, 7)) m.d.comb += Assert(j.rd == Const(0b10000, 5)) m.d.comb += Assert( j.imm == Const(0b1111_1111_1111_0010_0010_1101_1000_1110, 32)) m.d.comb += Assert(j.match(opcode=0b1001011)) m.d.comb += Assert(j.match(opcode=0b1001010) == 0) m.d.comb += Assert(j.match(rd=0b10000)) m.d.comb += Assert(j.match(rd=0b00001) == 0) m.d.comb += Assert( j.match(imm=0b1111_1111_1111_0010_0010_1101_1000_1110) ) #extra bits - sign ext m.d.comb += Assert(j.match(imm=0b110100010110110001110) == 0) m.d.comb += Assert( j.match(opcode=0b1001011, rd=0b10000, imm=0b1111_1111_1111_0010_0010_1101_1000_1110)) j_builder_check = Signal(32) j_builder_opcode = Signal(7) j_builder_rd = Signal(5) j_builder_imm = Signal(21) m.d.comb += Assume(j_builder_imm[0] == 0) built_jtype = JType.build_i32(opcode=j_builder_opcode, rd=j_builder_rd, imm=j_builder_imm) m.d.comb += j_builder_check.eq(built_jtype) j = JType("jtype.build") j.elaborate(m.d.comb, built_jtype) m.d.comb += Assert(j_builder_opcode == j.opcode) m.d.comb += Assert(j_builder_rd == j.rd) m.d.comb += Assert(j_builder_imm == j.imm[0:21]) j = JType("jtype.se") j.elaborate(m.d.comb, Const(0x8000_0000, 32)) m.d.comb += Assert(j.imm[20] == 1) m.d.comb += Assert(j.imm[31] == 1) j = JType("jtype.ze") j.elaborate(m.d.comb, Const(0x7FFF_FFFF, 32)) m.d.comb += Assert(j.imm[20] == 0) m.d.comb += Assert(j.imm[31] == 0) return [j_builder_check, j_builder_opcode, j_builder_rd, j_builder_imm]
def verify_btype(self, m): sig = self.build_signal(m, "B", [(0, 6, "1001011"), (7, 7, "1"), (8, 11, "0000"), (12, 14, "001"), (15, 19, "00010"), (20, 24, "00011"), (25, 30, "011111"), (31, 31, "1")]) b = BType("btype") b.elaborate(m.d.comb, sig) m.d.comb += Assert(b.opcode == Const(0b1001011, 7)) m.d.comb += Assert(b.funct3 == Const(1, 3)) m.d.comb += Assert(b.rs1 == Const(2, 5)) m.d.comb += Assert(b.rs2 == Const(3, 5)) m.d.comb += Assert( b.imm == Cat(Const(0b1101111100000, 13), Repl(1, 19))) m.d.comb += Assert(b.match(opcode=0b1001011)) m.d.comb += Assert(b.match(rs1=2)) m.d.comb += Assert(b.match(rs2=3)) m.d.comb += Assert(b.match(funct3=1)) m.d.comb += Assert(b.match(imm=0b11111111111111111111101111100000)) m.d.comb += Assert( b.match(opcode=0b1001011, funct3=1, rs1=2, rs2=3, imm=0b11111111111111111111101111100000)) m.d.comb += Assert(~b.match(opcode=0b1001011, funct3=3, rs1=1, rs2=5, imm=0b11111111111111111111101111100000)) b_builder_check = Signal(32) b_builder_opcode = Signal(7) b_builder_f3 = Signal(3) b_builder_rs1 = Signal(5) b_builder_rs2 = Signal(5) b_builder_imm = Signal(13) m.d.comb += Assume(b_builder_imm[0] == 0) built_btype = BType.build_i32(opcode=b_builder_opcode, funct3=b_builder_f3, rs1=b_builder_rs1, rs2=b_builder_rs2, imm=b_builder_imm) m.d.comb += b_builder_check.eq(built_btype) b = BType("btype.build") b.elaborate(m.d.comb, built_btype) m.d.comb += Assert(b_builder_opcode == b.opcode) m.d.comb += Assert(b_builder_imm == Cat(Const(0, 1), b.imm[1:13])) m.d.comb += Assert(b.imm[13:32] == Repl(b_builder_imm[12], 32 - 13)) return [ b_builder_check, b_builder_opcode, b_builder_f3, b_builder_rs1, b_builder_rs2, b_builder_imm ]
def formal(cls) -> Tuple[Module, List[Signal]]: """Formal verification for my module.""" m = Module() m.submodules.my_class = my_class = cls() sync_clk = ClockSignal("sync") sync_rst = ResetSignal("sync") # Make sure that the output is always the same as the input m.d.comb += Assert(my_class.my_input == my_class.my_output) # Cover the case where the output is 1. m.d.comb += Cover(my_class.my_output == 1) # Make sure the clock is clocking m.d.comb += Assume(sync_clk == ~Past(sync_clk)) # Include this only if you don't want to test resets m.d.comb += Assume(~sync_rst) # Ensure sync's clock and reset signals are manipulable. return m, [sync_clk, sync_rst, my_class.my_input]
def formal(cls) -> Tuple[Module, List[Signal]]: """Formal verification for the Counter module.""" m = Module() m.submodules.c = c = cls() m.d.comb += Assert((c.count >= 1) & (c.count <= 999_999_999_999)) sync_clk = ClockSignal("sync") sync_rst = ResetSignal("sync") with m.If(Rose(sync_clk) & ~Initial()): with m.If(c.count == 1): m.d.comb += Assert(Past(c.count) == 999_999_999_999) with m.Else(): m.d.comb += Assert(c.count == (Past(c.count) + 1)) # Make sure the clock is clocking m.d.comb += Assume(sync_clk == ~Past(sync_clk)) # Don't want to test what happens when we reset. m.d.comb += Assume(~sync_rst) return m, [sync_clk, sync_rst]
def verify_utype(self, m): sig = self.build_signal(m, "U", [(0, 6, "1001011"), (7, 11, "10000"), (12, 31, "00100010000110111111")]) u = UType("utype") u.elaborate(m.d.comb, sig) m.d.comb += Assert(u.opcode == Const(0b1001011, 7)) m.d.comb += Assert(u.rd == Const(0b10000, 5)) m.d.comb += Assert( u.imm == Const(0b00100010000110111111000000000000, 32)) m.d.comb += Assert(u.match(opcode=0b1001011)) m.d.comb += Assert(u.match(rd=0b10000)) m.d.comb += Assert(u.match(imm=0b00100010000110111111000000000000)) m.d.comb += Assert(u.match(opcode=0b1011011) == 0) m.d.comb += Assert(u.match(rd=0b11000) == 0) m.d.comb += Assert( u.match(imm=0b10100010000110111111000000000000) == 0) m.d.comb += Assert( u.match(opcode=0b1001011, rd=0b10000, imm=0b00100010000110111111000000000000)) u = UType("utype.sign") u.elaborate(m.d.comb, Const(0x8000_0000, 32)) m.d.comb += Assert(u.imm[31] == 1) u = UType("utype.trailzero") u.elaborate(m.d.comb, Const(0xFFFF_FFFF, 32)) m.d.comb += Assert(u.imm.bit_select(0, 12) == 0) u_builder_check = Signal(32) u_builder_opcode = Signal(7) u_builder_rd = Signal(5) u_builder_imm = Signal(20) m.d.comb += Assume(u_builder_imm[0:12] == 0) built_utype = UType.build_i32(opcode=u_builder_opcode, rd=u_builder_rd, imm=u_builder_imm) m.d.comb += u_builder_check.eq(built_utype) u = UType("utype.build") u.elaborate(m.d.comb, built_utype) m.d.comb += Assert(u_builder_opcode == u.opcode) m.d.comb += Assert(u_builder_rd == u.rd) m.d.comb += Assert(Cat(Repl(0, 12), u_builder_imm[12:32]) == u.imm) return [u_builder_check, u_builder_opcode, u_builder_rd, u_builder_imm]
def formal(cls) -> Tuple[Module, List[Signal]]: m = Module() ph = ClockDomain("ph") clk = ClockSignal("ph") m.domains += ph m.d.sync += clk.eq(~clk) s = IC_reg32_with_mux(clk="ph", N=2, faster=True) m.submodules += s sync_clk = ClockSignal("sync") sync_rst = ResetSignal("sync") with m.If(Rose(clk)): with m.Switch(~Past(s.n_sel)): with m.Case(0b11): m.d.comb += Assert(0) with m.Case(0b01): m.d.comb += Assert(s.q == Past(s.d[0])) with m.Case(0b10): m.d.comb += Assert(s.q == Past(s.d[1])) with m.Default(): m.d.comb += Assert(s.q == Past(s.q)) # Make sure the clock is clocking m.d.comb += Assume(sync_clk == ~Past(sync_clk)) # Don't want to test what happens when we reset. m.d.comb += Assume(~sync_rst) m.d.comb += Assume(~ResetSignal("ph")) m.d.comb += Assume(s.n_sel != 0) return m, [sync_clk, sync_rst, s.d[0], s.d[1], s.n_sel, s.q]
def formal(cls) -> Tuple[Module, List[Signal]]: """Formal verification for the active high 74182 chip. Used with an active high 74181. """ m = Module() m.submodules.clu = clu = IC_74182_active_high() m.submodules.alu = alu = IC_74181() add_mode = (alu.s == 9) & (alu.m == 0) m.d.comb += Assume(add_mode) m.d.comb += [ clu.x[0].eq(alu.x), clu.y[0].eq(alu.y), clu.x[1:].eq(0), clu.y[1:].eq(0), clu.n_carryin.eq(alu.n_carryin), ] m.d.comb += Assert(clu.n_carryout_x == alu.n_carryout) return m, clu.ports() + alu.ports()
m.submodules.alu = alu = ALU8() m.domains.ph1 = ph1 = ClockDomain("ph1") rst = Signal() ph1clk = ClockSignal("ph1") ph1.rst = rst test_daa = Signal() m.submodules.alu2 = alu2 = ALU8() carry_in = Signal() sum9 = Signal(9) sum8 = Signal(8) sum5 = Signal(5) m.d.ph1 += Assume(rst == 0) m.d.comb += Assert(alu._ccs[6:] == 0b11) with m.If((~test_daa) | ~Past(test_daa)): with m.Switch(alu.func): with m.Case(ALU8Func.ADD, ALU8Func.ADC): # sumN = input1[:N] + input2[:N] (so sumN[N-1] is the carry bit) with m.If(alu.func == ALU8Func.ADD): m.d.comb += carry_in.eq(0) with m.Else(): m.d.comb += carry_in.eq(alu.ccs[Flags.C]) h = sum5[4] n = sum9[7] c = sum9[8] z = sum9[:8] == 0 v = sum8[7] ^ c
def check(self, m: Module): self.assert_cycles(m, 2) input1 = self.data.pre_a pre_h = self.data.pre_ccs[Flags.H] pre_c = self.data.pre_ccs[Flags.C] lo = input1[:4] hi = input1[4:] with m.If(pre_h): m.d.comb += Assume(lo <= 3) with m.If(pre_c): m.d.comb += Assume(hi <= 3) # Conditions taken directly from Motorola 6800 Programming Reference Manual, # the DAA instruction operation table. # Conditions are: C before DAA, a condition on hi, H before DAA, a condition on lo. conds = Array( [ Array([0, hi <= 9, 0, lo <= 9]), Array([0, hi <= 8, 0, lo >= 10]), Array([0, hi <= 9, 1, lo <= 3]), Array([0, hi >= 10, 0, lo <= 9]), Array([0, hi >= 9, 0, lo >= 10]), Array([0, hi >= 10, 1, lo <= 3]), Array([1, hi <= 2, 0, lo <= 9]), Array([1, hi <= 2, 0, lo >= 10]), Array([1, hi <= 3, 1, lo <= 3]), ] ) # Results are: DAA adjustment, state of C after DAA results = Array( [ Array([0, 0]), Array([6, 0]), Array([6, 0]), Array([0x60, 1]), Array([0x66, 1]), Array([0x66, 1]), Array([0x60, 1]), Array([0x66, 1]), Array([0x66, 1]), ] ) input_is_valid = Signal() expected_output = Signal(8) expected_c = Signal() for i in range(len(conds)): with m.If( (pre_c == conds[i][0]) & conds[i][1] & (pre_h == conds[i][2]) & conds[i][3] ): m.d.comb += input_is_valid.eq(1) m.d.comb += expected_output.eq(input1 + results[i][0]) m.d.comb += expected_c.eq(results[i][1]) z = expected_output == 0 n = expected_output[7] with m.If(input_is_valid): self.assert_registers(m, A=expected_output, PC=self.data.pre_pc + 1) self.assert_flags(m, Z=z, N=n, C=expected_c)
def formal(cls) -> Tuple[Module, List[Signal]]: """Formal verification for the async memory. Note that you MUST have multiclock on in the sby file, because there is more than one clock in the system -- the default formal clock and the local clock inside the memory. """ m = Module() m.submodules.mem = mem = AsyncMemory(width=32, addr_lines=5) # Assume "good practices": # * n_oe and n_wr are never simultaneously 0, and any changes # are separated by at least a cycle to allow buffers to turn off. # * memory address remains stable throughout a write cycle, and # is also stable just before a write cycle. m.d.comb += Assume(mem.n_oe | mem.n_wr) # Paren placement is very important! While Python logical operators # and, or have lower precedence than ==, the bitwise operators # &, |, ^ have *higher* precedence. It can be confusing when it looks # like you're writing a boolean expression, but you're actually writing # a bitwise expression. with m.If(Fell(mem.n_oe)): m.d.comb += Assume((mem.n_wr == 1) & (Past(mem.n_wr) == 1)) with m.If(Fell(mem.n_wr)): m.d.comb += Assume((mem.n_oe == 1) & (Past(mem.n_oe) == 1)) with m.If(Rose(mem.n_wr) | (mem.n_wr == 0)): m.d.comb += Assume(Stable(mem.addr)) m.d.comb += Cover((mem.data_out == 0xAAAAAAAA) & (Past(mem.data_out) == 0xBBBBBBBB)) # Make sure that when the output is disabled, the output is zero, and # when enabled, it's whatever we're pointing at in memory. with m.If(mem.n_oe == 1): m.d.comb += Assert(mem.data_out == 0) with m.Else(): m.d.comb += Assert(mem.data_out == mem._mem[mem.addr]) # If we just wrote data, make sure that cell that we pointed at # for writing now contains the data we wrote. with m.If(Rose(mem.n_wr)): m.d.comb += Assert(mem._mem[Past(mem.addr)] == Past(mem.data_in)) # Pick an address, any address. check_addr = AnyConst(5) # We assert that unless that address is written, its data will not # change. To know when we've written the data, we have to create # a clock domain to let us save the data when written. saved_data_clk = ClockDomain("saved_data_clk") m.domains.saved_data_clk = saved_data_clk saved_data_clk.clk = mem.n_wr saved_data = Signal(32) with m.If(mem.addr == check_addr): m.d.saved_data_clk += saved_data.eq(mem.data_in) with m.If(Initial()): m.d.comb += Assume(saved_data == mem._mem[check_addr]) m.d.comb += Assume(mem.n_wr == 1) with m.Else(): m.d.comb += Assert(saved_data == mem._mem[check_addr]) return m, [mem.addr, mem.data_in, mem.n_wr, mem.n_oe]
def elaborate(self, platform): m = Module() m.d.comb += Assume(~dut.request_req) m.d.comb += Assert(~dut.request_gnt) return m
def elaborate(self, platform: Platform) -> Module: m = Module() m.d.sync += self.PSW.eq(self._psw) m.d.comb += self._psw.eq(self.PSW) with m.Switch(self.oper): with m.Case(Operation.NOP): if self.verification is Operation.NOP: with m.If(~Initial()): m.d.comb += [ Assert(self._psw.N == self.PSW.N), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == self.PSW.Z), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.ADC): low = Cat(self.result[:4], self._psw.H) high = Cat(self.result[4:], self._psw.C) m.d.comb += [ low.eq(self.inputa[:4] + self.inputb[:4] + self.PSW.C), high.eq(self.inputa[4:] + self.inputb[4:] + self._psw.H), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), self._psw.V.eq(self.result[7] != self._psw.C), ] if self.verification is Operation.ADC: r = Signal(8) f = Signal(9) h = Signal(5) m.d.comb += [ r.eq(self.inputa.as_signed() + self.inputb.as_signed() + self.PSW.C), f.eq(self.inputa + self.inputb + self.PSW.C), h.eq(self.inputa[:4] + self.inputb[:4] + self.PSW.C), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self.result == f[:8]), Assert(self._psw.N == f[7]), Assert(self._psw.V == (f[7] ^ f[8])), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == h[4]), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(f[:8].bool())), Assert(self._psw.C == f[8]), ] with m.Case(Operation.SBC): low = Cat(self.result[:4], self._psw.H) high = Cat(self.result[4:], self._psw.C) m.d.comb += [ low.eq(self.inputa[:4] - self.inputb[:4] - self.PSW.C), high.eq(self.inputa[4:] - self.inputb[4:] - self._psw.H), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), self._psw.V.eq(self.result[7] != self._psw.C), ] if self.verification is Operation.SBC: r = Signal(8) f = Signal(9) h = Signal(5) m.d.comb += [ r.eq(self.inputa.as_signed() - self.inputb.as_signed() - self.PSW.C), f.eq(self.inputa - self.inputb - self.PSW.C), h.eq(self.inputa[:4] - self.inputb[:4] - self.PSW.C), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self.result == f[:8]), Assert(self._psw.N == f[7]), Assert(self._psw.V == (f[7] ^ f[8])), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == h[4]), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(f[:8].bool())), Assert(self._psw.C == f[8]), ] with m.Case(Operation.CMP): full = Cat(self.result, self._psw.C) m.d.comb += [ full.eq(self.inputa.as_signed() - self.inputb.as_signed()), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), self._psw.V.eq(self.result[7] != self._psw.C), ] if self.verification is Operation.CMP: r = Signal(9) m.d.comb += r.eq(self.inputa.as_signed() - self.inputb.as_signed()) with m.If(~Initial()): m.d.comb += [ Assert(self.result == r[:8]), Assert(self._psw.N == r[7]), Assert(self._psw.V == (r[7] ^ r[8])), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r[:8].bool())), Assert(self._psw.C == r[8]), ] with m.Case(Operation.AND): m.d.comb += [ self.result.eq(self.inputa & self.inputb), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.AND: r = Signal(8) m.d.comb += r.eq(self.inputa & self.inputb) with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == r[7]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.OOR): m.d.comb += [ self.result.eq(self.inputa | self.inputb), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.OOR: r = Signal(8) m.d.comb += [ r.eq(self.inputa | self.inputb), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == r[7]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.EOR): m.d.comb += [ self.result.eq(self.inputa ^ self.inputb), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.EOR: r = Signal(8) m.d.comb += [ r.eq(self.inputa ^ self.inputb), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == r[7]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.INC): m.d.comb += [ self.result.eq(self.inputa + 1), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.INC: r = Signal(8) m.d.comb += [ r.eq(self.inputa + 1), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == r[7]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.DEC): m.d.comb += [ self.result.eq(self.inputa - 1), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.DEC: r = Signal(8) m.d.comb += [ r.eq(self.inputa - 1), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == r[7]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.ASL): m.d.comb += [ Cat(self.result, self._psw.C).eq(Cat(Const(0), self.inputa)), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.ASL: r = Signal(8) m.d.comb += [ r.eq(self.inputa * 2), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == self.inputa[6]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.inputa[7]), ] with m.Case(Operation.LSR): m.d.comb += [ Cat(self._psw.C, self.result).eq(Cat(self.inputa, Const(0))), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.LSR: r = Signal(8) m.d.comb += [ r.eq(self.inputa // 2), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == 0), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.inputa[0]), ] with m.Case(Operation.ROL): m.d.comb += [ Cat(self.result, self._psw.C).eq(Cat(self.PSW.C, self.inputa)), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.ROL: r = Signal(8) m.d.comb += [ r.eq(self.inputa * 2 + self.PSW.C), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == self.inputa[6]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.inputa[7]), ] with m.Case(Operation.ROR): m.d.comb += [ Cat(self._psw.C, self.result).eq(Cat(self.inputa, self.PSW.C)), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.ROR: r = Signal(8) m.d.comb += [ r.eq(self.inputa // 2 + Cat(Signal(7), self.PSW.C)), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == self.PSW.C), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.inputa[0]), ] with m.Case(Operation.XCN): m.d.comb += [ self.result.eq(Cat(self.inputa[4:], self.inputa[:4])), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.XCN: r = Signal(8) m.d.comb += [ r.eq(self.inputa * 16 + self.inputa // 16), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == self.inputa[3]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(self.inputa.bool())), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.DAA): temp = Signal().like(self.inputa) with m.If(self.PSW.C | (self.inputa > 0x99)): m.d.comb += self._psw.C.eq(1) m.d.comb += temp.eq(self.inputa + 0x60) with m.Else(): m.d.comb += temp.eq(self.inputa) with m.If(self.PSW.H | (temp[:4] > 0x09)): m.d.comb += self.result.eq(temp + 0x06) m.d.comb += [ self._psw.N.eq(self.result & 0x80), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.DAA: with m.If(~Initial()): m.d.comb += [Assert(False)] with m.Case(Operation.DAS): temp = Signal().like(self.inputa) with m.If(~self.PSW.C | (self.inputa > 0x99)): m.d.comb += self._psw.C.eq(0) m.d.comb += temp.eq(self.inputa - 0x60) with m.Else(): m.d.comb += temp.eq(self.inputa) with m.If(~self.PSW.H | (temp[:4] > 0x09)): m.d.comb += self.result.eq(temp - 0x06) m.d.comb += [ self._psw.N.eq(self.result & 0x80), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.DAS: with m.If(~Initial()): m.d.comb += [Assert(False)] # could be optimized with shift to right with m.Case(Operation.MUL): with m.Switch(self.count): for i in range(0, 8): with m.Case(i): prod = self.inputa * self.inputb[i] if i == 0: prod = Cat(prod[0:7], ~prod[7], Const(1)) elif i == 7: prod = Cat(~prod[0:7], prod[7], Const(1)) else: prod = Cat(prod[0:7], ~prod[7]) m.d.sync += self.partial.eq(self.partial + (prod << i)) m.d.sync += self.count.eq(i + 1) with m.Case(8): m.d.sync += self.partial_hi.eq(self.partial_lo) m.d.sync += self.count.eq(9) m.d.comb += [ self.result.eq(self.partial_hi), self._psw.N.eq(self.partial_hi.as_signed() < 0), self._psw.Z.eq(self.partial_hi == 0), ] with m.Case(9): m.d.sync += self.partial.eq(0) m.d.sync += self.count.eq(0) m.d.comb += [ self.result.eq(self.partial_hi), ] if self.verification is Operation.MUL: r = Signal(16) m.d.comb += [ r.eq(self.inputa.as_signed() * self.inputb.as_signed()), Cover(self.count == 9), ] with m.If(self.count == 9): m.d.comb += [ Assert(Past(self.result) == r[8:16]), Assert(self.result == r[0:8]), Assert(self._psw.N == r[15]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r[8:16].bool())), Assert(self._psw.C == self.PSW.C), ] with m.If(~Initial() & (self.count == 0)): m.d.comb += [ Assert(self.partial == 0), Assert((Past(self.count) == 0) | (Past(self.count) == 9)), ] with m.If(~Initial() & (self.count != 0)): m.d.comb += [ Assert(self.count == Past(self.count) + 1), Assume(self.inputa == Past(self.inputa)), Assume(self.inputb == Past(self.inputb)), ] with m.Case(Operation.DIV): over = Signal(reset=0) with m.Switch(self.count): with m.Case(0): m.d.sync += self.partial_hi.eq(self.inputa) # Y m.d.sync += self.count.eq(1) with m.Case(1): m.d.sync += self.partial_lo.eq(self.inputa) # A m.d.sync += self.count.eq(2) m.d.comb += self._psw.H.eq( Mux(self.partial_hi[0:4] >= self.inputb[0:4], 1, 0)) for i in range(2, 11): with m.Case(i): tmp1_w = Cat(self.partial << 1, over) tmp1_x = Signal(17) tmp1_y = Signal(17) tmp1_z = Signal(17) tmp2 = self.inputb << 9 m.d.comb += tmp1_x.eq(tmp1_w) with m.If(tmp1_w & 0x20000): m.d.comb += tmp1_x.eq((tmp1_w & 0x1FFFF) | 1) m.d.comb += tmp1_y.eq(tmp1_x) with m.If(tmp1_x >= tmp2): m.d.comb += tmp1_y.eq(tmp1_x ^ 1) m.d.comb += tmp1_z.eq(tmp1_y) with m.If(tmp1_y & 1): m.d.comb += tmp1_z.eq((tmp1_y - tmp2) & 0x1FFFF) m.d.sync += Cat(self.partial, over).eq(tmp1_z) m.d.sync += self.count.eq(i + 1) with m.Case(11): m.d.sync += self.count.eq(12) m.d.comb += [ self.result.eq( (Cat(self.partial, over) >> 9)), # Y % ] with m.Case(12): m.d.sync += self.partial.eq(0) m.d.sync += over.eq(0) m.d.sync += self.count.eq(0) m.d.comb += [ self.result.eq(self.partial_lo), # A / self._psw.N.eq(self.partial_lo.as_signed() < 0), self._psw.V.eq(over), self._psw.Z.eq(self.partial_lo == 0), ] if self.verification is Operation.DIV: m.d.comb += [ Cover(self.count == 12), ] with m.If(self.count == 12): m.d.comb += [Assert(False)] return m
if __name__ == "__main__": parser = main_parser() parser.add_argument("--oper") args = parser.parse_args() oper: Optional[Operation] = None if args.oper is not None: oper = Operation[args.oper] m = Module() m.submodules.alu = alu = ALU_big(oper) if oper is not None: m.d.comb += Assume(~ResetSignal()) m.d.comb += Assume(alu.oper == oper) main_runner(parser, args, m, ports=alu.ports()) else: sim = Simulator(m) def process(): yield yield alu.inputa.eq(0x12) yield alu.inputb.eq(0x34) yield alu.oper.eq(Operation.ADC) yield yield yield alu.inputa.eq(0x7F) yield alu.inputb.eq(0x7F)
def elaborate(self, platform: str): #-------- m = Module() #add_clk_domain(m, self.bus().clk) #add_clk_from_domain(m, self.bus.clk()) #-------- #-------- # Local variables bus = self.bus() loc = Blank() loc.arr = Array([Signal(bus.shape_t()) for _ in range(bus.SIZE())]) loc.PTR_WIDTH = width_from_arg(bus.SIZE()) loc.rd = Signal(loc.PTR_WIDTH) loc.wr = Signal(loc.PTR_WIDTH) loc.RD_PLUS_1 = loc.rd + 0x1 loc.WR_PLUS_1 = loc.wr + 0x1 loc.incr_rd = Signal(loc.PTR_WIDTH) loc.incr_wr = Signal(loc.PTR_WIDTH) loc.next_empty = Signal() loc.next_full = Signal() loc.next_rd = Signal(loc.PTR_WIDTH) loc.next_wr = Signal(loc.PTR_WIDTH) loc.rst = ResetSignal("sync") #loc.curr_en_cat = Signal(2) if self.FORMAL(): loc.formal = Blank() loc.formal.last_rd_val = Signal(bus.shape_t()) loc.formal.test_wr = Signal(loc.PTR_WIDTH) #loc.formal.empty = Signal() #loc.formal.full = Signal() loc.formal.wd_cnt = Signal(bus.shape_t(), reset=0xa0) #-------- #-------- if self.FORMAL(): m.d.sync \ += [ loc.formal.last_rd_val.eq(loc.arr[loc.rd]), loc.formal.wd_cnt.eq(loc.formal.wd_cnt - 0x10) ] m.d.comb \ += [ loc.formal.test_wr.eq((loc.wr + 0x1) % bus.SIZE()), ] #-------- #-------- # Combinational logic m.d.comb \ += [ loc.incr_rd.eq(Mux(loc.RD_PLUS_1 < bus.SIZE(), (loc.rd + 0x1), 0x0)), loc.incr_wr.eq(Mux(loc.WR_PLUS_1 < bus.SIZE(), (loc.wr + 0x1), 0x0)), loc.next_empty.eq(loc.next_wr == loc.next_rd), #loc.next_full.eq((loc.next_wr + 0x1) == loc.next_rd), #loc.curr_en_cat.eq(Cat(bus.rd_en, bus.wr_en)), ] with m.If(bus.rd_en & (~bus.empty)): m.d.comb += loc.next_rd.eq(loc.incr_rd) with m.Else(): m.d.comb += loc.next_rd.eq(loc.rd) with m.If(bus.wr_en & (~bus.full)): m.d.comb \ += [ loc.next_wr.eq(loc.incr_wr), loc.next_full.eq((loc.incr_wr + 0x1) == loc.next_rd), ] with m.Else(): m.d.comb \ += [ loc.next_wr.eq(loc.wr), loc.next_full.eq(loc.incr_wr == loc.next_rd), ] #-------- #-------- # Clocked behavioral code with m.If(loc.rst): #for elem in loc.arr: # m.d.sync += elem.eq(bus.shape_t()()) m.d.sync \ += [ loc.rd.eq(0x0), loc.wr.eq(0x0), #bus.rd_data.eq(bus.shape_t()()), bus.empty.eq(0b1), bus.full.eq(0b0), ] with m.Else(): # If(~loc.rst): #-------- m.d.sync \ += [ bus.empty.eq(loc.next_empty), bus.full.eq(loc.next_full), loc.rd.eq(loc.next_rd), loc.wr.eq(loc.next_wr), ] with m.If(bus.rd_en & (~bus.empty)): m.d.sync += bus.rd_data.eq(loc.arr[loc.rd]) with m.If(bus.wr_en & (~bus.full)): m.d.sync += loc.arr[loc.wr].eq(bus.wr_data) #-------- #-------- if self.FORMAL(): with m.If(Fell(loc.rst)): m.d.sync \ += [ Assert(loc.rd == 0x0), Assert(loc.wr == 0x0), Assert(bus.empty == 0b1), Assert(bus.full == 0b0), ] with m.Else(): # If(~Fell(loc.rst)): m.d.sync \ += [ Assert(bus.empty == Past(loc.next_empty)), Assert(bus.full == Past(loc.next_full)), Assert(loc.rd == Past(loc.next_rd)), Assert(loc.wr == Past(loc.next_wr)), ] with m.If(Past(bus.rd_en)): with m.If(Past(bus.empty)): m.d.sync \ += [ #Assert(Stable(bus.empty)), Assert(Stable(loc.rd)), ] with m.Else(): # If(~Past(bus.empty)): #with m.If(~Past(bus.wr_en)): m.d.sync \ += [ Assert(bus.rd_data == loc.arr[Past(loc.rd)]) ] with m.If(Past(bus.wr_en)): with m.If(Past(bus.full)): m.d.sync \ += [ Assert(Stable(loc.wr)), ] #with m.Else(): # If(~Past(bus.full)): # m.d.sync \ # += [ # Assert(Past(bus.wr_data)) # ] with m.Switch(Cat(bus.empty, bus.full)): with m.Case(0b00): m.d.sync \ += [ Assume(loc.wr != loc.rd), Assume(loc.formal.test_wr != loc.rd), ] with m.Case(0b01): m.d.sync \ += [ Assert(loc.wr == loc.rd) ] with m.Case(0b10): m.d.sync \ += [ Assert(loc.formal.test_wr == loc.rd), ] m.d.sync \ += [ Assert(~(bus.empty & bus.full)), #Assume(~Stable(bus.wr_data)), #Assume(bus.wr_data == loc.formal.wd_cnt), ] #-------- #-------- #-------- return m
instr: Optional[Instruction] = None if args.instr is not None: instr = getattr(modules["instruction.implemented"], args.instr.split(".")[0]) instr = getattr(instr, args.instr.split(".")[1]) if instr not in implemented.implemented: raise AttributeError() m = Module() m.submodules.core = core = Core(instr) if instr is not None: time = Signal(6, reset_less=True) m.d.sync += time.eq(time + 1) with m.If(Initial()): m.d.sync += Assume(ResetSignal()) with m.Else(): m.d.sync += Assume(~ResetSignal()) # A time slot delayed because PC and addr need to sync with m.If(time == 2): m.d.sync += Assume(~core.snapshot.taken) with m.If(time == 3): m.d.sync += Cover(core.snapshot.taken) m.d.sync += Assume(core.snapshot.taken) m.d.sync += Cover(Fell(core.snapshot.taken)) main_runner( parser, args, m, ports=core.ports() + [ClockSignal(), ResetSignal()] )
rst = Signal() ph1clk = ClockSignal("ph1") ph1.rst = rst if verification is not None: # Cycle counter cycle2 = Signal(6, reset_less=True) m.d.ph1 += cycle2.eq(cycle2 + 1) # Force a reset # m.d.comb += Assume(rst == (cycle2 < 8)) with m.If(cycle2 == 20): m.d.ph1 += Cover(core.formalData.snapshot_taken & core.end_instr_flag) m.d.ph1 += Assume(core.formalData.snapshot_taken & core.end_instr_flag) # Verify reset does what it's supposed to with m.If(Past(rst, 4) & ~Past(rst, 3) & ~Past(rst, 2) & ~Past(rst)): m.d.ph1 += Assert(Past(core.Addr, 2) == 0xFFFE) m.d.ph1 += Assert(Past(core.Addr) == 0xFFFF) m.d.ph1 += Assert(core.Addr[8:] == Past(core.Din, 2)) m.d.ph1 += Assert(core.Addr[:8] == Past(core.Din)) m.d.ph1 += Assert(core.Addr == core.pc) main_runner(parser, args, m, ports=core.ports() + [ph1clk, rst]) else: # Fake memory mem = { 0xFFFE: 0x12,
def formal(cls) -> Tuple[Module, List[Signal]]: """Formal verification for the register card.""" m = Module() ph1 = ClockDomain("ph1") ph2 = ClockDomain("ph2") regs = RegCard() m.domains += [ph1, ph2] m.submodules += regs # Generate the ph1 and ph2 clocks. cycle_count = Signal(8, reset=0, reset_less=True) phase_count = Signal(3, reset=0, reset_less=True) m.d.sync += phase_count.eq(phase_count + 1) with m.Switch(phase_count): with m.Case(0, 1, 2): m.d.comb += ph1.clk.eq(1) with m.Default(): m.d.comb += ph1.clk.eq(0) with m.Switch(phase_count): with m.Case(1, 4): m.d.comb += ph2.clk.eq(0) with m.Default(): m.d.comb += ph2.clk.eq(1) with m.If(phase_count == 5): m.d.sync += phase_count.eq(0) m.d.sync += cycle_count.eq(cycle_count + 1) # This is how we expect to use the card. with m.If(phase_count > 0): m.d.comb += [ Assume(Stable(regs.reg_x)), Assume(Stable(regs.reg_y)), Assume(Stable(regs.reg_z)), Assume(Stable(regs.reg_page)), Assume(Stable(regs.reg_to_x)), Assume(Stable(regs.reg_to_y)), Assume(Stable(regs.data_z)), ] # Figure out how to get to the point where X and Y are nonzero and different. m.d.comb += Cover((regs.data_x != 0) & (regs.data_y != 0) & (regs.data_x != regs.data_y)) # X and Y buses should not change during a cycle, except for the first phase with m.Switch(phase_count): with m.Case(2, 3, 4, 5): with m.If(regs.data_x != 0): m.d.comb += Assert(Stable(regs.data_x)) with m.If(regs.data_y != 0): m.d.comb += Assert(Stable(regs.data_y)) # X and Y buses should be zero if there is no data transfer. with m.If(regs.reg_to_x == 0): m.d.comb += Assert(regs.data_x == 0) with m.If(regs.reg_to_y == 0): m.d.comb += Assert(regs.data_y == 0) with m.If(phase_count > 0): # X and Y buses should be zero if we read from register 0. with m.If(regs.reg_to_x & (regs.reg_x == 0)): m.d.comb += Assert(regs.data_x == 0) with m.If(regs.reg_to_y & (regs.reg_y == 0)): m.d.comb += Assert(regs.data_y == 0) write_pulse = Signal() m.d.comb += write_pulse.eq(phase_count != 4) # On write, the data should have been written to both banks. past_mem_addr = Signal(6) m.d.comb += past_mem_addr[:5].eq(Past(regs.reg_z)) m.d.comb += past_mem_addr[5].eq(Past(regs.reg_page)) past_z = Past(regs.data_z) with m.If(Rose(write_pulse)): m.d.comb += Assert(regs._x_bank._mem[past_mem_addr] == past_z) m.d.comb += Assert(regs._y_bank._mem[past_mem_addr] == past_z) # Pick an register, any register, except 0. We assert that unless # it is written, its data will not change. check_addr = AnyConst(5) check_page = AnyConst(1) saved_data = Signal(32) stored_x_data = Signal(32) stored_y_data = Signal(32) write_pulse_domain = ClockDomain("write_pulse_domain", local=True) m.domains.write_pulse_domain = write_pulse_domain write_pulse_domain.clk = write_pulse mem_addr = Signal(6) m.d.comb += Assume(check_addr != 0) m.d.comb += [ mem_addr[:5].eq(check_addr), mem_addr[5].eq(check_page), stored_x_data.eq(regs._x_bank._mem[mem_addr]), stored_y_data.eq(regs._y_bank._mem[mem_addr]), ] with m.If((regs.reg_z == check_addr) & (regs.reg_page == check_page)): m.d.write_pulse_domain += saved_data.eq(regs.data_z) with m.If(Initial()): m.d.comb += Assume(saved_data == stored_x_data) m.d.comb += Assume(stored_x_data == stored_y_data) with m.Else(): m.d.comb += Assert(saved_data == stored_x_data) m.d.comb += Assert(saved_data == stored_y_data) return m, [regs.data_z, regs.reg_to_x, regs.reg_to_y, regs.reg_x, regs.reg_y, regs.reg_z, regs.reg_page, ph1.clk, ph2.clk, saved_data, stored_x_data, stored_y_data]