asmicon: multiplexer (untested)
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Sun, 18 Mar 2012 21:11:01 +0000 (22:11 +0100)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Sun, 18 Mar 2012 21:11:01 +0000 (22:11 +0100)
milkymist/asmicon/__init__.py
milkymist/asmicon/bankmachine.py
milkymist/asmicon/multiplexer.py
milkymist/asmicon/refresher.py
top.py

index 376d450c262dec331abfcc3d087ba475467b6a04..0d7564a84a1f081faaa3618c1d9191ddb8e67e3d 100644 (file)
@@ -20,12 +20,19 @@ class GeomSettings:
                self.mux_a = max(row_a, col_a)
 
 class TimingSettings:
-       def __init__(self, tRP, tRCD, tREFI, tRFC, slot_time):
+       def __init__(self, tRP, tRCD, tWR, tREFI, tRFC, CL, rd_delay, slot_time, read_time, write_time):
                self.tRP = tRP
                self.tRCD = tRCD
+               self.tWR = tWR
                self.tREFI = tREFI
                self.tRFC = tRFC
+               
+               self.CL = CL
+               self.rd_delay = rd_delay
+               
                self.slot_time = slot_time
+               self.read_time = read_time
+               self.write_time = write_time
 
 class ASMIcon:
        def __init__(self, phy_settings, geom_settings, timing_settings):
index 9c0f6e4f3ae337cb2b84649be9c6ccb599e6d22b..73618689447413fcb60bc00bca712a98c066e004 100644 (file)
@@ -150,7 +150,7 @@ class BankMachine:
                
                self.refresh_req = Signal()
                self.refresh_gnt = Signal()
-               self.cmd_request = CommandRequestRW(geom_settings.mux_a, geom_settings.bank_a,
+               self.cmd = CommandRequestRW(geom_settings.mux_a, geom_settings.bank_a,
                        bits_for(len(slots)-1))
 
        def get_fragment(self):
@@ -182,15 +182,15 @@ class BankMachine:
                # Address generation
                s_row_adr = Signal()
                comb += [
-                       self.cmd_request.ba.eq(self.bankn),
+                       self.cmd.ba.eq(self.bankn),
                        If(s_row_adr,
-                               self.cmd_request.a.eq(slicer.row(buf.adr))
+                               self.cmd.a.eq(slicer.row(buf.adr))
                        ).Else(
-                               self.cmd_request.a.eq(slicer.col(buf.adr))
+                               self.cmd.a.eq(slicer.col(buf.adr))
                        )
                ]
                
-               comb.append(self.cmd_request.tag.eq(buf.tag))
+               comb.append(self.cmd.tag.eq(buf.tag))
                
                # Control and command generation FSM
                fsm = FSM("REGULAR", "PRECHARGE", "ACTIVATE", "REFRESH", delayed_enters=[
@@ -203,12 +203,12 @@ class BankMachine:
                        ).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)
+                                               self.cmd.stb.eq(1),
+                                               buf.ack.eq(self.cmd.ack),
+                                               self.cmd.is_read.eq(~buf.we),
+                                               self.cmd.is_write.eq(buf.we),
+                                               self.cmd.cas_n.eq(0),
+                                               self.cmd.we_n.eq(~buf.we)
                                        ).Else(
                                                fsm.next_state(fsm.PRECHARGE)
                                        )
@@ -222,17 +222,17 @@ class BankMachine:
                        # 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)
+                       self.cmd.stb.eq(1),
+                       If(self.cmd.ack, fsm.next_state(fsm.TRP)),
+                       self.cmd.ras_n.eq(0),
+                       self.cmd.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)
+                       self.cmd.stb.eq(1),
+                       If(self.cmd.ack, fsm.next_state(fsm.TRCD)),
+                       self.cmd.ras_n.eq(0)
                )
                fsm.act(fsm.REFRESH,
                        self.refresh_gnt.eq(1),
index bc68e9cceaeb7f4190c9e19877df799186a28c13..da1818b5cd2c83cbe20af15aff043b24b7766c63 100644 (file)
@@ -1,4 +1,9 @@
+import math
+
 from migen.fhdl.structure import *
+from migen.corelogic.roundrobin import *
+from migen.corelogic.misc import multimux, optree
+from migen.corelogic.fsm import FSM
 
 class CommandRequest:
        def __init__(self, a, ba):
@@ -17,9 +22,261 @@ class CommandRequestRW(CommandRequest):
                self.is_write = Signal()
                self.tag = Signal(BV(tagbits))
 
+class _CommandChooser:
+       def __init__(self, requests, tagbits):
+               self.requests = requests
+               
+               self.want_reads = Signal()
+               self.want_writes = Signal()
+               # NB: cas_n/ras_n/we_n are 1 when stb is inactive
+               self.cmd = CommandRequestRW(self.requests[0].a.bv.width, self.requests[0].ba.bv.width, tagbits)
+       
+       def get_fragment(self):
+               comb = []
+               sync = []
+               
+               rr = RoundRobin(len(self.requests), SP_CE)
+               
+               comb += [rr.request[i].eq(req.stb & ((req.is_read == self.want_reads) | (req.is_write == self.want_writes)))
+                       for i, req in enumerate(self.requests)]
+               
+               stb = Signal()
+               inputs_perm = [[req.stb,
+                       req.a, req.ba,
+                       req.is_read, req.is_write, req.tag] for req in self.requests]
+               outputs_perm = [stb,
+                       self.cmd.a, self.cmd.ba,
+                       self.cmd.is_read, self.cmd.is_write, self.cmd.tag]
+               comb += multimux(rr.grant, inputs_perm, outputs_perm)
+               
+               inputs_filtered = [[req.cas_n, req.ras_n, req.we_n] for req in self.requests]
+               outputs_filtered = [self.cmd.cas_n, self.cmd.ras_n, self.cmd.we_n]
+               ms = multimux(rr.grant, inputs_filtered, outputs_filtered)
+               comb += [
+                       self.cmd.stb.eq(stb & ((self.cmd.is_read == self.want_reads) | (self.cmd.is_write == self.want_writes))),
+                       If(self.cmd.stb, *ms)
+               ]
+               
+               comb += [req.ack.eq(self.cmd.stb & self.cmd.ack & rr.grant == i)
+                       for i, req in enumerate(self.requests)]
+               comb.append(rr.ce.eq(self.cmd.ack))
+               
+               return Fragment(comb, sync) + rr.get_fragment()
+
+class _Steerer:
+       def __init__(self, commands, dfi):
+               self.commands = commands
+               self.dfi = dfi
+               
+               ncmd = len(self.commands)
+               nph = len(self.dfi.phases)
+               self.sel = [Signal(BV(bits_for(ncmd-1))) for i in range(nph)]
+       
+       def get_fragment(self):
+               comb = []
+               sync = []
+               def stb_and(cmd, attr):
+                       if not hasattr(cmd, "stb"):
+                               return Constant(0)
+                       else:
+                               return cmd.stb & getattr(cmd, attr)
+               inputs = [[cmd.a, cmd.ba,
+                       cmd.cas_n, cmd.ras_n,
+                       cmd.we_n, stb_and(cmd, "is_read"), stb_and(cmd, "is_write")]
+                       for cmd in self.commands]
+               for phase, sel in zip(self.dfi.phases, self.sel):
+                       comb += [
+                               phase.cke.eq(1),
+                               phase.cs_n.eq(0)
+                       ]
+                       outputs = [phase.address, phase.bank,
+                               phase.cas_n, phase.ras_n, phase.we_n,
+                               phase.rddata_en, phase.wrdata_en]
+                       sync += multimux(sel, inputs, outputs)
+               return Fragment(comb, sync)
+
+class _Datapath:
+       def __init__(self, timing_settings, command, dfi, hub):
+               self.timing_settings = timing_settings
+               self.command = command
+               self.dfi = dfi
+               self.hub = hub
+       
+       def get_fragment(self):
+               comb = []
+               sync = []
+               tagbits = self.hub.tag_call.bv.width
+               
+               rd_valid = Signal()
+               rd_tag = Signal(BV(tagbits))
+               wr_valid = Signal()
+               wr_tag = Signal(BV(tagbits))
+               comb += [
+                       self.hub.call.eq(rd_valid | wr_valid),
+                       If(wr_valid,
+                               self.hub.tag_call.eq(wr_tag)
+                       ).Else(
+                               self.hub.tag_call.eq(rd_tag)
+                       )
+               ]
+               
+               rd_valid_d = [Signal() for i in range(self.timing_settings.rd_delay)]
+               rd_tag_d = [Signal(BV(tagbits)) for i in range(self.timing_settings.rd_delay)]
+               for i in range(self.timing_settings.rd_delay):
+                       if i:
+                               sync += [
+                                       rd_valid_d[i].eq(rd_valid_d[i-1]),
+                                       rd_tag_d[i].eq(rd_tag_d[i-1])
+                               ]
+                       else:
+                               sync += [
+                                       rd_valid_d[i].eq(self.command.stb & self.command.ack & self.command.is_read),
+                                       rd_tag_d[i].eq(self.command.tag)
+                               ]               
+               comb += [
+                       rd_valid.eq(rd_valid_d[-1]),
+                       rd_tag.eq(rd_tag_d[-1]),
+                       wr_valid.eq(self.command.stb & self.command.ack & self.command.is_write),
+                       wr_tag.eq(self.command.tag),
+               ]
+               
+               all_rddata = [p.rddata for p in self.dfi.phases]
+               all_wrdata = [p.wrdata for p in self.dfi.phases]
+               all_wrdata_mask = [p.wrdata_mask for p in self.dfi.phases]
+               comb += [
+                       self.hub.dat_r.eq(Cat(*all_rddata)),
+                       Cat(*all_wrdata).eq(self.hub.dat_w),
+                       Cat(*all_wrdata_mask).eq(self.hub.dat_wm)
+               ]
+               
+               return Fragment(comb, sync)
+
 class Multiplexer:
        def __init__(self, phy_settings, geom_settings, timing_settings, bank_machines, refresher, dfi, hub):
-               pass
+               self.phy_settings = phy_settings
+               self.geom_settings = geom_settings
+               self.timing_settings = timing_settings
+               self.bank_machines = bank_machines
+               self.refresher = refresher
+               self.dfi = dfi
+               self.hub = hub
+               
+               assert(self.phy_settings.nphases == len(dfi.phases))
+               if self.phy_settings.nphases != 2:
+                       raise NotImplementedError("TODO: multiplexer only supports 2 phases")
        
        def get_fragment(self):
-               return Fragment()
+               comb = []
+               sync = []
+               
+               # Command choosing
+               requests = [bm.cmd for bm in self.bank_machines]
+               tagbits = self.hub.tag_call.bv.width
+               choose_cmd = _CommandChooser(requests, tagbits)
+               choose_req = _CommandChooser(requests, tagbits)
+               comb += [
+                       choose_cmd.want_reads.eq(0),
+                       choose_cmd.want_writes.eq(0)
+               ]
+               
+               # Command steering
+               nop = CommandRequest(self.geom_settings.mux_a, self.geom_settings.bank_a)
+               commands = [nop, choose_cmd.cmd, choose_req.cmd, self.refresher.cmd] # nop must be 1st
+               (STEER_NOP, STEER_CMD, STEER_REQ, STEER_REFRESH) = range(4)
+               steerer = _Steerer(commands, self.dfi)
+               
+               # Read/write turnaround
+               read_available = Signal()
+               write_available = Signal()
+               comb += [
+                       read_available.eq(optree("|", [req.stb & req.is_read for req in requests])),
+                       write_available.eq(optree("|", [req.stb & req.is_write for req in requests]))
+               ]
+               
+               def anti_starvation(timeout):
+                       en = Signal()
+                       max_time = Signal()
+                       if timeout:
+                               t = timeout - 1
+                               time = Signal(BV(bits_for(t)))
+                               comb.append(max_time.eq(time == 0))
+                               sync.append(
+                                       If(~en,
+                                               time.eq(t)
+                                       ).Elif(~max_time,
+                                               time.eq(time - 1)
+                                       )
+                               )
+                       else:
+                               comb.append(max_time.eq(0))
+                       return en, max_time
+               read_time_en, max_read_time = anti_starvation(self.timing_settings.read_time)
+               write_time_en, max_write_time = anti_starvation(self.timing_settings.write_time)
+               
+               # Refresh
+               refresh_w_ok = Signal()
+               t_unsafe_refresh = 2 + self.timing_settings.tWR - 1
+               unsafe_refresh_count = Signal(BV(bits_for(t_unsafe_refresh)))
+               comb.append(refresh_w_ok.eq(unsafe_refresh_count == 0))
+               sync += [
+                       If(choose_req.cmd.stb & choose_req.cmd.ack & choose_req.cmd.is_write,
+                               unsafe_refresh_count.eq(t_unsafe_refresh)
+                       ).Elif(~refresh_w_ok,
+                               unsafe_refresh_count.eq(unsafe_refresh_count-1)
+                       )
+               ]
+               # Reads cannot conflict with refreshes, since we have one idle cycle
+               # (all bank machines in refresh state) before the PRECHARGE ALL command
+               # from the refresher.
+               comb += [bm.refresh_req.eq(self.refresher.req)
+                       for bm in self.bank_machines]
+               comb.append(
+                       self.refresher.ack.eq(optree("&",
+                               [bm.refresh_gnt for bm in self.bank_machines]) \
+                               & refresh_w_ok)
+               )
+               
+               # Datapath
+               datapath = _Datapath(self.timing_settings, choose_req.cmd, self.dfi, self.hub)
+               
+               # Control FSM
+               fsm = FSM("READ", "WRITE", "REFRESH", delayed_enters=[
+                       ("RTW", "WRITE", math.ceil((self.timing_settings.CL+1)/2)),
+                       ("WTR", "READ", self.timing_settings.tWR)
+               ])
+               fsm.act(fsm.READ,
+                       read_time_en.eq(1),
+                       choose_req.want_reads.eq(1),
+                       choose_cmd.cmd.ack.eq(1),
+                       choose_req.cmd.ack.eq(1),
+                       steerer.sel[1-self.phy_settings.rdphase].eq(STEER_CMD),
+                       steerer.sel[self.phy_settings.rdphase].eq(STEER_REQ),
+                       If(write_available,
+                               # TODO: switch only after several cycles of ~read_available?
+                               If(~read_available | max_read_time, fsm.next_state(fsm.RTW))
+                       ),
+                       If(self.refresher.ack, fsm.next_state(fsm.REFRESH))
+               )
+               fsm.act(fsm.WRITE,
+                       write_time_en.eq(1),
+                       choose_req.want_writes.eq(1),
+                       choose_cmd.cmd.ack.eq(1),
+                       choose_req.cmd.ack.eq(1),
+                       steerer.sel[1-self.phy_settings.wrphase].eq(STEER_CMD),
+                       steerer.sel[self.phy_settings.wrphase].eq(STEER_REQ),
+                       If(read_available,
+                               If(~write_available | max_write_time, fsm.next_state(fsm.WTR))
+                       ),
+                       If(self.refresher.ack, fsm.next_state(fsm.REFRESH))
+               )
+               fsm.act(fsm.REFRESH,
+                       steerer.sel[0].eq(STEER_REFRESH),
+                       If(~self.refresher.req, fsm.next_state(fsm.READ))
+               )
+               
+               return Fragment(comb, sync) + \
+                       choose_cmd.get_fragment() + \
+                       choose_req.get_fragment() + \
+                       steerer.get_fragment() + \
+                       datapath.get_fragment() + \
+                       fsm.get_fragment()
index 8fbd2cfd7d6cb34d5ce58cd30c42329119af509a..9404a89e84d58a0901efe8189409e4480aae12df 100644 (file)
@@ -11,8 +11,8 @@ class Refresher:
                self.tRFC = tRFC
                
                self.req = Signal()
-               self.ack = Signal()
-               self.cmd_request = CommandRequest(a, ba)
+               self.ack = Signal() # 1st command 1 cycle after assertion of ack
+               self.cmd = CommandRequest(a, ba)
        
        def get_fragment(self):
                comb = []
@@ -23,22 +23,22 @@ class Refresher:
                seq_start = Signal()
                seq_done = Signal()
                sync += [
-                       self.cmd_request.a.eq(2**10),
-                       self.cmd_request.ba.eq(0),
-                       self.cmd_request.cas_n.eq(1),
-                       self.cmd_request.ras_n.eq(1),
-                       self.cmd_request.we_n.eq(1)
+                       self.cmd.a.eq(2**10),
+                       self.cmd.ba.eq(0),
+                       self.cmd.cas_n.eq(1),
+                       self.cmd.ras_n.eq(1),
+                       self.cmd.we_n.eq(1)
                ]
                sync += timeline(seq_start, [
-                       (0, [
-                               self.cmd_request.ras_n.eq(0),
-                               self.cmd_request.we_n.eq(0)
+                       (1, [
+                               self.cmd.ras_n.eq(0),
+                               self.cmd.we_n.eq(0)
                        ]),
-                       (self.tRP, [
-                               self.cmd_request.cas_n.eq(0),
-                               self.cmd_request.ras_n.eq(0)
+                       (1+self.tRP, [
+                               self.cmd.cas_n.eq(0),
+                               self.cmd.ras_n.eq(0)
                        ]),
-                       (self.tRP+self.tRFC-1, [
+                       (1+self.tRP+self.tRFC, [
                                seq_done.eq(1)
                        ])
                ])
diff --git a/top.py b/top.py
index 2cdf56b0b3e9839f9740411a90cc700fb4afde4f..528cf5ce16a607631dd56133739b82bae624823d 100644 (file)
--- a/top.py
+++ b/top.py
@@ -33,9 +33,16 @@ sdram_geom = asmicon.GeomSettings(
 sdram_timing = asmicon.TimingSettings(
        tRP=ns(15),
        tRCD=ns(15),
+       tWR=ns(15),
        tREFI=ns(7800, False),
        tRFC=ns(70),
-       slot_time=16
+       
+       CL=3,
+       rd_delay=4,
+
+       slot_time=16,
+       read_time=32,
+       write_time=16
 )
 
 def ddrphy_clocking(crg, phy):