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