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