From fd986210f81171fb03c7a4b2072cdb3cf21dcff7 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 10 Sep 2015 21:44:14 -0700 Subject: [PATCH] simulator: support generators --- examples/sim/abstract_transactions_wb.py | 84 ------------------------ examples/sim/basic1.py | 31 ++++----- examples/sim/basic2.py | 19 +++--- examples/sim/dataflow.py | 47 ------------- examples/sim/fir.py | 31 ++++----- migen/sim.py | 44 ++++++++++--- 6 files changed, 73 insertions(+), 183 deletions(-) delete mode 100644 examples/sim/abstract_transactions_wb.py delete mode 100644 examples/sim/dataflow.py diff --git a/examples/sim/abstract_transactions_wb.py b/examples/sim/abstract_transactions_wb.py deleted file mode 100644 index bc22cc41..00000000 --- a/examples/sim/abstract_transactions_wb.py +++ /dev/null @@ -1,84 +0,0 @@ -from random import Random - -from migen.fhdl.std import * -from migen.bus.transactions import * -from migen.bus import wishbone -from migen.sim.generic import run_simulation - - -# 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 - print("Read " + str(t.data) + " in " + str(t.latency) + " cycle(s)") - for delay in range(prng.randrange(0, 3)): - yield None - - -# Our bus slave. -class MyModelWB(wishbone.TargetModel): - def __init__(self): - self.prng = Random(763627) - - def read(self, address): - return address + 4 - - def can_ack(self, bus): - # Simulate variable latency. - return self.prng.randrange(0, 2) - - -class TB(Module): - def __init__(self): - # The "wishbone.Initiator" library component runs our generator - # and manipulates the bus signals accordingly. - self.submodules.master = wishbone.Initiator(my_generator()) - # The "wishbone.Target" library component examines the bus signals - # and calls into our model object. - self.submodules.slave = wishbone.Target(MyModelWB()) - # The "wishbone.Tap" library component examines the bus at the slave port - # and displays the transactions on the console (/). - self.submodules.tap = wishbone.Tap(self.slave.bus) - # Connect the master to the slave. - self.submodules.intercon = wishbone.InterconnectPointToPoint(self.master.bus, self.slave.bus) - -if __name__ == "__main__": - run_simulation(TB()) - -# Output: -# -# Wrote in 0 cycle(s) -# -# Wrote in 0 cycle(s) -# -# Wrote in 0 cycle(s) -# -# Wrote in 1 cycle(s) -# -# Wrote in 1 cycle(s) -# -# Wrote in 2 cycle(s) -# ... -# -# Read 4 in 2 cycle(s) -# -# Read 5 in 2 cycle(s) -# -# Read 6 in 1 cycle(s) -# -# Read 7 in 1 cycle(s) -# ... diff --git a/examples/sim/basic1.py b/examples/sim/basic1.py index 77b35310..9da2f460 100644 --- a/examples/sim/basic1.py +++ b/examples/sim/basic1.py @@ -1,9 +1,8 @@ from migen.fhdl.std import * -from migen.sim.generic import run_simulation +from migen.sim import Simulator -# Our simple counter, which increments at every cycle -# and prints its current value in simulation. +# Our simple counter, which increments at every cycle. class Counter(Module): def __init__(self): self.count = Signal(4) @@ -12,18 +11,20 @@ class Counter(Module): # We do it with convertible/synthesizable FHDL code. self.sync += self.count.eq(self.count + 1) - # This function will be called at every cycle. - def do_simulation(self, selfp): - # Simply read the count signal and print it. - # The output is: - # Count: 0 - # Count: 1 - # Count: 2 - # ... - print("Count: " + str(selfp.count)) + +# Simply read the count signal and print it. +# The output is: +# Count: 0 +# Count: 1 +# Count: 2 +# ... +def counter_test(dut): + for i in range(20): + print((yield dut.count)) # read and print + yield # next clock cycle + # simulation ends with this generator + if __name__ == "__main__": dut = Counter() - # Since we do not use StopSimulation, limit the simulation - # to some number of cycles. - run_simulation(dut, ncycles=20) + Simulator(dut, counter_test(dut)).run() diff --git a/examples/sim/basic2.py b/examples/sim/basic2.py index 95a44ada..2b6eabe5 100644 --- a/examples/sim/basic2.py +++ b/examples/sim/basic2.py @@ -1,5 +1,5 @@ from migen.fhdl.std import * -from migen.sim.generic import run_simulation +from migen.sim import Simulator # A slightly more elaborate counter. @@ -13,15 +13,17 @@ class Counter(Module): self.sync += If(self.ce, self.count.eq(self.count + 1)) - def do_simulation(self, selfp): + +def counter_test(dut): + for cycle in range(20): # Only assert CE every second cycle. # => each counter value is held for two cycles. - if selfp.simulator.cycle_counter % 2: - selfp.ce = 0 # This is how you write to a signal. + if cycle % 2: + yield dut.ce, 0 # This is how you write to a signal. else: - selfp.ce = 1 - print("Cycle: " + str(selfp.simulator.cycle_counter) + " Count: " + \ - str(selfp.count)) + yield dut.ce, 1 + print("Cycle: {} Count: {}".format(cycle, (yield dut.count))) + yield # Output is: # Cycle: 0 Count: -5 @@ -33,5 +35,4 @@ class Counter(Module): if __name__ == "__main__": dut = Counter() - # Demonstrate VCD output - run_simulation(dut, vcd_name="my.vcd", ncycles=20) + Simulator(dut, counter_test(dut)).run() diff --git a/examples/sim/dataflow.py b/examples/sim/dataflow.py deleted file mode 100644 index 1a5f1000..00000000 --- a/examples/sim/dataflow.py +++ /dev/null @@ -1,47 +0,0 @@ -from migen.fhdl.std import * -from migen.flow.actor import * -from migen.flow.transactions import * -from migen.flow.network import * -from migen.actorlib.sim import * -from migen.sim.generic import run_simulation - - -def source_gen(): - for i in range(10): - print("Sending: " + str(i)) - yield Token("source", {"value": i}) - - -class SimSource(SimActor): - def __init__(self): - self.source = Source([("value", 32)]) - SimActor.__init__(self, source_gen()) - - -def sink_gen(): - while True: - t = Token("sink") - yield t - print("Received: " + str(t.value["value"])) - - -class SimSink(SimActor): - def __init__(self): - self.sink = Sink([("value", 32)]) - SimActor.__init__(self, sink_gen()) - - -class TB(Module): - def __init__(self): - self.source = SimSource() - self.sink = SimSink() - g = DataFlowGraph() - g.add_connection(self.source, self.sink) - self.submodules.comp = CompositeActor(g) - - def do_simulation(self, selfp): - if self.source.token_exchanger.done: - raise StopSimulation - -if __name__ == "__main__": - run_simulation(TB()) diff --git a/examples/sim/fir.py b/examples/sim/fir.py index 641f6b38..ffc6b78a 100644 --- a/examples/sim/fir.py +++ b/examples/sim/fir.py @@ -4,7 +4,7 @@ import matplotlib.pyplot as plt from migen.fhdl.std import * from migen.fhdl import verilog -from migen.sim.generic import run_simulation +from migen.sim import Simulator from functools import reduce from operator import add @@ -29,24 +29,20 @@ class FIR(Module): muls.append(c_fp*sreg) sum_full = Signal((2*self.wsize-1, True)) self.sync += sum_full.eq(reduce(add, muls)) - self.comb += self.o.eq(sum_full[self.wsize-1:]) + self.comb += self.o.eq(sum_full >> self.wsize-1) # A test bench for our FIR filter. # Generates a sine wave at the input and records the output. -class TB(Module): - def __init__(self, coef, frequency): - self.submodules.fir = FIR(coef) - self.frequency = frequency - self.inputs = [] - self.outputs = [] +def fir_tb(dut, frequency, inputs, outputs): + f = 2**(dut.wsize - 1) + for cycle in range(200): + v = 0.1*cos(2*pi*frequency*cycle) + yield dut.i, int(f*v) + inputs.append(v) + outputs.append((yield dut.o)/f) + yield - def do_simulation(self, selfp): - f = 2**(self.fir.wsize - 1) - v = 0.1*cos(2*pi*self.frequency*selfp.simulator.cycle_counter) - selfp.fir.i = int(f*v) - self.inputs.append(v) - self.outputs.append(selfp.fir.o/f) if __name__ == "__main__": # Compute filter coefficients with SciPy. @@ -57,10 +53,9 @@ if __name__ == "__main__": in_signals = [] out_signals = [] for frequency in [0.05, 0.1, 0.25]: - tb = TB(coef, frequency) - run_simulation(tb, ncycles=200) - in_signals += tb.inputs - out_signals += tb.outputs + dut = FIR(coef) + tb = fir_tb(dut, frequency, in_signals, out_signals) + Simulator(dut, tb).run() # Plot data from the input and output waveforms. plt.plot(in_signals) diff --git a/migen/sim.py b/migen/sim.py index 26823965..863cd184 100644 --- a/migen/sim.py +++ b/migen/sim.py @@ -69,7 +69,7 @@ class Evaluator: self.modifications.clear() return r - def _eval(self, node): + def eval(self, node): if isinstance(node, (int, bool)): return node elif isinstance(node, Signal): @@ -78,7 +78,7 @@ class Evaluator: except KeyError: return node.reset elif isinstance(node, _Operator): - operands = [self._eval(o) for o in node.operands] + operands = [self.eval(o) for o in node.operands] if node.op == "-": if len(operands) == 1: return -operands[0] @@ -90,20 +90,23 @@ class Evaluator: # TODO: Cat, Slice, Array, ClockSignal, ResetSignal, Memory raise NotImplementedError + def assign(self, signal, value): + value = value & (2**signal.nbits - 1) + if signal.signed and (value & 2**(signal.nbits - 1)): + value -= 2**signal.nbits + self.modifications[signal] = value + def execute(self, statements): for s in statements: if isinstance(s, _Assign): - value = self._eval(s.r) + value = self.eval(s.r) if isinstance(s.l, Signal): - value = value & (2**s.l.nbits - 1) - if s.l.signed and (value & 2**(s.l.nbits - 1)): - value -= 2**s.l.nbits - self.modifications[s.l] = value + self.assign(s.l, value) else: # TODO: Cat, Slice, Array, ClockSignal, ResetSignal, Memory raise NotImplementedError elif isinstance(s, If): - if self._eval(s.cond): + if self.eval(s.cond): self.execute(s.t) else: self.execute(s.f) @@ -144,6 +147,26 @@ class Simulator: self.evaluator.execute(self.comb_dependent_statements[signal]) modified = self.evaluator.commit() + def _process_generators(self, cd): + if cd in self.generators: + exhausted = [] + for generator in self.generators[cd]: + reply = None + while True: + try: + request = generator.send(reply) + if request is None: + break # next cycle + elif isinstance(request, tuple): + self.evaluator.assign(*request) + else: + reply = self.evaluator.eval(request) + except StopIteration: + exhausted.append(generator) + break + for generator in exhausted: + self.generators[cd].remove(generator) + def _continue_simulation(self): # TODO: passive generators return any(self.generators.values()) @@ -153,10 +176,11 @@ class Simulator: self._comb_propagate(self.evaluator.commit()) while True: - print(self.evaluator.signal_values) cds = self.time.tick() for cd in cds: self.evaluator.execute(self.fragment.sync[cd]) - self._comb_propagate(self.evaluator.commit()) + self._process_generators(cd) + self._comb_propagate(self.evaluator.commit()) + if not self._continue_simulation(): break -- 2.30.2