comment about UARTResource for orangecrab
[ls2.git] / src / ls2.py
1 # Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
2 # Copyright (c) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3 # Copyright (C) 2022 Raptor Engineering, LLC <support@raptorengineering.com>
4 #
5 # Based on code from LambaConcept, from the gram example which is BSD-2-License
6 # https://github.com/jeanthom/gram/tree/master/examples
7 #
8 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
9 # under EU Grants 871528 and 957073, under the LGPLv3+ License
10
11 from nmigen import (Module, Elaboratable, DomainRenamer, Record,
12 Signal, Cat, Const, ClockSignal, ResetSignal)
13 from nmigen.build.dsl import Attrs
14 from nmigen.cli import verilog
15 from nmigen.lib.cdc import ResetSynchronizer
16 from nmigen_soc import wishbone, memory
17 from nmigen_soc.memory import MemoryMap
18 from nmigen.utils import log2_int
19 from nmigen_boards.resources.interface import UARTResource
20 from nmigen_stdio.serial import AsyncSerial
21
22 # HyperRAM
23 from nmigen_boards.resources.memory import HyperRAMResource
24 from lambdasoc.periph.hyperram import HyperRAM, HyperRAMPads, HyperRAMPHY
25
26 from lambdasoc.periph.event import IRQLine
27 from lambdasoc.periph.intc import GenericInterruptController
28 from lambdasoc.periph.sram import SRAMPeripheral
29 from lambdasoc.periph.timer import TimerPeripheral
30 from lambdasoc.periph import Peripheral
31 from lambdasoc.soc.base import SoC
32 from soc.bus.uart_16550 import UART16550 # opencores 16550 uart
33 from soc.bus.tercel import Tercel # SPI XIP master
34 from soc.bus.opencores_ethmac import EthMAC # OpenCores 10/100 Ethernet MAC
35 from soc.bus.external_core import ExternalCore # external libresoc/microwatt
36 from soc.bus.wb_downconvert import WishboneDownConvert
37 from soc.bus.wb_async import WBAsyncBridge
38 from soc.bus.syscon import MicrowattSYSCON
39 from soc.interrupts.xics import XICS_ICP, XICS_ICS
40
41 # DDR3
42 from gram.common import (PhySettings, get_cl_cw, get_sys_latency,
43 get_sys_phases,)
44 from gram.core import gramCore
45 from gram.phy.ecp5ddrphy import ECP5DDRPHY
46 from gram.phy.fakephy import FakePHY, SDRAM_VERBOSE_STD, SDRAM_VERBOSE_DBG
47 from gram.modules import MT41K256M16, MT41K64M16
48 from gram.frontend.wishbone import gramWishbone
49
50 # SPI / Ethernet MAC
51 from nmigen.build import Resource
52 from nmigen.build import Subsignal
53 from nmigen.build import Pins
54
55 # Board (and simulation) platforms
56 from nmigen_boards.versa_ecp5 import VersaECP5Platform
57 from nmigen_boards.versa_ecp5 import VersaECP5Platform85 # custom board
58 from nmigen_boards.ulx3s import ULX3S_85F_Platform
59 from nmigen_boards.arty_a7 import ArtyA7_100Platform
60 from nmigen_boards.test.blinky import Blinky
61 from nmigen_boards.orangecrab_r0_2 import OrangeCrabR0_2_85k_Platform
62 from icarusversa import IcarusVersaPlatform
63 # Clock-Reset Generator (works for all ECP5 platforms)
64 from ecp5_crg import ECP5CRG
65 from arty_crg import ArtyA7CRG
66
67 import sys
68 import os
69
70 def sim_ddr3_settings(clk_freq=100e6):
71 tck = 2/(2*2*clk_freq)
72 nphases = 2
73 databits = 16
74 nranks = 1
75 addressbits = 14
76 bankbits = 3
77 cl, cwl = get_cl_cw("DDR3", tck)
78 cl_sys_latency = get_sys_latency(nphases, cl)
79 cwl_sys_latency = get_sys_latency(nphases, cwl)
80 rdcmdphase, rdphase = get_sys_phases(nphases, cl_sys_latency, cl)
81 wrcmdphase, wrphase = get_sys_phases(nphases, cwl_sys_latency, cwl)
82 return PhySettings(
83 phytype="ECP5DDRPHY",
84 memtype="DDR3",
85 databits=databits,
86 dfi_databits=4*databits,
87 nranks=nranks,
88 nphases=nphases,
89 rdphase=rdphase,
90 wrphase=wrphase,
91 rdcmdphase=rdcmdphase,
92 wrcmdphase=wrcmdphase,
93 cl=cl,
94 cwl=cwl,
95 read_latency=2 + cl_sys_latency + 2 + log2_int(4//nphases) + 4,
96 write_latency=cwl_sys_latency
97 )
98
99
100 class WB64to32Convert(Elaboratable):
101 """Microwatt IO wishbone slave 64->32 bits converter
102
103 For timing reasons, this adds a one cycle latch on the way both
104 in and out. This relaxes timing and routing pressure on the "main"
105 memory bus by moving all simple IOs to a slower 32-bit bus.
106
107 This implementation is rather dumb at the moment, no stash buffer,
108 so we stall whenever that latch is busy. This can be improved.
109 """
110 def __init__(self, master, slave):
111 self.master = master
112 self.slave = slave
113
114 def elaborate(self, platform):
115 m = Module()
116 comb, sync = m.d.comb, m.d.sync
117 master, slave = self.master, self.slave
118
119 has_top = Signal()
120 has_top_r = Signal()
121 has_bot = Signal()
122
123 with m.FSM() as fsm:
124 with m.State("IDLE"):
125 # Clear ACK (and has_top_r) in case it was set
126 sync += master.ack.eq(0)
127 sync += has_top_r.eq(0)
128
129 # Do we have a cycle ?
130 with m.If(master.cyc & master.stb):
131 # Stall master until we are done, we are't (yet) pipelining
132 # this, it's all slow IOs.
133 sync += master.stall.eq(1)
134
135 # Start cycle downstream
136 sync += slave.cyc.eq(1)
137 sync += slave.stb.eq(1)
138
139 # Do we have a top word and/or a bottom word ?
140 comb += has_top.eq(master.sel[4:].bool())
141 comb += has_bot.eq(master.sel[:4].bool())
142 # record the has_top flag for the next FSM state
143 sync += has_top_r.eq(has_top)
144
145 # Copy write enable to IO out, copy address as well,
146 # LSB is set later based on HI/LO
147 sync += slave.we.eq(master.we)
148 sync += slave.adr.eq(Cat(0, master.adr))
149
150 # If we have a bottom word, handle it first, otherwise
151 # send the top word down. XXX Split the actual mux out
152 # and only generate a control signal.
153 with m.If(has_bot):
154 with m.If(master.we):
155 sync += slave.dat_w.eq(master.dat_w[:32])
156 sync += slave.sel.eq(master.sel[:4])
157
158 # Wait for ack on BOTTOM half
159 m.next = "WAIT_ACK_BOT"
160
161 with m.Else():
162 with m.If(master.we):
163 sync += slave.dat_w.eq(master.dat_w[32:])
164 sync += slave.sel.eq(master.sel[4:])
165
166 # Bump LSB of address
167 sync += slave.adr[0].eq(1)
168
169 # Wait for ack on TOP half
170 m.next = "WAIT_ACK_TOP"
171
172
173 with m.State("WAIT_ACK_BOT"):
174 # If we aren't stalled by the device, clear stb
175 if hasattr(slave, "stall"):
176 with m.If(~slave.stall):
177 sync += slave.stb.eq(0)
178
179 # Handle ack
180 with m.If(slave.ack):
181 # If it's a read, latch the data
182 with m.If(~slave.we):
183 sync += master.dat_r[:32].eq(slave.dat_r)
184
185 # Do we have a "top" part as well ?
186 with m.If(has_top_r):
187 # Latch data & sel
188 with m.If(master.we):
189 sync += slave.dat_w.eq(master.dat_w[32:])
190 sync += slave.sel.eq(master.sel[4:])
191
192 # Bump address and set STB
193 sync += slave.adr[0].eq(1)
194 sync += slave.stb.eq(1)
195
196 # Wait for new ack
197 m.next = "WAIT_ACK_TOP"
198
199 with m.Else():
200 # We are done, ack up, clear cyc downstram
201 sync += slave.cyc.eq(0)
202 sync += slave.stb.eq(0)
203
204 # And ack & unstall upstream
205 sync += master.ack.eq(1)
206 if hasattr(master , "stall"):
207 sync += master.stall.eq(0)
208
209 # Wait for next one
210 m.next = "IDLE"
211
212 with m.State("WAIT_ACK_TOP"):
213 # If we aren't stalled by the device, clear stb
214 if hasattr(slave, "stall"):
215 with m.If(~slave.stall):
216 sync += slave.stb.eq(0)
217
218 # Handle ack
219 with m.If(slave.ack):
220 # If it's a read, latch the data
221 with m.If(~slave.we):
222 sync += master.dat_r[32:].eq(slave.dat_r)
223
224 # We are done, ack up, clear cyc downstram
225 sync += slave.cyc.eq(0)
226 sync += slave.stb.eq(0)
227
228 # And ack & unstall upstream
229 sync += master.ack.eq(1)
230 if hasattr(master, "stall"):
231 sync += master.stall.eq(0)
232
233 # Wait for next one
234 m.next = "IDLE"
235
236 return m
237
238
239 class DDR3SoC(SoC, Elaboratable):
240 def __init__(self, *,
241 fpga,
242 dram_cls=None,
243 uart_pins=None, spi_0_pins=None, ethmac_0_pins=None,
244 ddr_pins=None, ddrphy_addr=None,
245 dramcore_addr=None, ddr_addr=None,
246 fw_addr=0x0000_0000, firmware=None,
247 uart_addr=None, uart_irqno=0,
248 spi0_addr=None, spi0_cfg_addr=None,
249 eth0_cfg_addr=None, eth0_irqno=None,
250 hyperram_addr=None,
251 hyperram_pins=None,
252 xics_icp_addr=None, xics_ics_addr=None,
253 clk_freq=50e6,
254 dram_clk_freq=None,
255 add_cpu=True):
256
257 # wishbone routing is as follows:
258 #
259 # SoC
260 # +--+--+
261 # | |
262 # ibus dbus
263 # | |
264 # +--+--+
265 # |
266 # 64to32DownCvt
267 # |
268 # arbiter------------------------------------------------------+
269 # | |
270 # +---decoder----+--------+---------------+-------------+--------+ |
271 # | | | | | | | |
272 # | | | WBAsyncBridge | | | |
273 # | | | | | | | |
274 # uart XICS CSRs DRAM XIP SPI HyperRAM EthMAC
275
276 # set up wishbone bus arbiter and decoder. arbiter routes,
277 # decoder maps local-relative addressed satellites to global addresses
278 self._arbiter = wishbone.Arbiter(addr_width=30, data_width=32,
279 granularity=8,
280 features={"cti", "bte", "stall"})
281 self._decoder = wishbone.Decoder(addr_width=30, data_width=32,
282 granularity=8,
283 features={"cti", "bte", "stall"})
284
285 # default firmware name
286 if firmware is None:
287 firmware = "firmware/main.bin"
288
289 # set up clock request generator
290 pod_bits = 25
291 if fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim', 'ulx3s',
292 'orangecrab']:
293 if fpga in ['isim']:
294 pod_bits = 6
295 self.crg = ECP5CRG(clk_freq, dram_clk_freq=dram_clk_freq,
296 pod_bits=pod_bits)
297 if fpga in ['arty_a7']:
298 self.crg = ArtyA7CRG(clk_freq)
299
300 self.dram_clk_freq = dram_clk_freq
301 if self.dram_clk_freq is None:
302 self.dram_clk_freq = clk_freq
303
304 # set up CPU, with 64-to-32-bit downconverters
305 if add_cpu:
306 self.cpu = ExternalCore(name="ext_core")
307 cvtdbus = wishbone.Interface(addr_width=30, data_width=32,
308 granularity=8, features={'stall'})
309 cvtibus = wishbone.Interface(addr_width=30, data_width=32,
310 granularity=8, features={'stall'})
311 self.dbusdowncvt = WB64to32Convert(self.cpu.dbus, cvtdbus)
312 self.ibusdowncvt = WB64to32Convert(self.cpu.ibus, cvtibus)
313 self._arbiter.add(cvtibus) # I-Cache Master
314 self._arbiter.add(cvtdbus) # D-Cache Master. TODO JTAG master
315 self.cvtibus = cvtibus
316 self.cvtdbus = cvtdbus
317
318 # CPU interrupt controller, needs stall to be added, also
319 # compat with wishbone.Interface
320 self.intc = GenericInterruptController(width=len(self.cpu.irq))
321 self.xics_icp = icp = XICS_ICP()
322 self.xics_ics = ics = XICS_ICS()
323 self.int_level_i = self.xics_ics.int_level_i
324
325 self.pbus = pbus = wishbone.Interface(name="xics_icp_bus",
326 addr_width=6, data_width=32,
327 granularity=8, features={'stall'})
328 self.sbus = sbus = wishbone.Interface(name="xics_ics_bus",
329 addr_width=10, data_width=32,
330 granularity=8, features={'stall'})
331 pmap = MemoryMap(addr_width=8, data_width=8, name="icp_map")
332 pbus.memory_map = pmap
333 self._decoder.add(pbus, addr=xics_icp_addr) # ICP addr
334
335 smap = MemoryMap(addr_width=12, data_width=8, name="ics_map")
336 sbus.memory_map = smap
337 self._decoder.add(sbus, addr=xics_ics_addr) # ICP addr
338
339
340 # SRAM (but actually a ROM, for firmware)
341 if fw_addr is not None:
342 print ("fw at address %x" % fw_addr)
343 sram_width = 32
344 self.bootmem = SRAMPeripheral(size=0x8000, data_width=sram_width,
345 writable=True)
346 if firmware is not None:
347 with open(firmware, "rb") as f:
348 words = iter(lambda: f.read(sram_width // 8), b'')
349 bios = [int.from_bytes(w, "little") for w in words]
350 self.bootmem.init = bios
351 self._decoder.add(self.bootmem.bus, addr=fw_addr) # ROM at fw_addr
352
353 # System Configuration info
354 # offset executable ELF payload at 6 megabyte offset (2<<20)
355 spi_offset = 2<<20 if (spi_0_pins is not None) else None
356 dram_offset = ddr_addr if (ddr_pins is not None) else None
357 self.syscon = MicrowattSYSCON(sys_clk_freq=clk_freq,
358 mem_clk_freq=self.dram_clk_freq,
359 has_uart=(uart_pins is not None),
360 spi_offset=spi_offset,
361 dram_addr=dram_offset)
362 self._decoder.add(self.syscon.bus, addr=0xc0000000) # at 0xc000_0000
363
364 if False:
365 # SRAM (read-writeable BRAM)
366 self.ram = SRAMPeripheral(size=4096)
367 self._decoder.add(self.ram.bus, addr=0x8000000) # at 0x8000_0000
368
369 # UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
370 if uart_pins is not None:
371 # sigh actual UART in microwatt is 8-bit
372 self.uart_irq = IRQLine()
373 self.uart = UART16550(data_width=8, pins=uart_pins,
374 features={'stall'},
375 irq=self.uart_irq)
376 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
377 # strictly speaking this is a nmigen-soc "sparse" arrangement
378 # which should be handled by MemoryMap, but needs investigation
379 cvtuartbus = wishbone.Interface(addr_width=5, data_width=32,
380 granularity=8,
381 features={'stall'})
382 umap = MemoryMap(addr_width=7, data_width=8, name="uart_map")
383 cvtuartbus.memory_map = umap
384 self._decoder.add(cvtuartbus, addr=uart_addr) # 16550 UART addr
385 self.cvtuartbus = cvtuartbus
386 self.intc.add_irq(self.uart.irq, index=uart_irqno)
387
388 # SDRAM module using opencores sdr_ctrl
389 """
390 class MT48LC16M16(SDRModule):
391 # geometry
392 nbanks = 4
393 nrows = 8192
394 ncols = 512
395 # timings
396 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
397 tWTR=(2, None),
398 tCCD=(1, None),
399 tRRD=(None, 15))
400 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
401 tRCD=20,
402 tWR=15,
403 tRFC=(None, 66),
404 tFAW=None,
405 tRAS=44)}
406 """
407
408 # DRAM Module. first, create the (triple) modules:
409 # * DDR PHY
410 # * gram Core: presents PHY with a DFI Interface
411 # * gram Bone (aka gram-with-wishbone) connects wishbone to DFI
412 # from there it gets a little complicated because of supporting
413 # several options: simulation, synchronous, and asynchronous clocks.
414 # dram_clk_freq can *never* be set equal to clk_freq, if it is,
415 # it's assumed to be synchronous, and the dram Domains need renaming
416
417 if ddr_pins is not None: # or fpga == 'sim':
418 ddrmodule = dram_cls(self.dram_clk_freq, "1:2") # match DDR3 P/N
419
420 # remap both the sync domain (wherever it occurs) and
421 # the sync2x domain, if dram frequency is specified and
422 # not equal to the core clock
423 drs = None
424 if dram_clk_freq is not None or fpga == 'sim':
425 drs = lambda x: x
426 else:
427 drs = DomainRenamer({"sync": "dramsync",
428 "sync2x": "dramsync2x"})
429
430 features = set()
431 if dram_clk_freq is None:
432 features.add("stall")
433
434 # create the PHY (fake one for sim)
435 if fpga == 'sim':
436 settings = sim_ddr3_settings(self.dram_clk_freq)
437 self.ddrphy = FakePHY(module=ddrmodule,
438 settings=settings,
439 verbosity=SDRAM_VERBOSE_DBG,
440 clk_freq=self.dram_clk_freq)
441 else:
442 self.ddrphy = drs(ECP5DDRPHY(ddr_pins,
443 #features=features,
444 sys_clk_freq=self.dram_clk_freq))
445
446 # create the core (bridge from PHY to DFI)
447 dramcore = gramCore(phy=self.ddrphy,
448 geom_settings=ddrmodule.geom_settings,
449 timing_settings=ddrmodule.timing_settings,
450 #features=features,
451 clk_freq=self.dram_clk_freq)
452 self.dramcore = drs(dramcore)
453
454 # create the wishbone presentation (wishbone to DFI)
455 drambone = gramWishbone(dramcore, features=features)
456 self.drambone = drs(drambone)
457
458 # this is the case where sys_clk === dram_clk. no ASync Bridge
459 # needed, so just let the phy core and wb-dfi be connected
460 # directly to WB decoder. both are running in "sync" domain
461 # (because of the DomainRenamer, above)
462
463 if ddr_pins is not None and dram_clk_freq is None:
464 self.ddrphy_bus = self.ddrphy.bus
465 self.dramcore_bus = self.dramcore.bus
466 self.drambone_bus = self.drambone.bus
467
468 # this covers the case where sys_clk != dram_clk: three separate
469 # ASync Bridges are constructed (!) and the interface that's to
470 # be wired to the WB decoder is the async bus because that's running
471 # in the "sync" domain.
472
473 if ddr_pins is not None and dram_clk_freq is not None:
474 # Set up Wishbone asynchronous bridge
475 pabus = wishbone.Interface(addr_width=self.ddrphy.bus.addr_width,
476 data_width=self.ddrphy.bus.data_width,
477 granularity=self.ddrphy.bus.granularity,
478 features={'stall'})
479 self.ddrphy_bus = pabus
480 self.ddrphy_bus.memory_map = self.ddrphy.bus.memory_map
481
482 pabr = WBAsyncBridge(master_bus=self.ddrphy_bus,
483 slave_bus=self.ddrphy.bus,
484 master_clock_domain=None,
485 slave_clock_domain="dramsync",
486 address_width=self.ddrphy.bus.addr_width,
487 data_width=self.ddrphy.bus.data_width,
488 granularity=self.ddrphy.bus.granularity,
489 master_features={'stall'})
490 self.ddrphy_async_br = pabr
491
492 # Set up Wishbone asynchronous bridge
493 dab = wishbone.Interface(addr_width=self.dramcore.bus.addr_width,
494 data_width=self.dramcore.bus.data_width,
495 granularity=self.dramcore.bus.granularity,
496 features={'stall'})
497 self.dramcore_bus = dab
498 self.dramcore_bus.memory_map = self.dramcore.bus.memory_map
499
500 dac = WBAsyncBridge(master_bus=self.dramcore_bus,
501 slave_bus=self.dramcore.bus,
502 master_clock_domain=None,
503 slave_clock_domain="dramsync",
504 address_width=self.dramcore.bus.addr_width,
505 data_width=self.dramcore.bus.data_width,
506 granularity=self.dramcore.bus.granularity,
507 master_features={'stall'})
508 self.dramcore_async_br = dac
509
510 # Set up Wishbone asynchronous bridge
511 bab = wishbone.Interface(addr_width=self.drambone.bus.addr_width,
512 data_width=self.drambone.bus.data_width,
513 granularity=self.drambone.bus.granularity,
514 features={'stall'})
515 self.drambone_bus = bab
516 self.drambone_bus.memory_map = self.drambone.bus.memory_map
517
518 bab = WBAsyncBridge(master_bus=self.drambone_bus,
519 slave_bus=self.drambone.bus,
520 master_clock_domain=None,
521 slave_clock_domain="dramsync",
522 address_width=self.drambone.bus.addr_width,
523 data_width=self.drambone.bus.data_width,
524 granularity=self.drambone.bus.granularity,
525 master_features={'stall'})
526 self.drambone_async_br = bab
527
528 if ddr_pins is not None:
529 # Add wishbone decoders
530 self._decoder.add(self.dramcore_bus, addr=dramcore_addr)
531 self._decoder.add(self.drambone_bus, addr=ddr_addr)
532 self._decoder.add(self.ddrphy_bus, addr=ddrphy_addr)
533
534 # additional SRAM at address if DRAM is not also at 0x0
535 # (TODO, check Flash, and HyperRAM as well)
536 if (ddr_pins is None or ddr_addr != 0x0) and fw_addr != 0:
537 print ("SRAM 0x8000 at address 0x0")
538 sram_width = 32
539 self.sram = SRAMPeripheral(size=0x8000,
540 data_width=sram_width,
541 writable=True)
542 self._decoder.add(self.sram.bus, addr=0x0) # RAM at 0x0
543
544 # SPI controller
545 if spi_0_pins is not None and fpga in ['sim',
546 'isim',
547 'rcs_arctic_tern_bmc_card',
548 'versa_ecp5',
549 'versa_ecp5_85',
550 'arty_a7']:
551 # The Lattice ECP5 devices require special handling on the
552 # dedicated SPI clock line, which is shared with the internal
553 # SPI controller used for FPGA bitstream loading.
554 spi0_is_lattice_ecp5_clk = False
555 if fpga in ['versa_ecp5',
556 'versa_ecp5_85',
557 'rcs_arctic_tern_bmc_card',
558 'isim']:
559 spi0_is_lattice_ecp5_clk = True
560
561 # Tercel contains two independent Wishbone regions, a
562 # configuration region and the direct API access region,
563 # Set the SPI 0 access region to 16MB, as the FPGA
564 # bitstream Flash device is unlikely to be larger than this.
565 # The main SPI Flash (SPI 1) should be set to at
566 # least 28 bits (256MB) to allow the use of large 4BA devices.
567 self.spi0 = Tercel(data_width=32, spi_region_addr_width=24,
568 adr_offset=spi0_addr,
569 features={'stall'},
570 clk_freq=clk_freq,
571 pins=spi_0_pins,
572 lattice_ecp5_usrmclk=spi0_is_lattice_ecp5_clk)
573 self._decoder.add(self.spi0.bus, addr=spi0_addr)
574 self._decoder.add(self.spi0.cfg_bus, addr=spi0_cfg_addr)
575
576 # Ethernet MAC
577 if ethmac_0_pins is not None and fpga in ['versa_ecp5',
578 'versa_ecp5_85',
579 'isim']:
580 self.eth_irq = IRQLine()
581 # The OpenCores Ethernet MAC contains two independent Wishbone
582 # interfaces, a slave (configuration) interface and a master (DMA)
583 # interface.
584 self.eth0 = EthMAC(pins=ethmac_0_pins, irq=self.eth_irq)
585 self._arbiter.add(self.eth0.master_bus)
586 self._decoder.add(self.eth0.slave_bus, addr=eth0_cfg_addr)
587 self.intc.add_irq(self.eth0.irq, index=eth0_irqno)
588
589 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
590 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
591 # moment
592 if hyperram_pins is not None:
593 self.hyperram = HyperRAM(io=hyperram_pins, phy_kls=HyperRAMPHY,
594 features={'stall'},
595 latency=7) # Winbond W956D8MBYA
596 self._decoder.add(self.hyperram.bus, addr=hyperram_addr)
597
598 self.memory_map = self._decoder.bus.memory_map
599
600 self.clk_freq = clk_freq
601 self.fpga = fpga
602
603 def elaborate(self, platform):
604 m = Module()
605 comb = m.d.comb
606
607 # add the peripherals and clock-reset-generator
608 if platform is not None and hasattr(self, "crg"):
609 m.submodules.sysclk = self.crg
610
611 if hasattr(self, "sram"):
612 m.submodules.sram = self.sram
613 if hasattr(self, "bootmem"):
614 m.submodules.bootmem = self.bootmem
615 m.submodules.syscon = self.syscon
616 if hasattr(self, "ram"):
617 m.submodules.ram = self.ram
618 if hasattr(self, "uart"):
619 m.submodules.uart = self.uart
620 comb += self.uart.cts_i.eq(1)
621 comb += self.uart.dsr_i.eq(1)
622 comb += self.uart.ri_i.eq(0)
623 comb += self.uart.dcd_i.eq(1)
624 # sigh connect up the wishbone bus manually to deal with
625 # the mis-match on the data. nmigen-soc "sparse" MemoryMap
626 # should be able to deal with this. TODO, investigate
627 uartbus = self.uart.bus
628 comb += uartbus.adr.eq(self.cvtuartbus.adr)
629 comb += uartbus.stb.eq(self.cvtuartbus.stb)
630 comb += uartbus.cyc.eq(self.cvtuartbus.cyc)
631 comb += uartbus.sel.eq(self.cvtuartbus.sel)
632 comb += uartbus.we.eq(self.cvtuartbus.we)
633 comb += uartbus.dat_w.eq(self.cvtuartbus.dat_w) # drops 8..31
634 comb += self.cvtuartbus.dat_r.eq(uartbus.dat_r) # drops 8..31
635 comb += self.cvtuartbus.ack.eq(uartbus.ack)
636 # aaand with the WB4-pipeline-to-WB3-classic mismatch, sigh
637 comb += uartbus.stall.eq(uartbus.cyc & ~uartbus.ack)
638 comb += self.cvtuartbus.stall.eq(uartbus.stall)
639 if hasattr(self, "cpu"):
640 m.submodules.intc = self.intc
641 m.submodules.extcore = self.cpu
642 m.submodules.dbuscvt = self.dbusdowncvt
643 m.submodules.ibuscvt = self.ibusdowncvt
644 # create stall sigs, assume wishbone classic
645 #ibus, dbus = self.cvtibus, self.cvtdbus
646 #comb += ibus.stall.eq(ibus.stb & ~ibus.ack)
647 #comb += dbus.stall.eq(dbus.stb & ~dbus.ack)
648
649 m.submodules.arbiter = self._arbiter
650 m.submodules.decoder = self._decoder
651 if hasattr(self, "ddrphy"):
652 m.submodules.ddrphy = self.ddrphy
653 m.submodules.dramcore = self.dramcore
654 m.submodules.drambone = drambone = self.drambone
655
656 # add async wishbone bridges
657 if hasattr(self, "ddrphy_async_br"):
658 m.submodules.ddrphy_async_br = self.ddrphy_async_br
659 if hasattr(self, "dramcore_async_br"):
660 m.submodules.dramcore_async_br = self.dramcore_async_br
661 if hasattr(self, "drambone_async_br"):
662 m.submodules.drambone_async_br = self.drambone_async_br
663
664 # grrr, same problem with WB async bridge: not WB4-pipe compliant
665 dab = self.ddrphy_bus
666 if hasattr(dab, "stall"):
667 comb += dab.stall.eq(dab.cyc & ~dab.ack)
668 dab = self.dramcore_bus
669 if hasattr(dab, "stall"):
670 comb += dab.stall.eq(dab.cyc & ~dab.ack)
671 dab = self.drambone_bus
672 comb += dab.stall.eq(dab.cyc & ~dab.ack)
673
674 # add wb async bridge verilog source. assumes directory structure
675 # where bridge has been checked out in a common subdirectory with:
676 # git clone https://github.com/alexforencich/verilog-wishbone.git
677 # git checkout d1fa24a0
678 verilog_wishbone = "../../verilog-wishbone/rtl"
679 pth = os.path.split(__file__)[0]
680 pth = os.path.join(pth, verilog_wishbone)
681 fname = os.path.abspath(pth)
682 print (fname)
683 if hasattr(self, "ddrphy_async_br"):
684 self.dramcore_async_br.add_verilog_source(fname, platform)
685 if hasattr(self, "drambone_async_br"):
686 self.drambone_async_br.add_verilog_source(fname, platform)
687
688 # add hyperram module
689 if hasattr(self, "hyperram"):
690 m.submodules.hyperram = hyperram = self.hyperram
691 # grrr, same problem with hyperram: not WB4-pipe compliant
692 comb += hyperram.bus.stall.eq(hyperram.bus.cyc & ~hyperram.bus.ack)
693 # set 3 top CSn lines to zero for now
694 if self.fpga == 'arty_a7':
695 comb += hyperram.phy.rst_n.eq(ResetSignal())
696
697 # add blinky lights so we know FPGA is alive
698 if platform is not None:
699 m.submodules.blinky = Blinky()
700
701 # connect the arbiter (of wishbone masters)
702 # to the decoder (addressing wishbone slaves)
703 comb += self._arbiter.bus.connect(self._decoder.bus)
704
705 if hasattr(self, "cpu"):
706 m.submodules.xics_icp = icp = self.xics_icp
707 m.submodules.xics_ics = ics = self.xics_ics
708 comb += icp.ics_i.eq(ics.icp_o) # connect ICS to ICP
709 comb += self.cpu.irq.eq(icp.core_irq_o) # connect ICP to core
710
711 # wire up the CPU interrupts from the GenericInterrupt
712 comb += self.int_level_i.eq(self.intc.ip)
713
714 # grrr
715 comb += self.pbus.stall.eq(self.pbus.cyc & ~self.pbus.ack)
716 comb += self.sbus.stall.eq(self.sbus.cyc & ~self.sbus.ack)
717
718 # and also wire up make_wb_layout() to wishbone.Interface.
719 # really, XICS_ICS and XICS_ICP both need to be converted
720 # to use wishbone.Interface and this all goes
721 comb += icp.bus.adr.eq(self.pbus.adr)
722 comb += icp.bus.dat_w.eq(self.pbus.dat_w)
723 comb += icp.bus.cyc.eq(self.pbus.cyc)
724 comb += icp.bus.stb.eq(self.pbus.stb)
725 comb += icp.bus.we.eq(self.pbus.we)
726 comb += self.pbus.ack.eq(icp.bus.ack)
727 comb += self.pbus.dat_r.eq(icp.bus.dat_r)
728 comb += ics.bus.adr.eq(self.sbus.adr)
729 comb += ics.bus.dat_w.eq(self.sbus.dat_w)
730 comb += ics.bus.cyc.eq(self.sbus.cyc)
731 comb += ics.bus.stb.eq(self.sbus.stb)
732 comb += ics.bus.we.eq(self.sbus.we)
733 comb += self.sbus.ack.eq(ics.bus.ack)
734 comb += self.sbus.dat_r.eq(ics.bus.dat_r)
735
736 if platform is None:
737 return m
738
739 # add uart16550 verilog source. assumes a directory
740 # structure where ls2 has been checked out in a common
741 # subdirectory as:
742 # git clone https://github.com/freecores/uart16550
743 opencores_16550 = "../../uart16550/rtl/verilog"
744 pth = os.path.split(__file__)[0]
745 pth = os.path.join(pth, opencores_16550)
746 fname = os.path.abspath(pth)
747 print (fname)
748 self.uart.add_verilog_source(fname, platform)
749
750 if hasattr(self, "spi0"):
751 # add spi submodule
752 m.submodules.spi0 = spi = self.spi0
753 # gonna drive me nuts, this.
754 comb += spi.bus.stall.eq(spi.bus.cyc & ~spi.bus.ack)
755 comb += spi.cfg_bus.stall.eq(spi.cfg_bus.cyc & ~spi.cfg_bus.ack)
756
757 # add Tercel verilog source. assumes a directory structure where
758 # microwatt has been checked out in a common subdirectory with:
759 # git clone https://git.libre-soc.org/git/microwatt.git tercel-qspi
760 # git checkout 882ace781e4
761 raptor_tercel = "../../tercel-qspi/tercel"
762 pth = os.path.split(__file__)[0]
763 pth = os.path.join(pth, raptor_tercel)
764 fname = os.path.abspath(pth)
765 print (fname)
766 self.spi0.add_verilog_source(fname, platform)
767
768 if hasattr(self, "eth0"):
769 # add ethernet submodule
770 m.submodules.eth0 = ethmac = self.eth0
771
772 # add EthMAC verilog source. assumes a directory
773 # structure where the opencores ethmac has been checked out
774 # in a common subdirectory as:
775 # git clone https://github.com/freecores/ethmac
776 opencores_ethmac = "../../ethmac/rtl/verilog"
777 pth = os.path.split(__file__)[0]
778 pth = os.path.join(pth, opencores_ethmac)
779 fname = os.path.abspath(pth)
780 print (fname)
781 self.eth0.add_verilog_source(fname, platform)
782
783 # add the main core
784 pth = os.path.split(__file__)[0]
785 pth = os.path.join(pth, '../external_core_top.v')
786 fname = os.path.abspath(pth)
787 with open(fname) as f:
788 platform.add_file(fname, f)
789
790 return m
791
792 def ports(self):
793 # puzzlingly the only IO ports needed are peripheral pins,
794 # and at the moment that's just UART tx/rx.
795 ports = []
796 ports += [self.uart.tx_o, self.uart.rx_i]
797 if hasattr(self, "hyperram"):
798 ports += list(self.hyperram.ports())
799 if hasattr(self, "ddrphy"):
800 if hasattr(self.ddrphy, "pads"): # real PHY
801 ports += list(self.ddrphy.pads.fields.values())
802 else: # FakePHY, get at the dfii pads, stops deletion of nets
803 for phase in self.dramcore.dfii.master.phases:
804 print ("dfi master", phase)
805 ports += list(phase.fields.values())
806 for phase in self.dramcore.dfii.slave.phases:
807 print ("dfi master", phase)
808 ports += list(phase.fields.values())
809 for phase in self.dramcore.dfii._inti.phases:
810 print ("dfi master", phase)
811 ports += list(phase.fields.values())
812 ports += [ClockSignal(), ResetSignal()]
813 return ports
814
815 def build_platform(fpga, firmware):
816
817 # create a platform selected from the toolchain.
818 platform_kls = {'versa_ecp5': VersaECP5Platform,
819 'versa_ecp5_85': VersaECP5Platform85,
820 'ulx3s': ULX3S_85F_Platform,
821 'orangecrab': OrangeCrabR0_2_85k_Platform,
822 'arty_a7': ArtyA7_100Platform,
823 'isim': IcarusVersaPlatform,
824 'sim': None,
825 }[fpga]
826 toolchain = {'arty_a7': "yosys_nextpnr",
827 'versa_ecp5': 'Trellis',
828 'versa_ecp5_85': 'Trellis',
829 'orangecrab': 'Trellis',
830 'isim': 'Trellis',
831 'ulx3s': 'Trellis',
832 'sim': None,
833 }.get(fpga, None)
834 dram_cls = {'arty_a7': None,
835 'versa_ecp5': MT41K64M16,
836 'versa_ecp5_85': MT41K64M16,
837 #'versa_ecp5': MT41K256M16,
838 'ulx3s': None,
839 'sim': MT41K256M16,
840 'isim': MT41K64M16,
841 }.get(fpga, None)
842 if platform_kls is not None:
843 platform = platform_kls(toolchain=toolchain)
844 if fpga == 'versa_ecp5_85':
845 platform.speed = "7" # HACK. speed grade 7, sigh
846 else:
847 platform = None
848
849 print ("platform", fpga, firmware, platform)
850
851 # set clock frequency
852 clk_freq = 70e6
853 dram_clk_freq = None
854 if fpga == 'sim':
855 clk_freq = 100e6
856 dram_clk_freq = clk_freq
857 if fpga == 'isim':
858 clk_freq = 50e6 # below 50 mhz, stops DRAM being enabled
859 dram_clk_freq = 100e6
860 if fpga == 'versa_ecp5':
861 clk_freq = 50e6 # crank right down to test hyperram
862 #dram_clk_freq = 100e6
863 if fpga == 'versa_ecp5_85':
864 # 50MHz works. 100MHz works. 55MHz does NOT work.
865 # Stick with multiples of 50MHz...
866 clk_freq = 50e6
867 dram_clk_freq = 100e6
868 if fpga == 'arty_a7':
869 clk_freq = 50e6
870 if fpga == 'ulx3s':
871 clk_freq = 40.0e6
872
873 # merge dram_clk_freq with clk_freq if the same
874 if clk_freq == dram_clk_freq:
875 dram_clk_freq = None
876
877 # select a firmware address
878 fw_addr = None
879 if firmware is not None:
880 fw_addr = 0xff00_0000 # firmware at HI address, now
881
882 print ("fpga", fpga, "firmware", firmware)
883
884 # get UART resource pins
885 if platform is not None:
886 if fpga=="orangecrab":
887 # assumes an FT232 USB-UART soldered onto these two pins.
888 orangecrab_uart = UARTResource(0, rx="N17", tx="M18",
889 attrs=Attrs(IOSTANDARD="LVCMOS33"))
890 platform.add_resources([orangecrab_uart])
891
892 uart_pins = platform.request("uart", 0)
893 else:
894 uart_pins = Record([('tx', 1), ('rx', 1)], name="uart_0")
895
896 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
897 ddr_pins = None
898 if (clk_freq >= 50e6 and platform is not None and
899 fpga in ['versa_ecp5', 'versa_ecp5_85', 'arty_a7', 'isim']):
900 ddr_pins = platform.request("ddr3", 0,
901 dir={"dq":"-", "dqs":"-"},
902 xdr={"rst": 4, "clk":4, "a":4,
903 "ba":4, "clk_en":4,
904 "odt":4, "ras":4, "cas":4, "we":4,
905 "cs": 4})
906 print ("ddr pins", ddr_pins)
907
908 # Get SPI resource pins
909 spi_0_pins = None
910 if False and platform is not None and \
911 fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
912 # Override here to get FlashResource out of the way and enable Tercel
913 # direct access to the SPI flash.
914 # each pin needs a separate direction control
915 spi_0_ios = [
916 Resource("spi_0", 0,
917 Subsignal("dq0", Pins("W2", dir="io")),
918 Subsignal("dq1", Pins("V2", dir="io")),
919 Subsignal("dq2", Pins("Y2", dir="io")),
920 Subsignal("dq3", Pins("W1", dir="io")),
921 Subsignal("cs_n", Pins("R2", dir="o")),
922 Attrs(PULLMODE="NONE", DRIVE="4", IO_TYPE="LVCMOS33"))
923 ]
924 platform.add_resources(spi_0_ios)
925 spi_0_pins = platform.request("spi_0", 0, dir={"cs_n":"o"},
926 xdr={"dq0":1, "dq1": 1,
927 "dq2":1, "dq3": 1,
928 "cs_n":0})
929
930 if platform is not None and \
931 fpga in ['arty_a7']:
932 # each pin needs a separate direction control
933 spi_0_ios = [
934 Resource("spi_0", 0,
935 Subsignal("dq0", Pins("K17", dir="io")),
936 Subsignal("dq1", Pins("K18", dir="io")),
937 Subsignal("dq2", Pins("L14", dir="io")),
938 Subsignal("dq3", Pins("M14", dir="io")),
939 Subsignal("cs_n", Pins("L13", dir="o")),
940 Subsignal("clk", Pins("L16", dir="o")),
941 Attrs(PULLMODE="NONE", DRIVE="4", IO_TYPE="LVCMOS33"))
942 ]
943 platform.add_resources(spi_0_ios)
944 spi_0_pins = platform.request("spi_0", 0)
945
946 print ("spiflash pins", spi_0_pins)
947
948 # Get Ethernet RMII resource pins
949 ethmac_0_pins = None
950 if False and platform is not None and \
951 fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
952 # Mainly on X3 connector, MDIO on X4 due to lack of pins
953 ethmac_0_ios = [
954 Resource("ethmac_0", 0,
955 Subsignal("mtx_clk", Pins("B19", dir="i")),
956 Subsignal("mtxd", Pins("B12 B9 E6 D6", dir="o")),
957 Subsignal("mtxen", Pins("E7", dir="o")),
958 Subsignal("mtxerr", Pins("D7", dir="o")),
959 Subsignal("mrx_clk", Pins("B11", dir="i")),
960 Subsignal("mrxd", Pins("B6 E9 D9 B8", dir="i")),
961 Subsignal("mrxdv", Pins("C8", dir="i")),
962 Subsignal("mrxerr", Pins("D8", dir="i")),
963 Subsignal("mcoll", Pins("E8", dir="i")),
964 Subsignal("mcrs", Pins("C7", dir="i")),
965 Subsignal("mdc", Pins("B18", dir="o")),
966 Subsignal("md", Pins("A18", dir="io")),
967 Attrs(PULLMODE="NONE", DRIVE="8", SLEWRATE="FAST",
968 IO_TYPE="LVCMOS33"))
969 ]
970 platform.add_resources(ethmac_0_ios)
971 ethmac_0_pins = platform.request("ethmac_0", 0,
972 dir={"mtx_clk":"i", "mtxd":"o",
973 "mtxen":"o",
974 "mtxerr":"o", "mrx_clk":"i",
975 "mrxd":"i",
976 "mrxdv":"i", "mrxerr":"i",
977 "mcoll":"i",
978 "mcrs":"i", "mdc":"o", "md":"io"},
979 xdr={"mtx_clk": 0, "mtxd": 0,
980 "mtxen": 0,
981 "mtxerr": 0, "mrx_clk": 0,
982 "mrxd": 0,
983 "mrxdv": 0, "mrxerr": 0,
984 "mcoll": 0,
985 "mcrs": 0, "mdc": 0, "md": 0})
986 print ("ethmac pins", ethmac_0_pins)
987
988 # Get HyperRAM pins
989 hyperram_pins = None
990 if platform is None:
991 hyperram_pins = HyperRAMPads()
992 elif fpga in ['isim']:
993 hyperram_ios = HyperRAMResource(0, cs_n="B13",
994 dq="E14 C10 B10 E12 D12 A9 D11 D14",
995 rwds="C14", rst_n="E13", ck_p="D13",
996 attrs=Attrs(IO_TYPE="LVCMOS33"))
997 platform.add_resources(hyperram_ios)
998 hyperram_pins = platform.request("hyperram")
999 print ("isim a7 hyperram", hyperram_ios)
1000 # Digilent Arty A7-100t
1001 elif platform is not None and fpga in ['arty_a7']:
1002 hyperram_ios = HyperRAMResource(0, cs_n="V12 V14 U12 U14",
1003 dq="D4 D3 F4 F3 G2 H2 D2 E2",
1004 rwds="U13", rst_n="T13", ck_p="V10",
1005 # ck_n="V11" - for later (DDR)
1006 attrs=Attrs(IOSTANDARD="LVCMOS33"))
1007 platform.add_resources(hyperram_ios)
1008 hyperram_pins = platform.request("hyperram")
1009 print ("arty a7 hyperram", hyperram_ios)
1010 # VERSA ECP5
1011 elif False and platform is not None and fpga in \
1012 ['versa_ecp5', 'versa_ecp5_85']:
1013 hyperram_ios = HyperRAMResource(0, cs_n="B13",
1014 dq="E14 C10 B10 E12 D12 A9 D11 D14",
1015 rwds="C14", rst_n="E13", ck_p="D13",
1016 attrs=Attrs(IO_TYPE="LVCMOS33"))
1017 platform.add_resources(hyperram_ios)
1018 hyperram_pins = platform.request("hyperram")
1019 print ("versa ecp5 hyperram", hyperram_ios)
1020 print ("hyperram pins", hyperram_pins)
1021
1022 # set up the SOC
1023 soc = DDR3SoC(fpga=fpga, dram_cls=dram_cls,
1024 # check microwatt_soc.h for these
1025 ddrphy_addr=0xfff00000, # DRAM_INIT_BASE, PHY address
1026 dramcore_addr=0xc8000000, # DRAM_CTRL_BASE
1027 ddr_addr=0x00000000, # DRAM_BASE
1028 spi0_addr=0xf0000000, # SPI0_BASE
1029 spi0_cfg_addr=0xc0006000, # SPI0_CTRL_BASE
1030 eth0_cfg_addr=0xc000c000, # ETH0_CTRL_BASE (4k)
1031 eth0_irqno=1, # ETH0_IRQ number (match microwatt)
1032 hyperram_addr=0xa0000000, # HYPERRAM_BASE
1033 fw_addr=fw_addr,
1034 #fw_addr=None,
1035 ddr_pins=ddr_pins,
1036 uart_pins=uart_pins,
1037 uart_irqno=0, # UART_IRQ number (match microwatt)
1038 uart_addr=0xc0002000, # UART0_ADDR
1039 spi_0_pins=spi_0_pins,
1040 ethmac_0_pins=ethmac_0_pins,
1041 hyperram_pins=hyperram_pins,
1042 firmware=firmware,
1043 xics_icp_addr=0xc000_4000, # XICS_ICP_BASE
1044 xics_ics_addr=0xc000_5000, # XICS_ICS_BASE
1045 clk_freq=clk_freq,
1046 dram_clk_freq=dram_clk_freq,
1047 add_cpu=True)
1048
1049 if toolchain == 'Trellis':
1050 # add -abc9 option to yosys synth_ecp5
1051 #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
1052 #os.environ['NMIGEN_synth_opts'] = '-abc9'
1053 os.environ['NMIGEN_synth_opts'] = '-nowidelut'
1054
1055 if platform is not None:
1056 # build and upload it
1057 if fpga == 'isim':
1058 platform.build(soc, do_program=False,
1059 do_build=True, build_dir="build_simsoc")
1060 else:
1061 platform.build(soc, do_program=True)
1062 else:
1063 # for now, generate verilog
1064 vl = verilog.convert(soc, ports=soc.ports())
1065 with open("ls2.v", "w") as f:
1066 f.write(vl)
1067
1068
1069 # urrr this gets exec()d by the build process without arguments
1070 # which screws up. use the arty_a7_ls2.py etc. with no arguments
1071 if __name__ == '__main__':
1072 fpga = None
1073 firmware = None
1074 if len(sys.argv) >= 2:
1075 fpga = sys.argv[1]
1076 if len(sys.argv) >= 3:
1077 firmware = sys.argv[2]
1078 build_platform(fpga, firmware)