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