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