bus: generic transaction model
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Thu, 8 Mar 2012 17:14:06 +0000 (18:14 +0100)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Thu, 8 Mar 2012 17:14:06 +0000 (18:14 +0100)
examples/wb_initiator.py [new file with mode: 0644]
migen/bus/transactions.py [new file with mode: 0644]
migen/bus/wishbone.py

diff --git a/examples/wb_initiator.py b/examples/wb_initiator.py
new file mode 100644 (file)
index 0000000..83df271
--- /dev/null
@@ -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 (file)
index 0000000..002344d
--- /dev/null
@@ -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
index ee7719b2e11d8dabe57c32be6f37f7bc6721a91f..5365b7954b0258054e0c7ce91c9f421a5ae5ec01 100644 (file)
@@ -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])