2 from collections
import OrderedDict
5 from nmigen
.lib
.cdc
import ResetSynchronizer
6 from nmigen
.build
import *
8 from nmigen_soc
import wishbone
9 from nmigen_soc
.periph
import ConstantMap
11 from nmigen_stdio
.serial
import AsyncSerial
13 from nmigen_boards
.arty_a7
import ArtyA7_35Platform
14 from nmigen_boards
.ecpix5
import ECPIX545Platform
, ECPIX585Platform
16 from lambdasoc
.cpu
.minerva
import MinervaCPU
17 from lambdasoc
.periph
.intc
import GenericInterruptController
18 from lambdasoc
.periph
.serial
import AsyncSerialPeripheral
19 from lambdasoc
.periph
.sram
import SRAMPeripheral
20 from lambdasoc
.periph
.timer
import TimerPeripheral
21 from lambdasoc
.periph
.sdram
import SDRAMPeripheral
22 from lambdasoc
.periph
.eth
import EthernetMACPeripheral
24 from lambdasoc
.soc
.cpu
import CPUSoC
, BIOSBuilder
26 from lambdasoc
.cores
.pll
.lattice_ecp5
import PLL_LatticeECP5
27 from lambdasoc
.cores
.pll
.xilinx_7series
import PLL_Xilinx7Series
28 from lambdasoc
.cores
import litedram
, liteeth
29 from lambdasoc
.cores
.utils
import request_bare
31 from lambdasoc
.sim
.blackboxes
.serial
import AsyncSerial_Blackbox
32 from lambdasoc
.sim
.platform
import CXXRTLPlatform
35 __all__
= ["MinervaSoC"]
38 class _ClockResetGenerator(Elaboratable
):
39 def __init__(self
, *, sync_clk_freq
, with_sdram
, with_ethernet
):
40 if not isinstance(sync_clk_freq
, (int, float)) or sync_clk_freq
<= 0:
41 raise ValueError("Sync domain clock frequency must be a positive integer or float, "
43 .format(sync_clk_freq
))
44 self
.sync_clk_freq
= sync_clk_freq
45 self
.with_sdram
= bool(with_sdram
)
46 self
.with_ethernet
= bool(with_ethernet
)
48 def elaborate(self
, platform
):
52 ClockDomain("_ref", reset_less
=platform
.default_rst
is None, local
=True),
56 m
.d
.comb
+= ClockSignal("_ref").eq(platform
.request(platform
.default_clk
, 0).i
)
57 if platform
.default_rst
is not None:
58 m
.d
.comb
+= ResetSignal("_ref").eq(platform
.request(platform
.default_rst
, 0).i
)
60 # On the Arty A7, the DP83848 Ethernet PHY uses a 25 MHz reference clock.
61 if isinstance(platform
, ArtyA7_35Platform
) and self
.with_ethernet
:
62 m
.domains
+= ClockDomain("_eth_ref", local
=True)
63 m
.submodules
+= Instance("BUFGCE",
64 i_I
= ClockSignal("_eth_ref"),
65 i_CE
= ~
ResetSignal("_eth_ref"),
66 o_O
= platform
.request("eth_clk25", 0).o
,
69 # The LiteDRAM core provides its own PLL, which drives the litedram_user clock domain.
70 # We reuse this clock domain as the sync domain, in order to avoid CDC between LiteDRAM
71 # and the SoC interconnect.
73 m
.domains
+= ClockDomain("litedram_input")
74 m
.d
.comb
+= ClockSignal("litedram_input").eq(ClockSignal("_ref"))
75 if platform
.default_rst
is not None:
76 m
.d
.comb
+= ResetSignal("litedram_input").eq(ResetSignal("_ref"))
78 m
.domains
+= ClockDomain("litedram_user")
80 ClockSignal("sync").eq(ClockSignal("litedram_user")),
81 ResetSignal("sync").eq(ResetSignal("litedram_user")),
84 # On the Arty A7, we still use our own PLL to drive the Ethernet PHY reference clock.
85 if isinstance(platform
, ArtyA7_35Platform
) and self
.with_ethernet
:
86 eth_ref_pll_params
= PLL_Xilinx7Series
.Parameters(
88 i_freq
= platform
.default_clk_frequency
,
89 i_reset_less
= platform
.default_rst
is None,
90 o_domain
= "_eth_ref",
93 m
.submodules
.eth_ref_pll
= eth_ref_pll
= PLL_Xilinx7Series(eth_ref_pll_params
)
95 if platform
.default_rst
is not None:
96 eth_ref_pll_arst
= ~eth_ref_pll
.locked |
ResetSignal("_ref")
98 eth_ref_pll_arst
= ~eth_ref_pll
.locked
100 m
.submodules
+= ResetSynchronizer(eth_ref_pll_arst
, domain
="_eth_ref")
102 # In simulation mode, the sync clock domain is directly driven by the platform clock.
103 elif isinstance(platform
, CXXRTLPlatform
):
104 assert self
.sync_clk_freq
== platform
.default_clk_frequency
105 m
.d
.comb
+= ClockSignal("sync").eq(ClockSignal("_ref"))
106 if platform
.default_rst
is not None:
107 m
.d
.comb
+= ResetSignal("sync").eq(ResetSignal("_ref"))
109 # Otherwise, we use a PLL to drive the sync clock domain.
111 if isinstance(platform
, ArtyA7_35Platform
):
112 sync_pll_params
= PLL_Xilinx7Series
.Parameters(
114 i_freq
= platform
.default_clk_frequency
,
115 i_reset_less
= platform
.default_rst
is None,
117 o_freq
= self
.sync_clk_freq
,
119 if self
.with_ethernet
:
120 sync_pll_params
.add_secondary_output(domain
="_eth_ref", freq
=25e6
)
121 m
.submodules
.sync_pll
= sync_pll
= PLL_Xilinx7Series(sync_pll_params
)
122 elif isinstance(platform
, (ECPIX545Platform
, ECPIX585Platform
)):
123 sync_pll_params
= PLL_LatticeECP5
.Parameters(
125 i_freq
= platform
.default_clk_frequency
,
126 i_reset_less
= platform
.default_rst
is None,
128 o_freq
= self
.sync_clk_freq
,
130 m
.submodules
.sync_pll
= sync_pll
= PLL_LatticeECP5(sync_pll_params
)
134 if platform
.default_rst
is not None:
135 sync_pll_arst
= ~sync_pll
.locked |
ResetSignal("_ref")
137 sync_pll_arst
= ~sync_pll
.locked
139 m
.submodules
+= ResetSynchronizer(sync_pll_arst
, domain
="sync")
140 if isinstance(platform
, ArtyA7_35Platform
) and self
.with_ethernet
:
141 m
.submodules
+= ResetSynchronizer(sync_pll_arst
, domain
="_eth_ref")
146 class MinervaSoC(CPUSoC
, Elaboratable
):
160 if not isinstance(sync_clk_freq
, (int, float)) or sync_clk_freq
<= 0:
161 raise ValueError("Sync domain clock frequency must be a positive integer or float, "
163 .format(sync_clk_freq
))
164 self
.sync_clk_freq
= int(sync_clk_freq
)
166 if not isinstance(cpu_core
, MinervaCPU
):
167 raise TypeError("CPU core must be an instance of MinervaCPU, not {!r}"
171 self
._arbiter
= wishbone
.Arbiter(addr_width
=30, data_width
=32, granularity
=8,
172 features
={"cti", "bte", "err"})
173 self
._decoder
= wishbone
.Decoder(addr_width
=30, data_width
=32, granularity
=8,
174 features
={"cti", "bte", "err"})
176 self
._arbiter
.add(self
.cpu
.ibus
)
177 self
._arbiter
.add(self
.cpu
.dbus
)
179 self
.intc
= GenericInterruptController(width
=len(self
.cpu
.ip
))
181 self
.bootrom
= SRAMPeripheral(size
=bootrom_size
, writable
=False)
182 self
._decoder
.add(self
.bootrom
.bus
, addr
=bootrom_addr
)
184 self
.scratchpad
= SRAMPeripheral(size
=scratchpad_size
)
185 self
._decoder
.add(self
.scratchpad
.bus
, addr
=scratchpad_addr
)
187 self
.uart
= AsyncSerialPeripheral(core
=uart_core
)
188 self
._decoder
.add(self
.uart
.bus
, addr
=uart_addr
)
189 self
.intc
.add_irq(self
.uart
.irq
, index
=uart_irqno
)
191 self
.timer
= TimerPeripheral(width
=timer_width
)
192 self
._decoder
.add(self
.timer
.bus
, addr
=timer_addr
)
193 self
.intc
.add_irq(self
.timer
.irq
, index
=timer_irqno
)
200 def memory_map(self
):
201 return self
._decoder
.bus
.memory_map
205 return super().constants
.union(
206 SDRAM
= self
.sdram
.constant_map
if self
.sdram
is not None else None,
207 ETHMAC
= self
.ethmac
.constant_map
if self
.ethmac
is not None else None,
209 WITH_SDRAM
= self
.sdram
is not None,
210 WITH_ETHMAC
= self
.ethmac
is not None,
211 MEMTEST_ADDR_SIZE
= 8192,
212 MEMTEST_DATA_SIZE
= 8192,
218 assert not (self
._sdram
and self
.sram
)
219 return self
._sdram
or self
._sram
229 def add_sdram(self
, core
, *, addr
, cache_size
):
230 if self
.mainram
is not None:
231 raise AttributeError("Main RAM has already been set to {!r}".format(self
.mainram
))
232 if core
.config
.user_clk_freq
!= self
.sync_clk_freq
:
233 raise ValueError("LiteDRAM user domain clock frequency ({} MHz) must match sync "
234 "domain clock frequency ({} MHz)"
235 .format(core
.config
.user_clk_freq
/ 1e6
, self
.sync_clk_freq
/ 1e6
))
236 self
._sdram
= SDRAMPeripheral(core
=core
, cache_size
=cache_size
)
237 self
._decoder
.add(self
._sdram
.bus
, addr
=addr
)
239 def add_internal_sram(self
, *, addr
, size
):
240 if self
.mainram
is not None:
241 raise AttributeError("Main RAM has already been set to {!r}".format(self
.mainram
))
242 self
._sram
= SRAMPeripheral(size
=size
)
243 self
._decoder
.add(self
._sram
.bus
, addr
=addr
)
249 def add_ethmac(self
, core
, *, addr
, irqno
, local_ip
, remote_ip
):
250 if self
._ethmac
is not None:
251 raise AttributeError("Ethernet MAC has already been set to {!r}"
252 .format(self
._ethmac
))
253 self
._ethmac
= EthernetMACPeripheral(core
=core
, local_ip
=local_ip
, remote_ip
=remote_ip
)
254 self
._decoder
.add(self
._ethmac
.bus
, addr
=addr
)
255 self
.intc
.add_irq(self
._ethmac
.irq
, index
=irqno
)
257 def elaborate(self
, platform
):
260 m
.submodules
.crg
= _ClockResetGenerator(
261 sync_clk_freq
= self
.sync_clk_freq
,
262 with_sdram
= self
.sdram
is not None,
263 with_ethernet
= self
.ethmac
is not None,
266 m
.submodules
.cpu
= self
.cpu
267 m
.submodules
.arbiter
= self
._arbiter
268 m
.submodules
.decoder
= self
._decoder
269 m
.submodules
.uart
= self
.uart
270 m
.submodules
.timer
= self
.timer
271 m
.submodules
.intc
= self
.intc
272 m
.submodules
.bootrom
= self
.bootrom
273 m
.submodules
.scratchpad
= self
.scratchpad
275 if self
.sdram
is not None:
276 m
.submodules
.sdram
= self
.sdram
277 if self
.sram
is not None:
278 m
.submodules
.sram
= self
.sram
279 if self
.ethmac
is not None:
280 m
.submodules
.ethmac
= self
.ethmac
283 self
._arbiter
.bus
.connect(self
._decoder
.bus
),
284 self
.cpu
.ip
.eq(self
.intc
.ip
),
290 if __name__
== "__main__":
291 parser
= argparse
.ArgumentParser()
292 parser
.add_argument("--build-dir", type=str,
293 default
="build/minerva_soc",
294 help="local build directory (default: 'build/sdram_soc')")
295 parser
.add_argument("--platform", type=str,
296 choices
=("sim", "arty_a7", "ecpix5_45", "ecpix5_85"),
298 help="target platform")
299 parser
.add_argument("--sync-clk-freq", type=int,
301 help="SoC clock frequency, in MHz. (default: 75)")
302 parser
.add_argument("--with-sdram", action
="store_true",
304 parser
.add_argument("--internal-sram-size", type=int,
306 help="Internal RAM size, in bytes. Ignored if --with-sdram is provided. "
308 parser
.add_argument("--baudrate", type=int,
310 help="UART baudrate (default: 9600)")
311 parser
.add_argument("--with-ethernet", action
="store_true",
312 help="enable Ethernet")
313 parser
.add_argument("--local-ip", type=str,
314 default
="192.168.1.50",
315 help="Local IPv4 address (default: 192.168.1.50)")
316 parser
.add_argument("--remote-ip", type=str,
317 default
="192.168.1.100",
318 help="Remote IPv4 address (default: 192.168.1.100)")
319 args
= parser
.parse_args()
323 if args
.platform
== "sim":
324 platform
= CXXRTLPlatform()
325 elif args
.platform
== "arty_a7":
326 platform
= ArtyA7_35Platform()
327 elif args
.platform
== "ecpix5_45":
328 platform
= ECPIX545Platform()
329 elif args
.platform
== "ecpix5_85":
330 platform
= ECPIX585Platform()
337 if isinstance(platform
, CXXRTLPlatform
):
338 litedram_config
= litedram
.ECP5Config(
340 module_name
= "MT41K256M16",
343 input_clk_freq
= int(platform
.default_clk_frequency
),
344 user_clk_freq
= int(platform
.default_clk_frequency
),
345 init_clk_freq
= int(1e6
),
347 elif isinstance(platform
, ArtyA7_35Platform
):
348 litedram_config
= litedram
.Artix7Config(
352 module_name
= "MT41K128M16",
358 input_clk_freq
= int(platform
.default_clk_frequency
),
359 user_clk_freq
= int(args
.sync_clk_freq
* 1e6
),
360 iodelay_clk_freq
= int(200e6
),
362 elif isinstance(platform
, (ECPIX545Platform
, ECPIX585Platform
)):
363 litedram_config
= litedram
.ECP5Config(
365 module_name
= "MT41K256M16",
368 input_clk_freq
= int(platform
.default_clk_frequency
),
369 user_clk_freq
= int(args
.sync_clk_freq
* 1e6
),
370 init_clk_freq
= int(25e6
),
375 if isinstance(platform
, CXXRTLPlatform
):
378 litedram_pins
= request_bare(platform
, "ddr3", 0)
380 litedram_core
= litedram
.Core(litedram_config
, pins
=litedram_pins
)
381 litedram_core
.build(litedram
.Builder(), platform
, args
.build_dir
,
382 sim
=isinstance(platform
, CXXRTLPlatform
))
383 mainram_size
= litedram_core
.size
386 mainram_size
= args
.internal_sram_size
390 if args
.with_ethernet
:
391 if isinstance(platform
, CXXRTLPlatform
):
392 raise NotImplementedError("Ethernet is currently unsupported in simulation.")
393 elif isinstance(platform
, ArtyA7_35Platform
):
394 liteeth_config
= liteeth
.Artix7Config(
396 clk_freq
= int(25e6
),
398 elif isinstance(platform
, (ECPIX545Platform
, ECPIX585Platform
)):
399 liteeth_config
= liteeth
.ECP5Config(
401 clk_freq
= int(125e6
),
406 liteeth_pins
= request_bare(platform
, f
"eth_{liteeth_config.phy_iface}", 0)
407 liteeth_core
= liteeth
.Core(liteeth_config
, pins
=liteeth_pins
)
408 liteeth_core
.build(liteeth
.Builder(), platform
, args
.build_dir
)
414 if isinstance(platform
, CXXRTLPlatform
):
415 uart_core
= AsyncSerial_Blackbox(
420 uart_core
= AsyncSerial(
422 divisor
= int(args
.sync_clk_freq
* 1e6
// args
.baudrate
),
423 pins
= platform
.request("uart", 0),
428 if isinstance(platform
, CXXRTLPlatform
):
429 sync_clk_freq
= platform
.default_clk_frequency
431 sync_clk_freq
= int(args
.sync_clk_freq
* 1e6
)
434 sync_clk_freq
= sync_clk_freq
,
436 cpu_core
= MinervaCPU(
437 reset_address
= 0x00000000,
442 icache_base
= 0x40000000,
443 icache_limit
= 0x40000000 + mainram_size
,
448 dcache_base
= 0x40000000,
449 dcache_limit
= 0x40000000 + mainram_size
,
453 bootrom_addr
= 0x00000000,
454 bootrom_size
= 0x8000,
455 scratchpad_addr
= 0x00008000,
456 scratchpad_size
= 0x1000,
458 uart_addr
= 0x80000000,
459 uart_core
= uart_core
,
461 timer_addr
= 0x80001000,
467 soc
.add_sdram(litedram_core
, addr
=0x40000000, cache_size
=4096)
469 soc
.add_internal_sram(addr
=0x40000000, size
=args
.internal_sram_size
)
471 if args
.with_ethernet
:
472 soc
.add_ethmac(liteeth_core
, addr
=0x90000000, irqno
=2,
473 local_ip
=args
.local_ip
, remote_ip
=args
.remote_ip
)
475 soc
.build(build_dir
=args
.build_dir
, do_init
=True)
477 if isinstance(platform
, CXXRTLPlatform
):
478 platform
.build(soc
, build_dir
=args
.build_dir
, blackboxes
={
479 "lambdasoc.sim.blackboxes.serial": "serial_pty",
482 platform
.build(soc
, build_dir
=args
.build_dir
, do_program
=True)