import migen in litex/gen
[litex.git] / litex / soc / misoc / cores / sdram_model.py
1 # This file is Copyright (c) 2015 Florent Kermarrec <florent@enjoy-digital.fr>
2 # License: BSD
3
4 # SDRAM simulation PHY at DFI level
5 # tested with SDR/DDR/DDR2/LPDDR/DDR3
6 # TODO:
7 # - add $display support to Migen and manage timing violations?
8
9 from migen import *
10 from migen.fhdl.specials import *
11 from misoc.mem.sdram.phy.dfi import *
12 from misoc.mem import sdram
13
14
15 class Bank(Module):
16 def __init__(self, data_width, nrows, ncols, burst_length):
17 self.activate = Signal()
18 self.activate_row = Signal(max=nrows)
19 self.precharge = Signal()
20
21 self.write = Signal()
22 self.write_col = Signal(max=ncols)
23 self.write_data = Signal(data_width)
24 self.write_mask = Signal(data_width//8)
25
26 self.read = Signal()
27 self.read_col = Signal(max=ncols)
28 self.read_data = Signal(data_width)
29
30 ###
31 active = Signal()
32 row = Signal(max=nrows)
33
34 self.sync += \
35 If(self.precharge,
36 active.eq(0),
37 ).Elif(self.activate,
38 active.eq(1),
39 row.eq(self.activate_row)
40 )
41
42 self.specials.mem = mem = Memory(data_width, nrows*ncols//burst_length)
43 self.specials.write_port = write_port = mem.get_port(write_capable=True,
44 we_granularity=8)
45 self.specials.read_port = read_port = mem.get_port(async_read=True)
46 self.comb += [
47 If(active,
48 write_port.adr.eq(row*ncols | self.write_col),
49 write_port.dat_w.eq(self.write_data),
50 write_port.we.eq(Replicate(self.write, data_width//8) & ~self.write_mask),
51 If(self.read,
52 read_port.adr.eq(row*ncols | self.read_col),
53 self.read_data.eq(read_port.dat_r)
54 )
55 )
56 ]
57
58
59 class DFIPhase(Module):
60 def __init__(self, dfi, n):
61 phase = getattr(dfi, "p"+str(n))
62
63 self.bank = phase.bank
64 self.address = phase.address
65
66 self.wrdata = phase.wrdata
67 self.wrdata_mask = phase.wrdata_mask
68
69 self.rddata = phase.rddata
70 self.rddata_valid = phase.rddata_valid
71
72 self.activate = Signal()
73 self.precharge = Signal()
74 self.write = Signal()
75 self.read = Signal()
76
77 ###
78 self.comb += [
79 If(~phase.cs_n & ~phase.ras_n & phase.cas_n,
80 self.activate.eq(phase.we_n),
81 self.precharge.eq(~phase.we_n)
82 ),
83 If(~phase.cs_n & phase.ras_n & ~phase.cas_n,
84 self.write.eq(~phase.we_n),
85 self.read.eq(phase.we_n)
86 )
87 ]
88
89
90 class SDRAMPHYSim(Module):
91 def __init__(self, module, settings):
92 if settings.memtype in ["SDR"]:
93 burst_length = settings.nphases*1 # command multiplication*SDR
94 elif settings.memtype in ["DDR", "LPDDR", "DDR2", "DDR3"]:
95 burst_length = settings.nphases*2 # command multiplication*DDR
96
97 addressbits = module.geom_settings.addressbits
98 bankbits = module.geom_settings.bankbits
99 rowbits = module.geom_settings.rowbits
100 colbits = module.geom_settings.colbits
101
102 self.settings = settings
103 self.module = module
104
105 self.dfi = Interface(addressbits, bankbits, self.settings.dfi_databits, self.settings.nphases)
106
107 ###
108 nbanks = 2**bankbits
109 nrows = 2**rowbits
110 ncols = 2**colbits
111 data_width = self.settings.dfi_databits*self.settings.nphases
112
113 # DFI phases
114 phases = [DFIPhase(self.dfi, n) for n in range(self.settings.nphases)]
115 self.submodules += phases
116
117 # banks
118 banks = [Bank(data_width, nrows, ncols, burst_length) for i in range(nbanks)]
119 self.submodules += banks
120
121 # connect DFI phases to banks (cmds, write datapath)
122 for nb, bank in enumerate(banks):
123 # bank activate
124 activates = Signal(len(phases))
125 cases = {}
126 for np, phase in enumerate(phases):
127 self.comb += activates[np].eq(phase.activate)
128 cases[2**np] = [
129 bank.activate.eq(phase.bank == nb),
130 bank.activate_row.eq(phase.address)
131 ]
132 self.comb += Case(activates, cases)
133
134 # bank precharge
135 precharges = Signal(len(phases))
136 cases = {}
137 for np, phase in enumerate(phases):
138 self.comb += precharges[np].eq(phase.precharge)
139 cases[2**np] = [
140 bank.precharge.eq((phase.bank == nb) | phase.address[10])
141 ]
142 self.comb += Case(precharges, cases)
143
144 # bank writes
145 writes = Signal(len(phases))
146 cases = {}
147 for np, phase in enumerate(phases):
148 self.comb += writes[np].eq(phase.write)
149 cases[2**np] = [
150 bank.write.eq(phase.bank == nb),
151 bank.write_col.eq(phase.address)
152 ]
153 self.comb += Case(writes, cases)
154 self.comb += [
155 bank.write_data.eq(Cat(*[phase.wrdata for phase in phases])),
156 bank.write_mask.eq(Cat(*[phase.wrdata_mask for phase in phases]))
157 ]
158
159 # bank reads
160 reads = Signal(len(phases))
161 cases = {}
162 for np, phase in enumerate(phases):
163 self.comb += reads[np].eq(phase.read)
164 cases[2**np] = [
165 bank.read.eq(phase.bank == nb),
166 bank.read_col.eq(phase.address)
167 ]
168 self.comb += Case(reads, cases)
169
170 # connect banks to DFI phases (cmds, read datapath)
171 banks_read = Signal()
172 banks_read_data = Signal(data_width)
173 self.comb += [
174 banks_read.eq(optree("|", [bank.read for bank in banks])),
175 banks_read_data.eq(optree("|", [bank.read_data for bank in banks]))
176 ]
177 # simulate read latency
178 for i in range(self.settings.read_latency):
179 new_banks_read = Signal()
180 new_banks_read_data = Signal(data_width)
181 self.sync += [
182 new_banks_read.eq(banks_read),
183 new_banks_read_data.eq(banks_read_data)
184 ]
185 banks_read = new_banks_read
186 banks_read_data = new_banks_read_data
187
188 self.comb += [
189 Cat(*[phase.rddata_valid for phase in phases]).eq(banks_read),
190 Cat(*[phase.rddata for phase in phases]).eq(banks_read_data)
191 ]