From ab800fa2edb3d591ddc17a6cce1ca2f6369c0835 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 8 Mar 2012 18:14:06 +0100 Subject: [PATCH] bus: generic transaction model --- examples/wb_initiator.py | 53 +++++++++++++++++++++++++++ migen/bus/transactions.py | 20 +++++++++++ migen/bus/wishbone.py | 76 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 examples/wb_initiator.py create mode 100644 migen/bus/transactions.py diff --git a/examples/wb_initiator.py b/examples/wb_initiator.py new file mode 100644 index 00000000..83df271c --- /dev/null +++ b/examples/wb_initiator.py @@ -0,0 +1,53 @@ +from random import Random + +from migen.fhdl.structure import * +from migen.fhdl import autofragment +from migen.bus.transactions import * +from migen.bus import wishbone +from migen.sim.generic import Simulator +from migen.sim.icarus import Runner + +def my_generator(): + prng = Random(92837) + for x in range(10): + t = TWrite(x, 2*x) + yield t + print("Wrote in " + str(t.latency) + " cycle(s)") + for delay in range(prng.randrange(0, 3)): + yield None + 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 + +class MyPeripheral: + def __init__(self): + self.bus = wishbone.Interface() + self.ack_en = Signal() + self.prng = Random(763627) + + def do_simulation(self, s): + # Only authorize acks on certain cycles to simulate variable latency + s.wr(self.ack_en, self.prng.randrange(0, 2)) + + def get_fragment(self): + comb = [ + self.bus.ack.eq(self.bus.cyc & self.bus.stb & self.ack_en), + self.bus.dat_r.eq(self.bus.adr + 4) + ] + return Fragment(comb, sim=[self.do_simulation]) + +def main(): + master = wishbone.Initiator(my_generator()) + slave = MyPeripheral() + tap = wishbone.Tap(slave.bus) + intercon = wishbone.InterconnectPointToPoint(master.bus, slave.bus) + def end_simulation(s): + s.interrupt = master.done + fragment = autofragment.from_local() + Fragment(sim=[end_simulation]) + sim = Simulator(fragment, Runner()) + sim.run() + +main() diff --git a/migen/bus/transactions.py b/migen/bus/transactions.py new file mode 100644 index 00000000..002344de --- /dev/null +++ b/migen/bus/transactions.py @@ -0,0 +1,20 @@ +from migen.fhdl.structure import bits_for + +class Transaction: + def __init__(self, address, data=0, sel=None): + self.address = address + self.data = data + if sel is None: + bytes = (bits_for(data) + 7)//8 + sel = 2**bytes - 1 + self.sel = sel + self.latency = 0 + + def __str__(self): + return "<" + self.__class__.__name__ + " adr:" + hex(self.address) + " dat:" + hex(self.data) + ">" + +class TRead(Transaction): + pass + +class TWrite(Transaction): + pass diff --git a/migen/bus/wishbone.py b/migen/bus/wishbone.py index ee7719b2..5365b795 100644 --- a/migen/bus/wishbone.py +++ b/migen/bus/wishbone.py @@ -2,6 +2,7 @@ from migen.fhdl.structure import * from migen.corelogic import roundrobin from migen.corelogic.misc import multimux, optree from migen.bus.simple import * +from migen.bus.transactions import * _desc = Description( (M_TO_S, "adr", 30), @@ -127,3 +128,78 @@ class InterconnectShared: def get_fragment(self): return self._arbiter.get_fragment() + self._decoder.get_fragment() + +class Tap: + def __init__(self, bus=None, handler=print): + # If bus is None, create one and act as a normal slave. + # If we pass an existing one, dump the transactions + # without interfering with the bus. + if bus is None: + self.bus = Interface() + self.ack = True + else: + self.bus = bus + self.ack = False + self.handler = handler + + def do_simulation(self, s): + if s.rd(self.bus.ack): + assert(s.rd(self.bus.cyc) and s.rd(self.bus.stb)) + if s.rd(self.bus.we): + transaction = TWrite(s.rd(self.bus.adr), + s.rd(self.bus.dat_w), + s.rd(self.bus.sel)) + else: + transaction = TRead(s.rd(self.bus.adr), + s.rd(self.bus.dat_r)) + self.handler(transaction) + + def get_fragment(self): + if self.ack: + sync = [ + self.bus.ack.eq(0), + If(self.bus.cyc & self.bus.stb & ~self.bus.ack, + self.bus.ack.eq(1) + ) + ] + else: + sync = [] + return Fragment(sync=sync, sim=[self.do_simulation]) + +class Initiator: + def __init__(self, generator): + self.generator = generator + self.bus = Interface() + self.transaction_start = 0 + self.transaction = None + self.done = False + + def do_simulation(self, s): + if not self.done: + if self.transaction is None or s.rd(self.bus.ack): + if self.transaction is not None: + self.transaction.latency = s.cycle_counter - self.transaction_start - 1 + if isinstance(self.transaction, TRead): + self.transaction.data = s.rd(self.bus.dat_r) + try: + self.transaction = next(self.generator) + except StopIteration: + self.done = True + self.transaction = None + if self.transaction is not None: + self.transaction_start = s.cycle_counter + s.wr(self.bus.cyc, 1) + s.wr(self.bus.stb, 1) + s.wr(self.bus.adr, self.transaction.address) + if isinstance(self.transaction, TWrite): + s.wr(self.bus.we, 1) + s.wr(self.bus.sel, self.transaction.sel) + s.wr(self.bus.dat_w, self.transaction.data) + else: + s.wr(self.bus.we, 0) + else: + s.wr(self.bus.cyc, 0) + s.wr(self.bus.stb, 0) + + def get_fragment(self): + return Fragment(sim=[self.do_simulation]) -- 2.30.2