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