lasmicon: update test benches
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Mon, 15 Jul 2013 15:45:55 +0000 (17:45 +0200)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Mon, 15 Jul 2013 15:45:55 +0000 (17:45 +0200)
tb/asmicon/asmicon.py [deleted file]
tb/asmicon/asmicon_wb.py [deleted file]
tb/asmicon/bankmachine.py [deleted file]
tb/asmicon/common.py [deleted file]
tb/asmicon/refresher.py [deleted file]
tb/asmicon/selector.py [deleted file]
tb/lasmicon/bankmachine.py [new file with mode: 0644]
tb/lasmicon/common.py [new file with mode: 0644]
tb/lasmicon/lasmicon.py [new file with mode: 0644]
tb/lasmicon/lasmicon_wb.py [new file with mode: 0644]
tb/lasmicon/refresher.py [new file with mode: 0644]

diff --git a/tb/asmicon/asmicon.py b/tb/asmicon/asmicon.py
deleted file mode 100644 (file)
index 399a18b..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-from migen.fhdl.std import *
-from migen.bus.asmibus import *
-from migen.sim.generic import Simulator, TopLevel
-
-from milkymist.asmicon import *
-
-from common import sdram_phy, sdram_geom, sdram_timing, DFILogger
-
-def my_generator_r():
-       for x in range(50):
-               t = TRead(x)
-               yield t
-       print("reads done")
-
-def my_generator_w():
-       for x in range(50):
-               t = TWrite(x, x)
-               yield t
-       print("writes done")
-
-def main():
-       dut = ASMIcon(sdram_phy, sdram_geom, sdram_timing)
-       initiator1 = Initiator(my_generator_r(), dut.hub.get_port())
-       initiator2 = Initiator(my_generator_w(), dut.hub.get_port())
-       dut.finalize()
-       
-       logger = DFILogger(dut.dfi)
-       
-       def end_simulation(s):
-               s.interrupt = initiator1.done and initiator2.done
-       
-       fragment = dut.get_fragment() + initiator1.get_fragment() + initiator2.get_fragment() + \
-               logger.get_fragment() + \
-               Fragment(sim=[end_simulation])
-       sim = Simulator(fragment, TopLevel("my.vcd"))
-       sim.run(700)
-
-main()
diff --git a/tb/asmicon/asmicon_wb.py b/tb/asmicon/asmicon_wb.py
deleted file mode 100644 (file)
index e5efe0f..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-from migen.fhdl.std import *
-from migen.bus import wishbone, wishbone2asmi, asmibus
-from migen.sim.generic import Simulator, TopLevel
-
-from milkymist.asmicon import *
-
-from common import sdram_phy, sdram_geom, sdram_timing, DFILogger
-
-l2_size = 8192 # in bytes
-
-def my_generator():
-       for x in range(20):
-               t = TWrite(x, x)
-               yield t
-               print(str(t) + " delay=" + str(t.latency))
-       for x in range(20):
-               t = TRead(x)
-               yield t
-               print(str(t) + " delay=" + str(t.latency))
-       for x in range(20):
-               t = TRead(x+l2_size//4)
-               yield t
-               print(str(t) + " delay=" + str(t.latency))
-
-def main():
-       controller = ASMIcon(sdram_phy, sdram_geom, sdram_timing)
-       bridge = wishbone2asmi.WB2ASMI(l2_size//4, controller.hub.get_port())
-       controller.finalize()
-       initiator = wishbone.Initiator(my_generator())
-       conn = wishbone.InterconnectPointToPoint(initiator.bus, bridge.wishbone)
-       
-       logger = DFILogger(controller.dfi)
-       
-       def end_simulation(s):
-               s.interrupt = initiator.done
-       
-       fragment = controller.get_fragment() + \
-               bridge.get_fragment() + \
-               initiator.get_fragment() + \
-               conn.get_fragment() + \
-               logger.get_fragment() + \
-               Fragment(sim=[end_simulation])
-       sim = Simulator(fragment, TopLevel("my.vcd"))
-       sim.run()
-
-main()
diff --git a/tb/asmicon/bankmachine.py b/tb/asmicon/bankmachine.py
deleted file mode 100644 (file)
index fd495d6..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-from migen.fhdl.std import *
-from migen.bus.asmibus import *
-from migen.sim.generic import Simulator, TopLevel
-
-from milkymist.asmicon.bankmachine import *
-
-from common import sdram_geom, sdram_timing, CommandLogger
-
-def my_generator():
-       for x in range(10):
-               t = TWrite(x)
-               yield t
-       for x in range(10):
-               t = TWrite(x + 2200)
-               yield t
-
-class Completer:
-       def __init__(self, hub, cmd):
-               self.hub = hub
-               self.cmd = cmd
-               
-       def get_fragment(self):
-               sync = [
-                       self.hub.call.eq(self.cmd.stb & self.cmd.ack & (self.cmd.is_read | self.cmd.is_write)),
-                       self.hub.tag_call.eq(self.cmd.tag)
-               ]
-               return Fragment(sync=sync)
-
-def main():
-       hub = Hub(12, 128, 2)
-       initiator = Initiator(hub.get_port(), my_generator())
-       hub.finalize()
-       
-       dut = BankMachine(sdram_geom, sdram_timing, 2, 0, hub.get_slots())
-       logger = CommandLogger(dut.cmd, True)
-       completer = Completer(hub, dut.cmd)
-       
-       def end_simulation(s):
-               s.interrupt = initiator.done
-       
-       fragment = hub.get_fragment() + initiator.get_fragment() + \
-               dut.get_fragment() + logger.get_fragment() + completer.get_fragment() + \
-               Fragment(sim=[end_simulation])
-       sim = Simulator(fragment, TopLevel("my.vcd"))
-       sim.run()
-
-main()
diff --git a/tb/asmicon/common.py b/tb/asmicon/common.py
deleted file mode 100644 (file)
index b41956c..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-from fractions import Fraction
-from math import ceil
-
-from migen.fhdl.std import *
-from migen.sim.generic import Proxy
-
-from milkymist import asmicon
-
-MHz = 1000000
-clk_freq = (83 + Fraction(1, 3))*MHz
-
-clk_period_ns = 1000000000/clk_freq
-def ns(t, margin=True):
-       if margin:
-               t += clk_period_ns/2
-       return ceil(t/clk_period_ns)
-
-sdram_phy = asmicon.PhySettings(
-       dfi_d=64, 
-       nphases=2,
-       rdphase=0,
-       wrphase=1
-)
-sdram_geom = asmicon.GeomSettings(
-       bank_a=2,
-       row_a=13,
-       col_a=10
-)
-sdram_timing = asmicon.TimingSettings(
-       tRP=ns(15),
-       tRCD=ns(15),
-       tWR=ns(15),
-       tREFI=ns(7800, False),
-       tRFC=ns(70),
-       
-       CL=3,
-       rd_delay=4,
-
-       slot_time=16,
-       read_time=32,
-       write_time=16
-)
-
-def decode_sdram(ras_n, cas_n, we_n, bank, address):
-       elts = []
-       if not ras_n and cas_n and we_n:
-               elts.append("ACTIVATE")
-               elts.append("BANK " + str(bank))
-               elts.append("ROW " + str(address))
-       elif ras_n and not cas_n and we_n:
-               elts.append("READ\t")
-               elts.append("BANK " + str(bank))
-               elts.append("COL " + str(address))
-       elif ras_n and not cas_n and not we_n:
-               elts.append("WRITE\t")
-               elts.append("BANK " + str(bank))
-               elts.append("COL " + str(address))
-       elif ras_n and cas_n and not we_n:
-               elts.append("BST")
-       elif not ras_n and not cas_n and we_n:
-               elts.append("AUTO REFRESH")
-       elif not ras_n and cas_n and not we_n:
-               elts.append("PRECHARGE")
-               if address & 2**10:
-                       elts.append("ALL")
-               else:
-                       elts.append("BANK " + str(bank))
-       elif not ras_n and not cas_n and not we_n:
-               elts.append("LMR")
-       return elts
-
-class CommandLogger:
-       def __init__(self, cmd, rw=False):
-               self.cmd = cmd
-               self.rw = rw
-       
-       def do_simulation(self, s):
-               elts = ["@" + str(s.cycle_counter)]
-               cmdp = Proxy(s, self.cmd)
-               elts += decode_sdram(cmdp.ras_n, cmdp.cas_n, cmdp.we_n, cmdp.ba, cmdp.a)
-               if len(elts) > 1:
-                       print("\t".join(elts))
-       
-       def get_fragment(self):
-               if self.rw:
-                       comb = [self.cmd.ack.eq(1)]
-               else:
-                       comb = []
-               return Fragment(comb, sim=[self.do_simulation])
-
-class DFILogger:
-       def __init__(self, dfi):
-               self.dfi = dfi
-       
-       def do_simulation(self, s):
-               dfip = Proxy(s, self.dfi)
-               
-               for i, p in enumerate(dfip.phases):
-                       elts = ["PH=" + str(i) + "\t @" + str(s.cycle_counter)]
-                       elts += decode_sdram(p.ras_n, p.cas_n, p.we_n, p.bank, p.address)
-                       if len(elts) > 1:
-                               print("\t".join(elts))
-       
-       def get_fragment(self):
-               return Fragment(sim=[self.do_simulation])
-               
-class SlotsLogger:
-       def __init__(self, slicer, slots):
-               self.slicer = slicer
-               self.slots = slots
-               
-       def do_simulation(self, sim):
-               state_strs = ["EMPTY", "PEND", "PRCESS"]
-               rw_strs = ["RD", "WR"]
-               print("\t" + "\t".join([str(x) for x in range(len(self.slots))]))
-               print("State:\t" + "\t".join([state_strs[sim.rd(s.state)] for s in self.slots]))
-               print("RW:\t" + "\t".join([rw_strs[sim.rd(s.we)] for s in self.slots]))
-               print("Row:\t" + "\t".join([str(self.slicer.row(sim.rd(s.adr))) for s in self.slots]))
-               print("Bank:\t" + "\t".join([str(self.slicer.bank(sim.rd(s.adr))) for s in self.slots]))
-               print("Col:\t" + "\t".join([str(self.slicer.col(sim.rd(s.adr))) for s in self.slots]))
-               times = []
-               for s in self.slots:
-                       if s.time:
-                               times.append(str(sim.rd(s._counter)) + "/" + str(s.time))
-                       else:
-                               times.append("N/A")
-               print("Time:\t" + "\t".join(times))
-
-       def get_fragment(self):
-               return Fragment(sim=[self.do_simulation])
diff --git a/tb/asmicon/refresher.py b/tb/asmicon/refresher.py
deleted file mode 100644 (file)
index 79b9b18..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-from random import Random
-
-from migen.fhdl.std import *
-from migen.sim.generic import Simulator, TopLevel
-
-from milkymist.asmicon.refresher import *
-
-from common import CommandLogger
-
-class Granter:
-       def __init__(self, req, ack):
-               self.req = req
-               self.ack = ack
-               self.state = 0
-               self.prng = Random(92837)
-       
-       def do_simulation(self, s):
-               elts = ["@" + str(s.cycle_counter)]
-               
-               if self.state == 0:
-                       if s.rd(self.req):
-                               elts.append("Refresher requested access")
-                               self.state = 1
-               elif self.state == 1:
-                       if self.prng.randrange(0, 5) == 0:
-                               elts.append("Granted access to refresher")
-                               s.wr(self.ack, 1)
-                               self.state = 2
-               elif self.state == 2:
-                       if not s.rd(self.req):
-                               elts.append("Refresher released access")
-                               s.wr(self.ack, 0)
-                               self.state = 0
-                       
-               if len(elts) > 1:
-                       print("\t".join(elts))
-       
-       def get_fragment(self):
-               return Fragment(sim=[self.do_simulation])
-
-def main():
-       dut = Refresher(13, 2, tRP=3, tREFI=100, tRFC=5)
-       logger = CommandLogger(dut.cmd)
-       granter = Granter(dut.req, dut.ack)
-       fragment = dut.get_fragment() + logger.get_fragment() + granter.get_fragment()
-       sim = Simulator(fragment)
-       sim.run(400)
-
-main()
diff --git a/tb/asmicon/selector.py b/tb/asmicon/selector.py
deleted file mode 100644 (file)
index ee98781..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-from random import Random
-
-from migen.fhdl.std import *
-from migen.bus.asmibus import *
-from migen.sim.generic import Simulator, TopLevel
-
-from milkymist.asmicon.bankmachine import _AddressSlicer, _SimpleSelector
-
-from common import SlotsLogger, sdram_geom
-
-def my_generator(dt, offset):
-       for t in range(dt):
-               yield None
-       for x in range(10):
-               t = TRead(x + offset)
-               yield t
-
-class Selector:
-       def __init__(self, slicer, bankn, slots):
-               self.selector = _SimpleSelector(slicer, bankn, slots)
-               self.queue = []
-               self.prng = Random(876)
-       
-       def do_simulation(self, s):
-               if self.prng.randrange(0, 5):
-                       s.wr(self.selector.ack, 1)
-               else:
-                       s.wr(self.selector.ack, 0)
-               if s.rd(self.selector.stb) and s.rd(self.selector.ack):
-                       tag = s.rd(self.selector.tag)
-                       self.queue.append(tag)
-                       print("==> SELECTED: " + str(tag))
-               print("")
-       
-       def get_fragment(self):
-               return self.selector.get_fragment() + \
-                       Fragment(sim=[self.do_simulation])
-
-class Completer:
-       def __init__(self, hub, queue):
-               self.hub = hub
-               self.queue = queue
-       
-       def do_simulation(self, s):
-               if self.queue:
-                       tag = self.queue.pop()
-                       s.wr(self.hub.call, 1)
-                       s.wr(self.hub.tag_call, tag)
-               else:
-                       s.wr(self.hub.call, 0)
-               
-       def get_fragment(self):
-               return Fragment(sim=[self.do_simulation])
-
-def main():
-       hub = Hub(12, 128, 8)
-       initiators = [Initiator(hub.get_port(), my_generator(0, 2200*(i//6)+i*10))
-               for i in range(8)]
-       hub.finalize()
-       
-       slots = hub.get_slots()
-       slicer = _AddressSlicer(sdram_geom, 2)
-       logger = SlotsLogger(slicer, slots)
-       selector = Selector(slicer, 0, slots)
-       completer = Completer(hub, selector.queue)
-       
-       def end_simulation(s):
-               s.interrupt = all([i.done for i in initiators])
-       
-       fragment = hub.get_fragment() + sum([i.get_fragment() for i in initiators], Fragment()) + \
-               logger.get_fragment() + selector.get_fragment() + completer.get_fragment() + \
-               Fragment(sim=[end_simulation])
-       sim = Simulator(fragment, TopLevel("my.vcd"))
-       sim.run()
-
-main()
diff --git a/tb/lasmicon/bankmachine.py b/tb/lasmicon/bankmachine.py
new file mode 100644 (file)
index 0000000..3564183
--- /dev/null
@@ -0,0 +1,44 @@
+from migen.fhdl.std import *
+from migen.bus.lasmibus import *
+from migen.sim.generic import Simulator, TopLevel
+
+from milkymist.lasmicon.bankmachine import *
+
+from common import sdram_geom, sdram_timing, CommandLogger
+
+def my_generator():
+       for x in range(10):
+               yield True, x
+       for x in range(10):
+               yield False, 128*x
+
+class TB(Module):
+       def __init__(self):
+               self.req = Interface(32, 32, 1,
+                       sdram_timing.req_queue_size, sdram_timing.read_latency, sdram_timing.write_latency)
+               self.submodules.dut = BankMachine(sdram_geom, sdram_timing, 2, 0, self.req)
+               self.submodules.logger = CommandLogger(self.dut.cmd, True)
+               self.generator = my_generator()
+               self.dat_ack_cnt = 0
+
+       def do_simulation(self, s):
+               if s.rd(self.req.dat_ack):
+                       self.dat_ack_cnt += 1
+               if s.rd(self.req.req_ack):
+                       try:
+                               we, adr = next(self.generator)
+                       except StopIteration:
+                               s.wr(self.req.stb, 0)
+                               if not s.rd(self.req.lock):
+                                       s.interrupt = True
+                                       print("data ack count: {0}".format(self.dat_ack_cnt))
+                               return
+                       s.wr(self.req.adr, adr)
+                       s.wr(self.req.we, we)
+                       s.wr(self.req.stb, 1)
+
+def main():    
+       sim = Simulator(TB(), TopLevel("my.vcd"))
+       sim.run()
+
+main()
diff --git a/tb/lasmicon/common.py b/tb/lasmicon/common.py
new file mode 100644 (file)
index 0000000..e7e63e1
--- /dev/null
@@ -0,0 +1,99 @@
+from fractions import Fraction
+from math import ceil
+
+from migen.fhdl.std import *
+from migen.sim.generic import Proxy
+
+from milkymist import lasmicon
+
+MHz = 1000000
+clk_freq = (83 + Fraction(1, 3))*MHz
+
+clk_period_ns = 1000000000/clk_freq
+def ns(t, margin=True):
+       if margin:
+               t += clk_period_ns/2
+       return ceil(t/clk_period_ns)
+
+sdram_phy = lasmicon.PhySettings(
+       type="DDR",
+       dfi_d=64, 
+       nphases=2,
+       rdphase=0,
+       wrphase=1,
+       cl=3
+)
+sdram_geom = lasmicon.GeomSettings(
+       bank_a=2,
+       row_a=13,
+       col_a=10
+)
+sdram_timing = lasmicon.TimingSettings(
+       tRP=ns(15),
+       tRCD=ns(15),
+       tWR=ns(15),
+       tWTR=2,
+       tREFI=ns(7800, False),
+       tRFC=ns(70),
+       
+       read_latency=5,
+       write_latency=0,
+
+       req_queue_size=8,
+       read_time=32,
+       write_time=16
+)
+
+def decode_sdram(ras_n, cas_n, we_n, bank, address):
+       elts = []
+       if not ras_n and cas_n and we_n:
+               elts.append("ACTIVATE")
+               elts.append("BANK " + str(bank))
+               elts.append("ROW " + str(address))
+       elif ras_n and not cas_n and we_n:
+               elts.append("READ\t")
+               elts.append("BANK " + str(bank))
+               elts.append("COL " + str(address))
+       elif ras_n and not cas_n and not we_n:
+               elts.append("WRITE\t")
+               elts.append("BANK " + str(bank))
+               elts.append("COL " + str(address))
+       elif ras_n and cas_n and not we_n:
+               elts.append("BST")
+       elif not ras_n and not cas_n and we_n:
+               elts.append("AUTO REFRESH")
+       elif not ras_n and cas_n and not we_n:
+               elts.append("PRECHARGE")
+               if address & 2**10:
+                       elts.append("ALL")
+               else:
+                       elts.append("BANK " + str(bank))
+       elif not ras_n and not cas_n and not we_n:
+               elts.append("LMR")
+       return elts
+
+class CommandLogger(Module):
+       def __init__(self, cmd, rw=False):
+               self.cmd = cmd
+               if rw:
+                       self.comb += self.cmd.ack.eq(1)
+       
+       def do_simulation(self, s):
+               elts = ["@" + str(s.cycle_counter)]
+               cmdp = Proxy(s, self.cmd)
+               elts += decode_sdram(cmdp.ras_n, cmdp.cas_n, cmdp.we_n, cmdp.ba, cmdp.a)
+               if len(elts) > 1:
+                       print("\t".join(elts))
+
+class DFILogger(Module):
+       def __init__(self, dfi):
+               self.dfi = dfi
+       
+       def do_simulation(self, s):
+               dfip = Proxy(s, self.dfi)
+               
+               for i, p in enumerate(dfip.phases):
+                       elts = ["@" + str(s.cycle_counter) + ":" + str(i)]
+                       elts += decode_sdram(p.ras_n, p.cas_n, p.we_n, p.bank, p.address)
+                       if len(elts) > 1:
+                               print("\t".join(elts))
diff --git a/tb/lasmicon/lasmicon.py b/tb/lasmicon/lasmicon.py
new file mode 100644 (file)
index 0000000..e64ece3
--- /dev/null
@@ -0,0 +1,45 @@
+from migen.fhdl.std import *
+from migen.bus.lasmibus import *
+from migen.sim.generic import Simulator, TopLevel
+
+from milkymist.lasmicon import *
+
+from common import sdram_phy, sdram_geom, sdram_timing, DFILogger
+
+def my_generator_r(n):
+       for x in range(10):
+               t = TRead(128*n + 48*n*x)
+               yield t
+       print("{0:3}: reads done".format(n))
+
+def my_generator_w(n):
+       for x in range(10):
+               t = TWrite(128*n + 48*n*x, x)
+               yield t
+       print("{0:3}: writes done".format(n))
+
+def my_generator(n):
+       if n % 2:
+               return my_generator_w(n // 2)
+       else:
+               return my_generator_r(n // 2)
+
+class TB(Module):
+       def __init__(self):
+               self.submodules.dut = LASMIcon(sdram_phy, sdram_geom, sdram_timing)
+               self.submodules.xbar = lasmibus.Crossbar([self.dut.lasmic], 6, self.dut.nrowbits)
+               self.submodules.logger = DFILogger(self.dut.dfi)
+
+               self.initiators = [Initiator(my_generator(n), master)
+                       for n, master in enumerate(self.xbar.masters)]
+               self.submodules += self.initiators
+
+       def do_simulation(self, s):
+               s.interrupt = all(initiator.done for initiator in self.initiators)
+
+
+def main():
+       sim = Simulator(TB(), TopLevel("my.vcd"))
+       sim.run()
+
+main()
diff --git a/tb/lasmicon/lasmicon_wb.py b/tb/lasmicon/lasmicon_wb.py
new file mode 100644 (file)
index 0000000..4d6e247
--- /dev/null
@@ -0,0 +1,43 @@
+from migen.fhdl.std import *
+from migen.bus import wishbone, wishbone2lasmi, lasmibus
+from migen.bus.transactions import *
+from migen.sim.generic import Simulator, TopLevel
+
+from milkymist.lasmicon import *
+
+from common import sdram_phy, sdram_geom, sdram_timing, DFILogger
+
+l2_size = 8192 # in bytes
+
+def my_generator():
+       for x in range(20):
+               t = TWrite(x, x)
+               yield t
+               print(str(t) + " delay=" + str(t.latency))
+       for x in range(20):
+               t = TRead(x)
+               yield t
+               print(str(t) + " delay=" + str(t.latency))
+       for x in range(20):
+               t = TRead(x+l2_size//4)
+               yield t
+               print(str(t) + " delay=" + str(t.latency))
+
+class TB(Module):
+       def __init__(self):
+               self.submodules.ctler = LASMIcon(sdram_phy, sdram_geom, sdram_timing)
+               # FIXME: remove dummy master
+               self.submodules.xbar = lasmibus.Crossbar([self.ctler.lasmic], 2, self.ctler.nrowbits)
+               self.submodules.logger = DFILogger(self.ctler.dfi)
+               self.submodules.bridge = wishbone2lasmi.WB2LASMI(l2_size//4, self.xbar.masters[0])
+               self.submodules.initiator = wishbone.Initiator(my_generator())
+               self.submodules.conn = wishbone.InterconnectPointToPoint(self.initiator.bus, self.bridge.wishbone)
+
+       def do_simulation(self, s):
+               s.interrupt = self.initiator.done
+
+def main():
+       sim = Simulator(TB(), TopLevel("my.vcd"))
+       sim.run()
+
+main()
diff --git a/tb/lasmicon/refresher.py b/tb/lasmicon/refresher.py
new file mode 100644 (file)
index 0000000..72a527a
--- /dev/null
@@ -0,0 +1,47 @@
+from random import Random
+
+from migen.fhdl.std import *
+from migen.sim.generic import Simulator, TopLevel
+
+from milkymist.lasmicon.refresher import *
+
+from common import CommandLogger
+
+class Granter(Module):
+       def __init__(self, req, ack):
+               self.req = req
+               self.ack = ack
+               self.state = 0
+               self.prng = Random(92837)
+       
+       def do_simulation(self, s):
+               elts = ["@" + str(s.cycle_counter)]
+               
+               if self.state == 0:
+                       if s.rd(self.req):
+                               elts.append("Refresher requested access")
+                               self.state = 1
+               elif self.state == 1:
+                       if self.prng.randrange(0, 5) == 0:
+                               elts.append("Granted access to refresher")
+                               s.wr(self.ack, 1)
+                               self.state = 2
+               elif self.state == 2:
+                       if not s.rd(self.req):
+                               elts.append("Refresher released access")
+                               s.wr(self.ack, 0)
+                               self.state = 0
+                       
+               if len(elts) > 1:
+                       print("\t".join(elts))
+
+class TB(Module):
+       def __init__(self):
+               self.submodules.dut = Refresher(13, 2, tRP=3, tREFI=100, tRFC=5)
+               self.submodules.logger = CommandLogger(self.dut.cmd)
+               self.submodules.granter = Granter(self.dut.req, self.dut.ack)
+
+def main():
+       Simulator(TB()).run(400)
+
+main()