Minicon: small SDRAM controller
[litex.git] / misoclib / lasmicon / bankmachine.py
1 from migen.fhdl.std import *
2 from migen.genlib.roundrobin import *
3 from migen.genlib.fsm import FSM, NextState
4 from migen.genlib.misc import optree
5 from migen.genlib.fifo import SyncFIFO
6
7 from misoclib.lasmicon.multiplexer import *
8
9 class _AddressSlicer:
10 def __init__(self, col_a, address_align):
11 self.col_a = col_a
12 self.address_align = address_align
13
14 def row(self, address):
15 split = self.col_a - self.address_align
16 if isinstance(address, int):
17 return address >> split
18 else:
19 return address[split:]
20
21 def col(self, address):
22 split = self.col_a - self.address_align
23 if isinstance(address, int):
24 return (address & (2**split - 1)) << self.address_align
25 else:
26 return Cat(Replicate(0, self.address_align), address[:split])
27
28 class BankMachine(Module):
29 def __init__(self, geom_settings, timing_settings, address_align, bankn, req):
30 self.refresh_req = Signal()
31 self.refresh_gnt = Signal()
32 self.cmd = CommandRequestRW(geom_settings.mux_a, geom_settings.bank_a)
33
34 ###
35
36 # Request FIFO
37 self.submodules.req_fifo = SyncFIFO([("we", 1), ("adr", flen(req.adr))], timing_settings.req_queue_size)
38 self.comb += [
39 self.req_fifo.din.we.eq(req.we),
40 self.req_fifo.din.adr.eq(req.adr),
41 self.req_fifo.we.eq(req.stb),
42 req.req_ack.eq(self.req_fifo.writable),
43
44 self.req_fifo.re.eq(req.dat_ack),
45 req.lock.eq(self.req_fifo.readable)
46 ]
47 reqf = self.req_fifo.dout
48
49 slicer = _AddressSlicer(geom_settings.col_a, address_align)
50
51 # Row tracking
52 has_openrow = Signal()
53 openrow = Signal(geom_settings.row_a)
54 hit = Signal()
55 self.comb += hit.eq(openrow == slicer.row(reqf.adr))
56 track_open = Signal()
57 track_close = Signal()
58 self.sync += [
59 If(track_open,
60 has_openrow.eq(1),
61 openrow.eq(slicer.row(reqf.adr))
62 ),
63 If(track_close,
64 has_openrow.eq(0)
65 )
66 ]
67
68 # Address generation
69 s_row_adr = Signal()
70 self.comb += [
71 self.cmd.ba.eq(bankn),
72 If(s_row_adr,
73 self.cmd.a.eq(slicer.row(reqf.adr))
74 ).Else(
75 self.cmd.a.eq(slicer.col(reqf.adr))
76 )
77 ]
78
79 # Respect write-to-precharge specification
80 precharge_ok = Signal()
81 t_unsafe_precharge = 2 + timing_settings.tWR - 1
82 unsafe_precharge_count = Signal(max=t_unsafe_precharge+1)
83 self.comb += precharge_ok.eq(unsafe_precharge_count == 0)
84 self.sync += [
85 If(self.cmd.stb & self.cmd.ack & self.cmd.is_write,
86 unsafe_precharge_count.eq(t_unsafe_precharge)
87 ).Elif(~precharge_ok,
88 unsafe_precharge_count.eq(unsafe_precharge_count-1)
89 )
90 ]
91
92 # Control and command generation FSM
93 fsm = FSM()
94 self.submodules += fsm
95 fsm.act("REGULAR",
96 If(self.refresh_req,
97 NextState("REFRESH")
98 ).Elif(self.req_fifo.readable,
99 If(has_openrow,
100 If(hit,
101 # NB: write-to-read specification is enforced by multiplexer
102 self.cmd.stb.eq(1),
103 req.dat_ack.eq(self.cmd.ack),
104 self.cmd.is_read.eq(~reqf.we),
105 self.cmd.is_write.eq(reqf.we),
106 self.cmd.cas_n.eq(0),
107 self.cmd.we_n.eq(~reqf.we)
108 ).Else(
109 NextState("PRECHARGE")
110 )
111 ).Else(
112 NextState("ACTIVATE")
113 )
114 )
115 )
116 fsm.act("PRECHARGE",
117 # Notes:
118 # 1. we are presenting the column address, A10 is always low
119 # 2. since we always go to the ACTIVATE state, we do not need
120 # to assert track_close.
121 If(precharge_ok,
122 self.cmd.stb.eq(1),
123 If(self.cmd.ack, NextState("TRP")),
124 self.cmd.ras_n.eq(0),
125 self.cmd.we_n.eq(0),
126 self.cmd.is_cmd.eq(1)
127 )
128 )
129 fsm.act("ACTIVATE",
130 s_row_adr.eq(1),
131 track_open.eq(1),
132 self.cmd.stb.eq(1),
133 self.cmd.is_cmd.eq(1),
134 If(self.cmd.ack, NextState("TRCD")),
135 self.cmd.ras_n.eq(0)
136 )
137 fsm.act("REFRESH",
138 self.refresh_gnt.eq(precharge_ok),
139 track_close.eq(1),
140 self.cmd.is_cmd.eq(1),
141 If(~self.refresh_req, NextState("REGULAR"))
142 )
143 fsm.delayed_enter("TRP", "ACTIVATE", timing_settings.tRP-1)
144 fsm.delayed_enter("TRCD", "REGULAR", timing_settings.tRCD-1)