work-in-progress on sdram opencores wrapper
[soc.git] / src / soc / bus / sdr_ctrl.py
1 #!/usr/bin/env python3
2 #
3 # SPDX-License-Identifier: LGPLv3+
4 # Copyright (C) 2022 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
5 # Sponsored by NLnet and NGI POINTER under EU Grants 871528 and 957073
6 # Part of the Libre-SOC Project.
7 #
8 # this is a wrapper around the opencores verilog uart16550 module
9
10 from nmigen import (Elaboratable, Cat, Module, Signal, ClockSignal, Instance,
11 ResetSignal, Record)
12
13 from nmigen_soc.wishbone.bus import Interface
14 from nmigen.cli import rtlil, verilog
15 import os
16
17 __all__ = ["SDRAM", "SDRAMConfig"]
18
19 """
20 class MT48LC16M16(SDRModule):
21 # geometry
22 nbanks = 4
23 nrows = 8192
24 ncols = 512
25 # timings
26 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
27 tWTR=(2, None),
28 tCCD=(1, None),
29 tRRD=(None, 15))
30 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
31 tRCD=20,
32 tWR=15,
33 tRFC=(None, 66),
34 tFAW=None,
35 tRAS=44)}
36 # for MT48LC16M16-75 part
37 comb += self.cfg.sdr_en.eq(1)
38 comb += self.cfg.sdr_mode_reg.eq(0x033)
39 comb += self.cfg.req_depth.eq(3) # max
40 comb += self.cfg.sdr_tras_d.eq(44) # Active to precharge delay
41 comb += self.cfg.sdr_trp_d.eq(20) # Precharge to active delay
42 comb += self.cfg.sdr_trcd_d.eq(20) # Active to R/W delay
43 comb += self.cfg.sdr_cas.eq(3) # CAS latency
44 comb += self.cfg.sdr_trcar_d.eq(66) # tRFC auto-refresh period
45 comb += self.cfg.sdr_twr_d.eq(15) # clock + 7.5ns
46 comb += self.cfg.sdr_rfsh.eq(0x100)
47 comb += self.cfg.sdr_rfmax.eq(6)
48 """
49
50
51 class SDRAMConfig(Record):
52 def __init__(self, refresh_timer_sz, refresh_row_count, name=None):
53 super().__init__(name=name, layout=[
54 # configuration parameters, these need to match the SDRAM IC datasheet
55 ('req_depth', 2), # max request accepted
56 ('sdr_en', 1), # Enable SDRAM controller
57 ('sdr_mode_reg', 13),
58 ('sdr_tras_d', 4), # Active to precharge delay
59 ('sdr_trp_d', 4), # Precharge to active delay
60 ('sdr_trcd_d', 4), # Active to R/W delay
61 ('sdr_cas', 3), # SDRAM CAS Latency
62 ('sdr_trcar_d', 4), # Auto-refresh period
63 ('sdr_twr_d', 4), # Write recovery delay
64 ('sdr_rfsh', refresh_timer_sz),
65 ('sdr_rfmax', refresh_row_count)
66 ])
67
68
69 class SDRAM(Elaboratable):
70 """SDRAM controller from opencores, nmigen wrapper. remember to call
71 SDRAM.add_verilog_source.
72
73 * the SDRAM IC will be accessible over the Wishbone Bus
74 * sdr_* signals must be wired to the IC
75 * cfg parameters must match those listed in the SDRAM IC's datasheet
76 """
77
78 def __init__(self, bus=None, features=None, name=None,
79 data_width=32, addr_width=26,
80 sdr_data_width=16,
81 cfg=None,
82 pins=None):
83 if name is not None:
84 name = "sdram"
85 self.data_width = data_width
86 self.sdr_data_width = sdr_data_width
87 self.addr_width = addr_width
88 self.refresh_timer_sz = 12
89 self.refresh_row_count = 3
90
91 # set up the wishbone bus
92 if features is None:
93 features = frozenset({'cti'})
94 if bus is None:
95 bus = Interface(addr_width=addr_width,
96 data_width=data_width,
97 features=features,
98 granularity=8,
99 name=name)
100 self.bus = bus
101 assert len(self.bus.dat_r) == data_width, \
102 "bus width must be %d" % data_width
103
104 byte_width = sdr_data_width // 8 # for individual byte masks/enables
105
106 # SDRAM signals
107 self.sdram_clk = Signal() # sdram phy clock
108 self.sdram_resetn = Signal(reset_less=True) # sdram reset (low)
109 self.sdr_cs_n = Signal() # chip select
110 self.sdr_cke = Signal() # clock-enable
111 self.sdr_ras_n = Signal() # read-address strobe
112 self.sdr_cas_n = Signal() # cas
113 self.sdr_we_n = Signal() # write-enable
114 self.sdr_dqm = Signal(byte_width) # data mask
115 self.sdr_ba = Signal(2) # bank enable
116 self.sdr_addr = Signal(13) # sdram address, 13 bits
117 # these combine to create a bi-direction inout, sdr_dq
118 # note, each bit of sdr_den_n covers a *byte* of sdr_din/sdr_dout
119 self.sdr_den_n = Signal(byte_width)
120 self.sdr_din = Signal(data_width)
121 self.sdr_dout = Signal(data_width)
122
123 # configuration parameters, these need to match the SDRAM IC datasheet
124 self.sdr_init_done = Signal() # Indicate SDRAM init Done
125 if cfg is None:
126 cfg = SDRAMConfig(self.refresh_timer_sz,
127 self.refresh_row_count, name="sdr_cfg")
128
129 # config and pins resource
130 self.pins = pins
131 self.cfg = cfg
132
133 @classmethod
134 def add_verilog_source(cls, verilog_src_dir, platform):
135 # add each of the verilog sources, needed for when doing platform.build
136 for fname in [ './core/sdrc_bank_ctl.v', './core/sdrc_bank_fsm.v',
137 './core/sdrc_bs_convert.v', './core/sdrc_core.v',
138 './core/sdrc_req_gen.v', './core/sdrc_xfr_ctl.v',
139 './core/sdrc_define.v',
140 './lib/async_fifo.v', './lib/sync_fifo.v',
141 './top/sdrc_top.v', './wb2sdrc/wb2sdrc.v',
142 ]:
143 # prepend the src directory to each filename, add its contents
144 fullname = os.path.join(verilog_src_dir, fname)
145 with open(fullname) as f:
146 platform.add_file(fullname, f)
147
148 def elaborate(self, platform):
149 m = Module()
150 comb = m.d.comb
151
152 # create definition of external verilog 16550 uart here, so that # nmigen understands I/O directions (defined by i_ and o_ prefixes)
153 bus = self.bus
154
155 params = {
156 # clock/reset (use DomainRenamer if needed)
157 'i_wb_clk_i' : ClockSignal(),
158 'i_wb_rst_i' : ResetSignal(),
159
160 # wishbone bus signals
161 'i_wb_adr_i' : bus.adr,
162 'i_wb_dat_i' : bus.dat_w,
163 'i_wb_sel_i' : bus.sel,
164 'o_wb_dat_o' : bus.dat_r,
165 'i_wb_we_i' : bus.we,
166 'i_wb_stb_i' : bus.stb,
167 'i_wb_cyc_i' : bus.cyc,
168 'o_wb_ack_o' : bus.ack,
169
170 # SDRAM signals
171 'i_sdram_clk' : self.sdram_clk,
172 'i_sdram_resetn' : self.sdram_resetn,
173 'o_sdr_cs_n' : self.sdr_cs_n,
174 'o_sdr_cke' : self.sdr_cke,
175 'o_sdr_ras_n' : self.sdr_ras_n,
176 'o_sdr_cas_n' : self.sdr_cas_n,
177 'o_sdr_we_n' : self.sdr_we_n,
178 'o_sdr_dqm' : self.sdr_dqm,
179 'o_sdr_ba' : self.sdr_ba,
180 'o_sdr_addr' : self.sdr_addr,
181 'o_sdr_den_n' : self.sdr_den_n,
182 'i_sdr_din' : self.sdr_din,
183 'o_sdr_dout' : self.sdr_dout,
184
185 # configuration parameters (from the SDRAM IC datasheet)
186 'o_sdr_init_done' : self.sdr_init_done ,
187 'i_cfg_req_depth' : self.cfg.req_depth ,
188 'i_cfg_sdr_en' : self.cfg.sdr_en ,
189 'i_cfg_sdr_mode_reg' : self.cfg.sdr_mode_reg ,
190 'i_cfg_sdr_tras_d' : self.cfg.sdr_tras_d ,
191 'i_cfg_sdr_trp_d' : self.cfg.sdr_trp_d ,
192 'i_cfg_sdr_trcd_d' : self.cfg.sdr_trcd_d ,
193 'i_cfg_sdr_cas' : self.cfg.sdr_cas ,
194 'i_cfg_sdr_trcar_d' : self.cfg.sdr_trcar_d ,
195 'i_cfg_sdr_twr_d' : self.cfg.sdr_twr_d ,
196 'i_cfg_sdr_rfsh' : self.cfg.sdr_rfsh ,
197 'i_cfg_sdr_rfmax' : self.cfg.sdr_rfmax,
198
199 # verilog parameters
200 'p_APP_AW' : self.addr_width, # Application Address Width
201 'p_APP_DW' : self.data_width, # Application Data Width
202 'p_APP_BW' : self.addr_width//8, # Application Byte Width
203 'p_APP_RW' : 9, # Application Request Width
204 'p_SDR_DW' : self.sdr_data_width, # SDR Data Width
205 'p_SDR_BW' : self.sdr_data_width//8, # SDR Byte Width
206 'p_dw' : self.data_width, # data width
207 'p_tw' : 8, # tag id width
208 'p_bl' : 9, # burst_length_width
209 }
210 m.submodules['sdrc_top'] = Instance("sdrc_top", **params)
211
212 return m
213
214 if self.pins is not None:
215 comb += self.pins.tx.eq(self.tx_o)
216 comb += self.rx_i.eq(self.pins.rx)
217
218 return m
219
220
221 def create_ilang(dut, ports, test_name):
222 vl = rtlil.convert(dut, name=test_name, ports=ports)
223 with open("%s.il" % test_name, "w") as f:
224 f.write(vl)
225
226 def create_verilog(dut, ports, test_name):
227 vl = verilog.convert(dut, name=test_name, ports=ports)
228 with open("%s.v" % test_name, "w") as f:
229 f.write(vl)
230
231
232 if __name__ == "__main__":
233 sdram = SDRAM(name="sdram", data_width=8)
234 create_ilang(sdram, [sdram.bus.cyc, sdram.bus.stb, sdram.bus.ack,
235 sdram.bus.dat_r, sdram.bus.dat_w, sdram.bus.adr,
236 sdram.bus.we, sdram.bus.sel,
237 sdram.sdram_clk, sdram.sdram_resetn,
238 sdram.sdr_cs_n, sdram.sdr_cke,
239 sdram.sdr_ras_n, sdram.sdr_cas_n, sdram.sdr_we_n,
240 sdram.sdr_dqm, sdram.sdr_ba, sdram.sdr_addr,
241 sdram.sdr_den_n, sdram.sdr_din, sdram.sdr_dout,
242 sdram.sdr_init_done, sdram.cfg.req_depth,
243 sdram.cfg.sdr_en, sdram.cfg.sdr_mode_reg,
244 sdram.cfg.sdr_tras_d, sdram.cfg.sdr_trp_d,
245 sdram.cfg.sdr_trcd_d, sdram.cfg.sdr_cas,
246 sdram.cfg.sdr_trcar_d, sdram.cfg.sdr_twr_d,
247 sdram.cfg.sdr_rfsh, sdram.cfg.sdr_rfmax,
248 ], "sdram")
249