Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I apparently don't know how to define a ClockDomain #273

Closed
RobertBaruch opened this issue Nov 19, 2019 · 3 comments
Closed

I apparently don't know how to define a ClockDomain #273

RobertBaruch opened this issue Nov 19, 2019 · 3 comments
Labels

Comments

@RobertBaruch
Copy link

Here's a file that should fail formal verification:

from nmigen import *
from nmigen.cli import main
from nmigen.asserts import *

if __name__ == "__main__":
    m = Module()

    ph1 = ClockDomain("ph1")
    m.domains.ph1 = ph1

    # Cycle counter
    cycles = Signal(6, reset=0, reset_less=True)
    m.d.ph1 += cycles.eq(cycles + 1)

    with m.If(cycles == 12):
        m.d.ph1 += Assert(cycles == 9)

    main(m, ports=[cycles])
[tasks]
bmc

[options]
bmc: mode bmc
depth 55
multiclock on

[engines]
smtbmc boolector

[script]
read_ilang toplevel.il
prep -top top

[files]
toplevel.il

And hey, BMC passes! But it shouldn't because there's a bad assertion in there that should fail every single time.

If I delete my ClockDomain and just replace m.d.ph1 with m.d.sync, BMC properly fails. So how do I define a ClockDomain correctly?

@whitequark
Copy link
Contributor

If I delete my ClockDomain and just replace m.d.ph1 with m.d.sync, BMC properly fails. So how do I define a ClockDomain correctly?

Aha. What you're hitting there is that verilog.convert / rtlil.convert (and therefore nmigen.cli.main) automatically create all missing clock domains with the default settings (i.e. they call ClockDomain(name)). When they do that, they also add the clock and reset to the list of ports.

So writing this:

m = Module()

# Cycle counter
cycles = Signal(6, reset=0, reset_less=True)
m.d.ph1 += cycles.eq(cycles + 1)

with m.If(cycles == 12):
    m.d.ph1 += Assert(cycles == 9)

main(m, ports=[cycles])

is equivalent to writing this:

m = Module()

ph1 = ClockDomain("ph1")
m.domains.ph1 = ph1

# Cycle counter
cycles = Signal(6, reset=0, reset_less=True)
m.d.ph1 += cycles.eq(cycles + 1)

with m.If(cycles == 12):
    m.d.ph1 += Assert(cycles == 9)

main(m, ports=[ph1.clk, ph1.rst, cycles])

(It works the same regardless of the domain name until you get to nmigen.build. nmigen.build does treat sync specially, in the sense that it is connected to the default platform clock when it is automatically created.)

@RobertBaruch
Copy link
Author

Ah, so the absence of ph1.clk and ph1.rst in the ports passed to main was causing BMC to never hit the bad assert, because there was no clock for BMC to manipulate. Thus, it never encountered cycles==12. When I added ph1.clk and ph1.rst to the list of ports, BMC properly hits the assert and fails.

@whitequark
Copy link
Contributor

Indeed! Right now the SymbiYosys flow is not very ergonomic unfortunately. I'll make sure to improve this but this is more of a medium-term plan.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants