doc: more examples and comments
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Sat, 10 Mar 2012 18:38:39 +0000 (19:38 +0100)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Sat, 10 Mar 2012 18:38:39 +0000 (19:38 +0100)
doc/index.rst
examples/basic2_sim.py [new file with mode: 0644]
examples/basic_sim.py
examples/fir.py
examples/memory_sim.py
examples/wb_initiator.py

index cbd0bd59fad6e1aaa61e30d288d59d8adebaf10a..7b2060fa1ec63dc830aed2f9440eb3ae816cdb8b 100644 (file)
@@ -567,29 +567,30 @@ Migen comes with a ``migen.sim.generic.TopLevel`` object that implements the abo
 
 The main parameters of its constructor are the output VCD file (default: ``None``) and the levels of hierarchy that must be present in the VCD (default: 1).
 
-Basic simulation example
-========================
-::
-
-       from migen.fhdl.structure import *
-       from migen.sim.generic import Simulator
-       from migen.sim.icarus import Runner
-
-       class Counter:
-               def __init__(self):
-                       self.count = Signal(BV(4))
-               
-               def do_simulation(self, s):
-                       print("Count: " + str(s.rd(self.count)))
-               
-               def get_fragment(self):
-                       sync = [self.count.eq(self.count + 1)]
-                       sim = [self.do_simulation]
-                       return Fragment(sync=sync, sim=sim)
-
-       def main():
-               dut = Counter()
-               sim = Simulator(dut.get_fragment(), Runner())
-               sim.run(20)
-
-       main()
+Simulation examples
+*******************
+
+Most basic
+==========
+.. include:: ../examples/basic_sim.py
+   :code: python
+
+A few more features
+===================
+.. include:: ../examples/basic2_sim.py
+   :code: python
+
+Memory access
+=============
+.. include:: ../examples/memory_sim.py
+   :code: python
+
+A FIR filter
+============
+.. include:: ../examples/fir.py
+   :code: python
+   
+Wishbone
+========
+.. include:: ../examples/wb_initiator.py
+   :code: python
diff --git a/examples/basic2_sim.py b/examples/basic2_sim.py
new file mode 100644 (file)
index 0000000..f7bb0d0
--- /dev/null
@@ -0,0 +1,49 @@
+from migen.fhdl.structure import *
+from migen.sim.generic import Simulator, TopLevel
+from migen.sim.icarus import Runner
+
+# A slightly improved counter.
+# Has a clock enable (CE) signal, counts on more bits
+# and resets with a negative number.
+class Counter:
+       def __init__(self):
+               self.ce = Signal()
+               # Demonstrate negative numbers and signals larger than 32 bits.
+               self.count = Signal(BV(37, True), reset=-5)
+       
+       def do_simulation(self, s):
+               # Only assert CE every second cycle.
+               # => each counter value is held for two cycles.
+               if s.cycle_counter % 2:
+                       s.wr(self.ce, 0) # This is how you write to a signal.
+               else:
+                       s.wr(self.ce, 1)
+               print("Cycle: " + str(s.cycle_counter) + " Count: " + \
+                       str(s.rd(self.count)))
+       # Set the "initialize" property on our simulation function.
+       # The simulator will call it during the reset cycle,
+       # with s.cycle_counter == -1.
+       do_simulation.initialize = True
+       
+       # Output is:
+       # Cycle: -1 Count: 0
+       # Cycle: 0 Count: -5
+       # Cycle: 1 Count: -5
+       # Cycle: 2 Count: -4
+       # Cycle: 3 Count: -4
+       # Cycle: 4 Count: -3
+       # ...
+       
+       def get_fragment(self):
+               sync = [If(self.ce, self.count.eq(self.count + 1))]
+               sim = [self.do_simulation]
+               return Fragment(sync=sync, sim=sim)
+
+def main():
+       dut = Counter()
+       # Instantiating the generic top-level ourselves lets us
+       # specify a VCD output file.
+       sim = Simulator(dut.get_fragment(), Runner(), TopLevel("my.vcd"))
+       sim.run(20)
+
+main()
index 035440ca322dcf22ff0c7311d5c0dede2c7e9ebf..900fceded33390f583ce51425e426c547f38fa0b 100644 (file)
@@ -1,28 +1,38 @@
 from migen.fhdl.structure import *
-from migen.sim.generic import Simulator, TopLevel
+from migen.sim.generic import Simulator
 from migen.sim.icarus import Runner
 
+# Our simple counter, which increments at every cycle
+# and prints its current value in simulation.
 class Counter:
        def __init__(self):
-               self.ce = Signal()
-               self.count = Signal(BV(37, True), reset=-5)
+               self.count = Signal(BV(4))
        
+       # This function will be called at every cycle.
        def do_simulation(self, s):
-               if s.cycle_counter % 2:
-                       s.wr(self.ce, 0)
-               else:
-                       s.wr(self.ce, 1)
-               print("Cycle: " + str(s.cycle_counter) + " Count: " + str(s.rd(self.count)))
-       do_simulation.initialize = True
+               # Simply read the count signal and print it.
+               # The output is:
+               # Count: 0 
+               # Count: 1
+               # Count: 2
+               # ...
+               print("Count: " + str(s.rd(self.count)))
        
        def get_fragment(self):
-               sync = [If(self.ce, self.count.eq(self.count + 1))]
+               # At each cycle, increase the value of the count signal.
+               # We do it with convertible/synthesizable FHDL code.
+               sync = [self.count.eq(self.count + 1)]
+               # List our simulation function in the fragment.
                sim = [self.do_simulation]
                return Fragment(sync=sync, sim=sim)
 
 def main():
        dut = Counter()
-       sim = Simulator(dut.get_fragment(), Runner(), TopLevel("my.vcd"))
+       # Use the Icarus Verilog runner.
+       # We do not specify a top-level object, and use the default.
+       sim = Simulator(dut.get_fragment(), Runner())
+       # Since we do not use sim.interrupt, limit the simulation
+       # to some number of cycles.
        sim.run(20)
 
 main()
index b5d366e8d8b6a921827ae9a63029e8a095df0dc0..3e6490a51f925283a0c87bafe20b1b4802c6b414 100644 (file)
@@ -1,5 +1,5 @@
-from scipy import signal
 from math import cos, pi
+from scipy import signal
 
 from migen.fhdl.structure import *
 from migen.fhdl import verilog
@@ -8,6 +8,7 @@ from migen.fhdl import autofragment
 from migen.sim.generic import Simulator
 from migen.sim.icarus import Runner
 
+# A synthesizable FIR filter.
 class FIR:
        def __init__(self, coef, wsize=16):
                self.coef = coef
@@ -31,6 +32,8 @@ class FIR:
                comb = [self.o.eq(sum_full[self.wsize-1:])]
                return Fragment(comb, sync)
 
+# A test bench for our FIR filter.
+# Generates a sine wave at the input and records the output.
 class TB:
        def __init__(self, fir, frequency):
                self.fir = fir
@@ -49,12 +52,17 @@ class TB:
                return Fragment(sim=[self.do_simulation])
 
 def main():
+       # Compute filter coefficients with SciPy.
        coef = signal.remez(80, [0, 0.1, 0.1, 0.5], [1, 0])
        fir = FIR(coef)
        tb = TB(fir, 0.3)
+       # Combine the FIR filter with its test bench.
        fragment = autofragment.from_local()
        sim = Simulator(fragment, Runner())
        sim.run(200)
+       # Print data from the input and output waveforms.
+       # When matplotlib works easily with Python 3, we could
+       # display them graphically here.
        print(tb.inputs)
        print(tb.outputs)
 
index 571bad2ebb86a71ecf325c4232624d6d0140a199..765b0de82f8aca4f10876d5b38d67ae4c4503767 100644 (file)
@@ -7,11 +7,20 @@ class Mem:
                self.a = Signal(BV(12))
                self.d = Signal(BV(16))
                p = MemoryPort(self.a, self.d)
+               # Initialize the beginning of the memory with integers
+               # from 0 to 19.
                self.mem = Memory(16, 2**12, p, init=list(range(20)))
        
        def do_simulation(self, s):
+               # Read the memory. Use the cycle counter as address.
                value = s.rd(self.mem, s.cycle_counter)
+               # Print the result. Output is:
+               # 0
+               # 1
+               # 2
+               # ...
                print(value)
+               # Demonstrate how to interrupt the simulator.
                if value == 10:
                        s.interrupt = True
        
@@ -21,6 +30,7 @@ class Mem:
 def main():
        dut = Mem()
        sim = Simulator(dut.get_fragment(), Runner())
+       # No need for a cycle limit here, we use sim.interrupt instead.
        sim.run()
 
 main()
index 83df271c769654ad96e98c7066770ddebf7301d9..04caecd475033b22d4afb6aced952ca0ebeccbea 100644 (file)
@@ -7,14 +7,21 @@ from migen.bus import wishbone
 from migen.sim.generic import Simulator
 from migen.sim.icarus import Runner
 
+# Our bus master.
+# Python generators let us program bus transactions in an elegant sequential style.
 def my_generator():
        prng = Random(92837)
+
+       # Write to the first addresses.
        for x in range(10):
                t = TWrite(x, 2*x)
                yield t
                print("Wrote in " + str(t.latency) + " cycle(s)")
+               # Insert some dead cycles to simulate bus inactivity.
                for delay in range(prng.randrange(0, 3)):
                        yield None
+
+       # Read from the first addresses.
        for x in range(10):
                t = TRead(x)
                yield t
@@ -22,6 +29,9 @@ def my_generator():
                for delay in range(prng.randrange(0, 3)):
                        yield None
 
+# Our bus slave.
+# All transactions complete with a random delay.
+# Reads return address + 4. Writes are simply acknowledged.
 class MyPeripheral:
        def __init__(self):
                self.bus = wishbone.Interface()
@@ -29,7 +39,7 @@ class MyPeripheral:
                self.prng = Random(763627)
 
        def do_simulation(self, s):
-               # Only authorize acks on certain cycles to simulate variable latency
+               # Only authorize acks on certain cycles to simulate variable latency.
                s.wr(self.ack_en, self.prng.randrange(0, 2))
 
        def get_fragment(self):
@@ -40,10 +50,18 @@ class MyPeripheral:
                return Fragment(comb, sim=[self.do_simulation])
 
 def main():
+       # The "wishbone.Initiator" library component runs our generator
+       # and manipulates the bus signals accordingly.
        master = wishbone.Initiator(my_generator())
+       # Our slave.
        slave = MyPeripheral()
+       # The "wishbone.Tap" library component examines the bus at the slave port
+       # and displays the transactions on the console (<TRead...>/<TWrite...>).
        tap = wishbone.Tap(slave.bus)
+       # Connect the master to the slave.
        intercon = wishbone.InterconnectPointToPoint(master.bus, slave.bus)
+       # A small extra simulation function to terminate the process when
+       # the initiator is done (i.e. our generator is exhausted).
        def end_simulation(s):
                s.interrupt = master.done
        fragment = autofragment.from_local() + Fragment(sim=[end_simulation])
@@ -51,3 +69,27 @@ def main():
        sim.run()
 
 main()
+
+# Output:
+# <TWrite adr:0x0 dat:0x0>
+# Wrote in 0 cycle(s)
+# <TWrite adr:0x1 dat:0x2>
+# Wrote in 0 cycle(s)
+# <TWrite adr:0x2 dat:0x4>
+# Wrote in 0 cycle(s)
+# <TWrite adr:0x3 dat:0x6>
+# Wrote in 1 cycle(s)
+# <TWrite adr:0x4 dat:0x8>
+# Wrote in 1 cycle(s)
+# <TWrite adr:0x5 dat:0xa>
+# Wrote in 2 cycle(s)
+# ...
+# <TRead adr:0x0 dat:0x4>
+# Read 4 in 2 cycle(s)
+# <TRead adr:0x1 dat:0x5>
+# Read 5 in 2 cycle(s)
+# <TRead adr:0x2 dat:0x6>
+# Read 6 in 1 cycle(s)
+# <TRead adr:0x3 dat:0x7>
+# Read 7 in 1 cycle(s)
+# ...