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

How to simulate with two clock domains? #236

Closed
nmigen-issue-migration opened this issue Sep 26, 2019 · 12 comments
Closed

How to simulate with two clock domains? #236

nmigen-issue-migration opened this issue Sep 26, 2019 · 12 comments
Labels

Comments

@nmigen-issue-migration
Copy link

Issue by RobertBaruch
Thursday Sep 26, 2019 at 02:12 GMT
Originally opened as m-labs/nmigen#236


What's the right pattern to use? Here is an example of what I've tried:

from nmigen import *
from nmigen.cli import main
from nmigen.asserts import *
from nmigen.back import pysim
from nmigen.hdl.ast import Tick


# module edgelord outputs the state of the clock without using
# the clock in combinatorial logic. This is a good thing in
# FPGAs, where the clock is a special signal that might get
# badly routed if it has to go through anything other than the
# clock inputs of flipflops.
#
# The reset signal MUST be held high for both edges,
# otherwise the clk_state will be inverted.
class Edgelord(Elaboratable):
    def __init__(self):
        self.clk_state = Signal()

    def elaborate(self, platform):
        pos = Signal()
        neg = Signal()
        rst = ResetSignal("pos")

        m = Module()

        # Verilog equivalent:
        #
        # assign clk_state = reset || !(pos ^ neg);
        m.d.comb += self.clk_state.eq(rst | ~(pos ^ neg))

        # Verilog equivalent:
        #
        # always @(posedge clk) begin
        #     if (reset) pos <= 0;
        #     else pos <= !(pos ^ clk_state);
        # end
        #
        # always @(negedge clk) begin
        #     if (reset) neg <= 0;
        #     else neg <= neg ^ clk_state;
        # end
        with m.If(rst):
            m.d.pos += pos.eq(0)
            m.d.neg += neg.eq(0)
        with m.Else():
            m.d.pos += pos.eq(~(pos ^ self.clk_state))
            m.d.neg += neg.eq(neg ^ self.clk_state)

        if platform == "formal":
            self.formal(m)

        return m

    def formal(self, m):
        cycle = Signal(8, reset_less=True)
        rst = ResetSignal("pos")
        clk = ClockSignal("pos")

        m.d.pos += cycle.eq(cycle + (cycle != 255))

        m.d.comb += Assume(rst == (cycle < 2))
        with m.If(rst == 0):
            m.d.comb += Assert(clk == self.clk_state)


if __name__ == "__main__":
    clk = Signal()
    rst = Signal()

    pos = ClockDomain()
    pos.clk = clk
    pos.rst = rst

    neg = ClockDomain(clk_edge="neg")
    neg.clk = clk
    neg.rst = rst

    edgelord = Edgelord()

    m = Module()
    m.domains.pos = pos
    m.domains.neg = neg
    m.submodules.edgelord = edgelord

    with pysim.Simulator(
            m,
            vcd_file=open("edgelord.vcd", "w"),
            gtkw_file=open("edgelord.gtkw", "w"),
            traces=[clk, rst, edgelord.clk_state]) as sim:
        sim.add_clock(1e-9, domain="pos")
        sim.add_clock(1e-9, domain="neg")

        #sim.add_clock(1e-9)


        def process():
            for i in range(0, 30):
                yield Tick(domain="pos")
                yield Tick(domain="neg")

        #sim.add_sync_process(process(), domain="pos")
        sim.add_process(process())
        sim.run()

    #main(m, ports=[clk, rst, edgelord.clk_state], platform="formal")

This resulted in:

Traceback (most recent call last):
  File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/mnt/f/mz80/mz80/core/edgelord.py", line 104, in <module>
    sim.run()
  File "/home/robertbaruch/.local/lib/python3.6/site-packages/nmigen/back/pysim.py", line 835, in run
    while self.step():
  File "/home/robertbaruch/.local/lib/python3.6/site-packages/nmigen/back/pysim.py", line 800, in step
    raise DeadlineError("Delta cycles exceeded process deadline; combinatorial loop?")
nmigen.back.pysim.DeadlineError: Delta cycles exceeded process deadline; combinatorial loop?
@nmigen-issue-migration
Copy link
Author

Comment by whitequark
Thursday Sep 26, 2019 at 02:16 GMT


This is currently broken for reasons that are not entirely clear. See #28. I plan to rewrite the simulator entirely, since it has a number of serious flaws that require changing core decisions, but this will take some time.

A possible workaround is to use EnableInserter to sequence the pos and neg actions, and then DomainRenamer to map them both to a single sync domain.

Another possible workaround is to use https://github.com/andresdemski/nmigen-cocotb. (I have not personally used it.) I will also take a good look at cocotb's user interface (see #228), so if later you want to get rid of cocotb in favor of Python-only simulation, it is likely that the migration would not be very painful.

@nmigen-issue-migration
Copy link
Author

Comment by whitequark
Saturday Oct 12, 2019 at 23:32 GMT


@RobertBaruch Have you been able to use one of these workarounds?

@nmigen-issue-migration
Copy link
Author

Comment by RobertBaruch
Sunday Oct 13, 2019 at 13:52 GMT


I haven't looked at it yet. I can try nmigen-cocotb.

@nmigen-issue-migration
Copy link
Author

Comment by RobertBaruch
Sunday Oct 13, 2019 at 16:15 GMT


Tried nmigen-cocotb, but apparently nothing is output. I get a sim_build directory with a vvp file. I did run vvp on that vvp file and got an output.vcd, which contained no signal changes. Maybe there's some documentation out of date -- the test runner doesn't seem to run the function annotated with @cocotb.test().

from nmigen import *
# from nmigen.cli import main
from nmigen.asserts import *
# from nmigen.back import pysim
# from nmigen.hdl.ast import Tick


# module edgelord outputs the state of the clock without using
# the clock in combinatorial logic. This is a good thing in
# FPGAs, where the clock is a special signal that might get
# badly routed if it has to go through anything other than the
# clock inputs of flipflops.
#
# The reset signal MUST be held high for both edges,
# otherwise the clk_state will be inverted.
class Edgelord(Elaboratable):
    def __init__(self):
        self.clk_state = Signal()
        self.unfunf = Signal()

    def elaborate(self, platform):
        pos = Signal()
        neg = Signal()
        rst = ResetSignal("pos")

        m = Module()

        # Verilog equivalent:
        #
        # assign clk_state = reset || !(pos ^ neg);
        m.d.comb += self.clk_state.eq(rst | ~(pos ^ neg))

        # Verilog equivalent:
        #
        # always @(posedge clk) begin
        #     if (reset) pos <= 0;
        #     else pos <= !(pos ^ clk_state);
        # end
        #
        # always @(negedge clk) begin
        #     if (reset) neg <= 0;
        #     else neg <= neg ^ clk_state;
        # end
        m.d.neg += self.unfunf.eq(~self.unfunf)
        with m.If(rst):
            m.d.pos += pos.eq(0)
            m.d.neg += neg.eq(0)
        with m.Else():
            m.d.pos += pos.eq(~(pos ^ self.clk_state))
            m.d.neg += neg.eq(neg ^ self.clk_state)

        return m


from nmigen_cocotb import run, get_current_module
import cocotb
from cocotb.triggers import Timer

def tick(dut):
    print("tick\n")
    dut.clk <= 0
    yield Timer(10, 'ns')
    dut.clk <= 1
    yield Timer(10, 'ns')

@cocotb.test()
def reset_test(dut):
    print("start reset_test\n")
    dut._log.info("Running test!")
    dut.rst <= 1
    tick(dut)
    tick(dut)
    dut.rst <= 0
    tick(dut)
    tick(dut)
    tick(dut)
    dut._log.info("Test complete!")

def test_module():
    clk = Signal()
    rst = Signal()

    pos = ClockDomain()
    pos.clk = clk
    pos.rst = rst

    neg = ClockDomain(clk_edge="neg")
    neg.clk = clk
    neg.rst = rst

    edgelord = Edgelord()

    m = Module()
    m.domains.pos = pos
    m.domains.neg = neg
    m.submodules.edgelord = edgelord

    print("Start run\n")
    run(m, get_current_module(), ports=[clk, rst, edgelord.clk_state], vcd_file='output.vcd')

if __name__ == "__main__":
    print("Running\n")
    test_module()
$ python3 edgelord.py cocotb -m test -v output.vcd

Running

Start run

top [(sig clk), (sig rst), (sig clk_state)]
iverilog -o /mnt/c/Users/rober/Documents/mz80/mz80/core/sim_build/top.vvp -D COCOTB_SIM=1 -s top -g2012 -s cocotb_waveform_module /tmp/tmpeot_rz96/nmigen_output.v
vvp -M /home/robertbaruch/.local/lib/python3.6/site-packages/cocotb_test/libs/icarus -m libvpi /mnt/c/Users/rober/Documents/mz80/mz80/core/sim_build/top.vvp
     -.--ns INFO     cocotb.gpi                         ../embed/gpi_embed.c:111  in embed_init_python               Did not detect Python virtual environment. Using system-wide Python interpreter.
     -.--ns INFO     cocotb.gpi                         ../gpi/GpiCommon.cpp:91   in gpi_print_registered_impl       VPI registered
     0.00ns INFO     cocotb                                      __init__.py:131  in _initialise_testbench           Running tests with Cocotb v1.2.0 from Unknown
     0.00ns INFO     cocotb                                      __init__.py:148  in _initialise_testbench           Seeding Python random module with 1570983084
     0.00ns INFO     cocotb.regression                         regression.py:210  in tear_down                       Passed 0 tests (0 skipped)
     0.00ns INFO     cocotb.regression                         regression.py:392  in _log_sim_summary                *************************************************************************************
                                                                                                                     **                                 ERRORS : 0                                      **
                                                                                                                     *************************************************************************************
                                                                                                                     **                               SIM TIME : 0.00 NS                                **
                                                                                                                     **                              REAL TIME : 0.00 S                                 **
                                                                                                                     **                        SIM / REAL TIME : 0.00 NS/S                              **
                                                                                                                     *************************************************************************************

     0.00ns INFO     cocotb.regression                         regression.py:219  in tear_down                       Shutting down...
$ vvp sim_build top.vvp

output.zip

@nmigen-issue-migration
Copy link
Author

Comment by RobertBaruch
Sunday Oct 13, 2019 at 16:20 GMT


Hmm, the coctb-nmigen example doesn't even output anything, so there must be some step missing.

@nmigen-issue-migration
Copy link
Author

Comment by RobertBaruch
Sunday Oct 13, 2019 at 16:43 GMT


Yeah, no.

$ vvp -M /home/robertbaruch/.local/lib/python3.6/site-packages/cocotb_test/libs/icarus -m libvpi /mnt/c/Users/rober/Documents/mz80/mz80/core/sim_build/top.vvp
     -.--ns INFO     cocotb.gpi                         ../embed/gpi_embed.c:111  in embed_init_python               Did not detect Python virtual environment. Using system-wide Python interpreter.
     -.--ns INFO     cocotb.gpi                         ../gpi/GpiCommon.cpp:91   in gpi_print_registered_impl       VPI registered
AttributeError: module 'cocotb' has no attribute 'loggpi'
Error in sys.excepthook:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/apport_python_hook.py", line 63, in apport_excepthook
    from apport.fileutils import likely_packaged, get_recent_crashes
  File "/usr/lib/python3/dist-packages/apport/__init__.py", line 5, in <module>
    from apport.report import Report
  File "/usr/lib/python3/dist-packages/apport/report.py", line 30, in <module>
    import apport.fileutils
  File "/usr/lib/python3/dist-packages/apport/fileutils.py", line 23, in <module>
    from apport.packaging_impl import impl as packaging
  File "/usr/lib/python3/dist-packages/apport/packaging_impl.py", line 23, in <module>
    import apt
  File "/usr/lib/python3/dist-packages/apt/__init__.py", line 23, in <module>
    import apt_pkg
ModuleNotFoundError: No module named 'apt_pkg'

Original exception was:
AttributeError: module 'cocotb' has no attribute 'loggpi'
Failed to to get simlog object
Segmentation fault (core dumped)

@nmigen-issue-migration
Copy link
Author

Comment by whitequark
Sunday Oct 13, 2019 at 16:45 GMT


What about my other suggestion?

@nmigen-issue-migration
Copy link
Author

Comment by RobertBaruch
Sunday Oct 13, 2019 at 16:49 GMT


Working on that now!

@nmigen-issue-migration
Copy link
Author

Comment by RobertBaruch
Sunday Oct 13, 2019 at 17:05 GMT


Sorry, I'm going to need more explicit instructions on what to do with EnableInserter and DomainRenamer.

@nmigen-issue-migration
Copy link
Author

Comment by whitequark
Sunday Oct 13, 2019 at 17:09 GMT


Something like:

m = Module()
phase = Signal()
m.d.sync += phase.eq(~phase)
design = EnableInserter({"pos":~phase,"neg":phase})(design)
design = DomainRenamer({"pos":"sync","neg":"sync"})(design)
m.submodules += design

@nmigen-issue-migration
Copy link
Author

Comment by RobertBaruch
Sunday Oct 13, 2019 at 17:28 GMT


Yes, that seems to have worked, thanks!

@nmigen-issue-migration
Copy link
Author

Comment by andresdemski
Thursday Oct 17, 2019 at 16:12 GMT


Hi, get_current_module function is not working currently in nmigen-cocotb. Try to use it with harcoded module.
Try this commit: https://github.com/andresdemski/nmigen-cocotb/tree/9e5d868587c859820147365a51d38f60e459cbb0

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

No branches or pull requests

1 participant