asmicon: bank machine (untested)
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Sat, 17 Mar 2012 23:12:03 +0000 (00:12 +0100)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Sat, 17 Mar 2012 23:12:03 +0000 (00:12 +0100)
milkymist/asmicon/__init__.py
milkymist/asmicon/bankmachine.py
milkymist/asmicon/multiplexer.py
milkymist/asmicon/refresher.py
top.py

index e107255c6b5b5e8eb66ca7af8c15c289991a667e..db68776f19df50a2e56cd6cc95f4e4e811942d39 100644 (file)
@@ -6,9 +6,7 @@ from milkymist.asmicon.bankmachine import *
 from milkymist.asmicon.multiplexer import *
 
 class PhySettings:
-       def __init__(self, dfi_a, dfi_d, nphases, rdphase, wrphase):
-               # NB: dfi_ba is obtained from GeomSettings.bank_a
-               self.dfi_a = dfi_a
+       def __init__(self, dfi_d, nphases, rdphase, wrphase):
                self.dfi_d = dfi_d
                self.nphases = nphases
                self.rdphase = rdphase
@@ -19,10 +17,12 @@ class GeomSettings:
                self.bank_a = bank_a
                self.row_a = row_a
                self.col_a = col_a
+               self.mux_a = max(row_a, col_a)
 
 class TimingSettings:
-       def __init__(self, tRP, tREFI, tRFC):
+       def __init__(self, tRP, tRCD, tREFI, tRFC):
                self.tRP = tRP
+               self.tRCD = tRCD
                self.tREFI = tREFI
                self.tRFC = tRFC
 
@@ -33,7 +33,7 @@ class ASMIcon:
                self.timing_settings = timing_settings
                self.finalized = False
                
-               self.dfi = dfi.Interface(self.phy_settings.dfi_a,
+               self.dfi = dfi.Interface(self.geom_settings.mux_a,
                        self.geom_settings.bank_a,
                        self.phy_settings.dfi_d,
                        self.phy_settings.nphases)
@@ -49,7 +49,7 @@ class ASMIcon:
                self.finalized = True
                self.hub.finalize()
                slots = self.hub.get_slots()
-               self.refresher = Refresher(self.phy_settings.dfi_a, self.geom_settings.bank_a,
+               self.refresher = Refresher(self.geom_settings.mux_a, self.geom_settings.bank_a,
                        self.timing_settings.tRP, self.timing_settings.tREFI, self.timing_settings.tRFC)
                self.bank_machines = [BankMachine(self.geom_settings, self.timing_settings, self.address_align, i, slots) for i in range(2**self.geom_settings.bank_a)]
                self.multiplexer = Multiplexer(self.phy_settings, self.geom_settings, self.timing_settings,
index d4820cde02a1902b15ec8b1be599bfd9791cdcc2..9c0f6e4f3ae337cb2b84649be9c6ccb599e6d22b 100644 (file)
@@ -1,8 +1,246 @@
 from migen.fhdl.structure import *
+from migen.bus.asmibus import *
+from migen.corelogic.roundrobin import *
+from migen.corelogic.fsm import FSM
+from migen.corelogic.misc import multimux, optree
 
+from milkymist.asmicon.multiplexer import *
+
+# Row:Bank:Col address mapping
+class _AddressSlicer:
+       def __init__(self, geom_settings, address_align):
+               self.geom_settings = geom_settings
+               self.address_align = address_align
+               
+               self._b1 = self.geom_settings.col_a - self.address_align
+               self._b2 = self._b1 + self.geom_settings.row_a
+       
+       def bank(self, address):
+               return address[self._b2:]
+       
+       def row(self, address):
+               return address[self._b1:self._b2]
+       
+       def col(self, address):
+               return Cat(Constant(0, BV(self.address_align)), address[:self._b1])
+
+class _Selector:
+       def __init__(self, slicer, bankn, slots):
+               self.slicer = slicer
+               self.bankn = bankn
+               self.slots = slots
+               
+               self.nslots = len(self.slots)
+               self.stb = Signal()
+               self.ack = Signal()
+               self.tag = Signal(BV(bits_for(self.nslots-1)))
+               self.adr = Signal(self.slots[0].adr.bv)
+               self.we = Signal()
+               
+       def get_fragment(self):
+               comb = []
+               sync = []
+
+               # List outstanding requests for our bank
+               outstandings = []
+               for slot in self.slots:
+                       outstanding = Signal()
+                       comb.append(outstanding.eq(
+                               self.slicer.bank(slot.adr) == self.bankn & \
+                               slot.state == SLOT_PENDING
+                       ))
+                       outstandings.append(outstanding)
+               
+               # Row tracking
+               openrow_r = Signal(BV(self.slicer.geom_settings.row_a))
+               openrow_n = Signal(BV(self.slicer.geom_settings.row_a))
+               openrow = Signal(BV(self.slicer.geom_settings.row_a))
+               comb += [
+                       openrow_n.eq(self.slicer.row(self.adr)),
+                       If(self.stb,
+                               openrow.eq(openrow_n)
+                       ).Else(
+                               openrow.eq(openrow_r)
+                       )
+               ]
+               sync += [
+                       If(self.stb & self.ack,
+                               openrow_r.eq(openrow_n)
+                       )
+               ]
+               hits = []
+               for slot in self.slots:
+                       hit = Signal()
+                       comb.append(hit.eq(self.slicer.row(slot.adr) == openrow))
+                       hits.append(hit)
+               
+               # Determine best request
+               rr = RoundRobin(self.nslots, SP_CE)
+               has_hit = Signal()
+               comb.append(has_hit.eq(optree("|", hits)))
+               
+               best_hit = [rr.request[i].eq(hit & os)
+                       for i, (hit, os) in enumerate(zip(hits, outstandings))]
+               best_fallback = [rr.request[i].eq(os)
+                       for i, os in enumerate(outstandings)]
+               select_stmt = If(has_hit,
+                               *best_hit
+                       ).Else(
+                               *best_fallback
+                       )
+               
+               if self.slots[0].time:
+                       # Implement anti-starvation timer
+                       has_mature = Signal()
+                       comb.append(has_mature.eq(optree("|", [slot.mature for slot in self.slots])))
+                       best_mature = [rr.request[i].eq(slot.mature & os)
+                               for i, (slot, os) in enumerate(zip(self.slots, outstandings))]
+                       select_stmt = If(has_mature, *best_mature).Else(select_stmt)
+               comb.append(select_stmt)
+               
+               # Multiplex
+               state = Signal(BV(2))
+               mux_outputs = [state, self.adr, self.we]
+               mux_inputs = [[slot.state, slot.adr, slot.we]
+                       for slot in self.slots]
+               comb += multimux(rr.grant, mux_inputs, mux_outputs)
+               comb += [
+                       self.stb.eq(state == SLOT_PENDING),
+                       rr.ce.eq(self.ack),
+                       self.tag.eq(rr.grant)
+               ]
+               comb += [slot.process.eq(rr.grant == i & self.ack)
+                       for i, slot in enumerate(self.slots)]
+               
+               return Fragment(comb, sync) + rr.get_fragment()
+
+class _Buffer:
+       def __init__(self, source):
+               self.source = source
+               
+               self.stb = Signal()
+               self.ack = Signal()
+               self.tag = Signal(self.source.tag.bv)
+               self.adr = Signal(self.source.adr.bv)
+               self.we = Signal()
+       
+       def get_fragment(self):
+               en = Signal()
+               comb = [
+                       en.eq(self.ack | ~self.stb),
+                       self.source.ack.eq(en)
+               ]
+               sync = [
+                       If(en,
+                               self.stb.eq(self.source.stb),
+                               self.tag.eq(self.source.tag),
+                               self.adr.eq(self.source.adr),
+                               self.we.eq(self.source.we)
+                       )
+               ]
+               return Fragment(comb, sync)
+       
 class BankMachine:
        def __init__(self, geom_settings, timing_settings, address_align, bankn, slots):
-               pass
+               self.geom_settings = geom_settings
+               self.timing_settings = timing_settings
+               self.address_align = address_align
+               self.bankn = bankn
+               self.slots = slots
+               
+               self.refresh_req = Signal()
+               self.refresh_gnt = Signal()
+               self.cmd_request = CommandRequestRW(geom_settings.mux_a, geom_settings.bank_a,
+                       bits_for(len(slots)-1))
 
        def get_fragment(self):
-               return Fragment()
+               comb = []
+               sync = []
+               
+               # Sub components
+               slicer = _AddressSlicer(self.geom_settings, self.address_align)
+               selector = _Selector(slicer, self.bankn, self.slots)
+               buf = _Buffer(selector)
+               
+               # Row tracking
+               has_openrow = Signal()
+               openrow = Signal(BV(self.geom_settings.row_a))
+               hit = Signal()
+               comb.append(hit.eq(openrow == slicer.row(buf.adr)))
+               track_open = Signal()
+               track_close = Signal()
+               sync += [
+                       If(track_open,
+                               has_openrow.eq(1),
+                               openrow.eq(slicer.row(buf.adr))
+                       ),
+                       If(track_close,
+                               has_openrow.eq(0)
+                       )
+               ]
+               
+               # Address generation
+               s_row_adr = Signal()
+               comb += [
+                       self.cmd_request.ba.eq(self.bankn),
+                       If(s_row_adr,
+                               self.cmd_request.a.eq(slicer.row(buf.adr))
+                       ).Else(
+                               self.cmd_request.a.eq(slicer.col(buf.adr))
+                       )
+               ]
+               
+               comb.append(self.cmd_request.tag.eq(buf.tag))
+               
+               # Control and command generation FSM
+               fsm = FSM("REGULAR", "PRECHARGE", "ACTIVATE", "REFRESH", delayed_enters=[
+                       ("TRP", "ACTIVATE", self.timing_settings.tRP-1),
+                       ("TRCD", "REGULAR", self.timing_settings.tRCD-1)
+               ])
+               fsm.act(fsm.REGULAR,
+                       If(self.refresh_req,
+                               fsm.next_state(fsm.REFRESH)
+                       ).Elif(buf.stb,
+                               If(has_openrow,
+                                       If(hit,
+                                               self.cmd_request.stb.eq(1),
+                                               buf.ack.eq(self.cmd_request.ack),
+                                               self.cmd_request.is_read.eq(~buf.we),
+                                               self.cmd_request.is_write.eq(buf.we),
+                                               self.cmd_request.cas_n.eq(0),
+                                               self.cmd_request.we_n.eq(~buf.we)
+                                       ).Else(
+                                               fsm.next_state(fsm.PRECHARGE)
+                                       )
+                               ).Else(
+                                       fsm.next_state(fsm.ACTIVATE)
+                               )
+                       )
+               )
+               fsm.act(fsm.PRECHARGE,
+                       # Notes:
+                       # 1. we are presenting the column address, A10 is always low
+                       # 2. since we always go to the ACTIVATE state, we do not need
+                       # to assert track_close.
+                       self.cmd_request.stb.eq(1),
+                       If(self.cmd_request.ack, fsm.next_state(fsm.TRP)),
+                       self.cmd_request.ras_n.eq(0),
+                       self.cmd_request.we_n.eq(0)
+               )
+               fsm.act(fsm.ACTIVATE,
+                       s_row_adr.eq(1),
+                       track_open.eq(1),
+                       self.cmd_request.stb.eq(1),
+                       If(self.cmd_request.ack, fsm.next_state(fsm.TRCD)),
+                       self.cmd_request.ras_n.eq(0)
+               )
+               fsm.act(fsm.REFRESH,
+                       self.refresh_gnt.eq(1),
+                       track_close.eq(1),
+                       If(~self.refresh_req, fsm.next_state(fsm.REGULAR))
+               )
+               
+               return Fragment(comb, sync) + \
+                       selector.get_fragment() + \
+                       buf.get_fragment() + \
+                       fsm.get_fragment()
index 3e8ebf7770c7aa6c30ffba552e4e57d64fed32fe..bc68e9cceaeb7f4190c9e19877df799186a28c13 100644 (file)
@@ -1,16 +1,16 @@
 from migen.fhdl.structure import *
 
 class CommandRequest:
-       def __init__(self, dfi_a, dfi_ba):
-               self.a = Signal(BV(dfi_a))
-               self.ba = Signal(BV(dfi_ba))
+       def __init__(self, a, ba):
+               self.a = Signal(BV(a))
+               self.ba = Signal(BV(ba))
                self.cas_n = Signal(reset=1)
                self.ras_n = Signal(reset=1)
                self.we_n = Signal(reset=1)
 
 class CommandRequestRW(CommandRequest):
-       def __init__(self, dfi_a, dfi_ba, tagbits):
-               CommandRequest.__init__(self, dfi_a, dfi_ba)
+       def __init__(self, a, ba, tagbits):
+               CommandRequest.__init__(self, a, ba)
                self.stb = Signal()
                self.ack = Signal()
                self.is_read = Signal()
index 7e086f527e3c04cee817c781cfe8fef1d49796ff..8fbd2cfd7d6cb34d5ce58cd30c42329119af509a 100644 (file)
@@ -5,14 +5,14 @@ from migen.corelogic.fsm import FSM
 from milkymist.asmicon.multiplexer import *
 
 class Refresher:
-       def __init__(self, dfi_a, dfi_ba, tRP, tREFI, tRFC):
+       def __init__(self, a, ba, tRP, tREFI, tRFC):
                self.tRP = tRP
                self.tREFI = tREFI
                self.tRFC = tRFC
                
                self.req = Signal()
                self.ack = Signal()
-               self.cmd_request = CommandRequest(dfi_a, dfi_ba)
+               self.cmd_request = CommandRequest(a, ba)
        
        def get_fragment(self):
                comb = []
@@ -38,7 +38,7 @@ class Refresher:
                                self.cmd_request.cas_n.eq(0),
                                self.cmd_request.ras_n.eq(0)
                        ]),
-                       (self.tRP+self.tRFC, [
+                       (self.tRP+self.tRFC-1, [
                                seq_done.eq(1)
                        ])
                ])
diff --git a/top.py b/top.py
index 6c938cc2b8fc8c7e9a6b9006d4378f159d9206ce..9c6edbd4b3c05952939c7f8f5eab3302c043dbe5 100644 (file)
--- a/top.py
+++ b/top.py
@@ -14,13 +14,12 @@ sram_size = 4096 # in bytes
 l2_size = 8192 # in bytes
 
 clk_period_ns = 1000000000/clk_freq
-def ns(t, margin=False):
+def ns(t, margin=True):
        if margin:
                t += clk_period_ns/2
        return ceil(t/clk_period_ns)
 
 sdram_phy = asmicon.PhySettings(
-       dfi_a=13,
        dfi_d=64, 
        nphases=2,
        rdphase=0,
@@ -33,7 +32,8 @@ sdram_geom = asmicon.GeomSettings(
 )
 sdram_timing = asmicon.TimingSettings(
        tRP=ns(15),
-       tREFI=ns(7800),
+       tRCD=ns(15),
+       tREFI=ns(7800, False),
        tRFC=ns(70)
 )
 
@@ -52,15 +52,15 @@ def get():
        #
        # ASMI
        #
-       asmicon0 = asmicon.ASMIcon(sdram_phy, sdram_geom, sdram_timing, 8)
+       asmicon0 = asmicon.ASMIcon(sdram_phy, sdram_geom, sdram_timing, 16)
        asmiport_wb = asmicon0.hub.get_port()
        asmicon0.finalize()
        
        #
        # DFI
        #
-       ddrphy0 = s6ddrphy.S6DDRPHY(sdram_phy.dfi_a, sdram_geom.bank_a, sdram_phy.dfi_d)
-       dfii0 = dfii.DFIInjector(1, sdram_phy.dfi_a, sdram_geom.bank_a, sdram_phy.dfi_d, sdram_phy.nphases)
+       ddrphy0 = s6ddrphy.S6DDRPHY(sdram_geom.mux_a, sdram_geom.bank_a, sdram_phy.dfi_d)
+       dfii0 = dfii.DFIInjector(1, sdram_geom.mux_a, sdram_geom.bank_a, sdram_phy.dfi_d, sdram_phy.nphases)
        dficon0 = dfi.Interconnect(dfii0.master, ddrphy0.dfi)
        dficon1 = dfi.Interconnect(asmicon0.dfi, dfii0.slave)