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