add read of SYSCON and entry for SPIFlash
[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), at address 0x0
307 if fw_addr is not None:
308 sram_width = 32
309 self.bootmem = SRAMPeripheral(size=0x8000, data_width=sram_width,
310 writable=True)
311 if firmware is not None:
312 with open(firmware, "rb") as f:
313 words = iter(lambda: f.read(sram_width // 8), b'')
314 bios = [int.from_bytes(w, "little") for w in words]
315 self.bootmem.init = bios
316 self._decoder.add(self.bootmem.bus, addr=fw_addr) # ROM at fw_addr
317
318 # System Configuration info
319 spi_offset = spi0_addr if (spi_0_pins is not None) else None
320 self.syscon = MicrowattSYSCON(sys_clk_freq=clk_freq,
321 has_uart=(uart_pins is not None),
322 spi_offset=spi_offset)
323 self._decoder.add(self.syscon.bus, addr=0xc0000000) # at 0xc000_0000
324
325 if False:
326 # SRAM (read-writeable BRAM)
327 self.ram = SRAMPeripheral(size=4096)
328 self._decoder.add(self.ram.bus, addr=0x8000000) # at 0x8000_0000
329
330 # UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
331 if uart_pins is not None:
332 # sigh actual UART in microwatt is 8-bit
333 self.uart = UART16550(data_width=8, pins=uart_pins,
334 features={'stall'})
335 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
336 cvtuartbus = wishbone.Interface(addr_width=5, data_width=32,
337 granularity=8,
338 features={'stall'})
339 umap = MemoryMap(addr_width=7, data_width=8, name="uart_map")
340 cvtuartbus.memory_map = umap
341 self._decoder.add(cvtuartbus, addr=0xc0002000) # 16550 UART addr
342 self.cvtuartbus = cvtuartbus
343
344 # SDRAM module using opencores sdr_ctrl
345 """
346 class MT48LC16M16(SDRModule):
347 # geometry
348 nbanks = 4
349 nrows = 8192
350 ncols = 512
351 # timings
352 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
353 tWTR=(2, None),
354 tCCD=(1, None),
355 tRRD=(None, 15))
356 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
357 tRCD=20,
358 tWR=15,
359 tRFC=(None, 66),
360 tFAW=None,
361 tRAS=44)}
362 """
363
364 # DRAM Module
365 if ddr_pins is not None or fpga == 'sim':
366 ddrmodule = dram_cls(clk_freq, "1:2") # match DDR3 ASIC P/N
367
368 #drs = lambda x: x
369 drs = DomainRenamer("dramsync")
370
371 if fpga == 'sim':
372 self.ddrphy = FakePHY(module=ddrmodule,
373 settings=sim_ddr3_settings(clk_freq),
374 verbosity=SDRAM_VERBOSE_DBG,
375 clk_freq=clk_freq)
376 else:
377 self.ddrphy = drs(ECP5DDRPHY(ddr_pins, sys_clk_freq=clk_freq))
378 self._decoder.add(self.ddrphy.bus, addr=ddrphy_addr)
379
380 dramcore = gramCore(phy=self.ddrphy,
381 geom_settings=ddrmodule.geom_settings,
382 timing_settings=ddrmodule.timing_settings,
383 clk_freq=clk_freq)
384 if fpga == 'sim':
385 self.dramcore = dramcore
386 else:
387 self.dramcore = drs(dramcore)
388 self._decoder.add(self.dramcore.bus, addr=dramcore_addr)
389
390 # map the DRAM onto Wishbone, XXX use stall but set classic below
391 drambone = gramWishbone(dramcore, features={'stall'})
392 if fpga == 'sim':
393 self.drambone = drambone
394 else:
395 self.drambone = drs(drambone)
396 self._decoder.add(self.drambone.bus, addr=ddr_addr)
397
398 # SPI controller
399 if spi_0_pins is not None and fpga in ['sim',
400 'isim',
401 'rcs_arctic_tern_bmc_card',
402 'versa_ecp5',
403 'versa_ecp5_85',
404 'arty_a7']:
405 # The Lattice ECP5 devices require special handling on the
406 # dedicated SPI clock line, which is shared with the internal
407 # SPI controller used for FPGA bitstream loading.
408 spi0_is_lattice_ecp5_clk = False
409 if fpga in ['versa_ecp5',
410 'versa_ecp5_85',
411 'rcs_arctic_tern_bmc_card',
412 'isim']:
413 spi0_is_lattice_ecp5_clk = True
414
415 # Tercel contains two independent Wishbone regions, a
416 # configuration region and the direct API access region,
417 # Set the SPI 0 access region to 16MB, as the FPGA
418 # bitstream Flash device is unlikely to be larger than this.
419 # The main SPI Flash (SPI 1) should be set to at
420 # least 28 bits (256MB) to allow the use of large 4BA devices.
421 self.spi0 = Tercel(data_width=32, spi_region_addr_width=24,
422 adr_offset=spi0_addr,
423 features={'stall'},
424 clk_freq=clk_freq,
425 pins=spi_0_pins,
426 lattice_ecp5_usrmclk=spi0_is_lattice_ecp5_clk)
427 self._decoder.add(self.spi0.bus, addr=spi0_addr)
428 self._decoder.add(self.spi0.cfg_bus, addr=spi0_cfg_addr)
429
430 # Ethernet MAC
431 if ethmac_0_pins is not None and fpga in ['versa_ecp5',
432 'versa_ecp5_85',
433 'isim']:
434 # The OpenCores Ethernet MAC contains two independent Wishbone
435 # interfaces, a slave (configuration) interface and a master (DMA)
436 # interface.
437 self.eth0 = EthMAC(pins=ethmac_0_pins)
438 self._arbiter.add(self.eth0.master_bus)
439 self._decoder.add(self.eth0.slave_bus, addr=eth0_cfg_addr)
440 self.intc.add_irq(self.eth0.irq, index=eth0_irqno)
441
442 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
443 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
444 # moment
445 if hyperram_pins is not None:
446 self.hyperram = HyperRAM(io=hyperram_pins, phy_kls=HyperRAMPHY,
447 features={'stall'},
448 latency=7) # Winbond W956D8MBYA
449 self._decoder.add(self.hyperram.bus, addr=hyperram_addr)
450
451 self.memory_map = self._decoder.bus.memory_map
452
453 self.clk_freq = clk_freq
454 self.fpga = fpga
455
456 def elaborate(self, platform):
457 m = Module()
458 comb = m.d.comb
459
460 # add the peripherals and clock-reset-generator
461 if platform is not None and hasattr(self, "crg"):
462 m.submodules.sysclk = self.crg
463
464 if hasattr(self, "bootmem"):
465 m.submodules.bootmem = self.bootmem
466 m.submodules.syscon = self.syscon
467 if hasattr(self, "ram"):
468 m.submodules.ram = self.ram
469 if hasattr(self, "uart"):
470 m.submodules.uart = self.uart
471 comb += self.uart.cts_i.eq(1)
472 comb += self.uart.dsr_i.eq(1)
473 comb += self.uart.ri_i.eq(0)
474 comb += self.uart.dcd_i.eq(1)
475 # sigh connect up the wishbone bus manually to deal with
476 # the mis-match on the data
477 uartbus = self.uart.bus
478 comb += uartbus.adr.eq(self.cvtuartbus.adr)
479 comb += uartbus.stb.eq(self.cvtuartbus.stb)
480 comb += uartbus.cyc.eq(self.cvtuartbus.cyc)
481 comb += uartbus.sel.eq(self.cvtuartbus.sel)
482 comb += uartbus.we.eq(self.cvtuartbus.we)
483 comb += uartbus.dat_w.eq(self.cvtuartbus.dat_w) # drops 8..31
484 comb += self.cvtuartbus.dat_r.eq(uartbus.dat_r) # drops 8..31
485 comb += self.cvtuartbus.ack.eq(uartbus.ack)
486 # aaand with the WB4-pipeline-to-WB3-classic mismatch, sigh
487 comb += uartbus.stall.eq(uartbus.cyc & ~uartbus.ack)
488 comb += self.cvtuartbus.stall.eq(uartbus.stall)
489 if hasattr(self, "cpu"):
490 m.submodules.intc = self.intc
491 m.submodules.extcore = self.cpu
492 m.submodules.dbuscvt = self.dbusdowncvt
493 m.submodules.ibuscvt = self.ibusdowncvt
494 # create stall sigs, assume wishbone classic
495 #ibus, dbus = self.cvtibus, self.cvtdbus
496 #comb += ibus.stall.eq(ibus.stb & ~ibus.ack)
497 #comb += dbus.stall.eq(dbus.stb & ~dbus.ack)
498
499 m.submodules.arbiter = self._arbiter
500 m.submodules.decoder = self._decoder
501 if hasattr(self, "ddrphy"):
502 m.submodules.ddrphy = self.ddrphy
503 m.submodules.dramcore = self.dramcore
504 m.submodules.drambone = drambone = self.drambone
505 # grrr, same problem with drambone: not WB4-pipe compliant
506 comb += drambone.bus.stall.eq(drambone.bus.cyc & ~drambone.bus.ack)
507
508 # add hyperram module
509 if hasattr(self, "hyperram"):
510 m.submodules.hyperram = hyperram = self.hyperram
511 # grrr, same problem with hyperram: not WB4-pipe compliant
512 comb += hyperram.bus.stall.eq(hyperram.bus.cyc & ~hyperram.bus.ack)
513 # set 3 top CSn lines to zero for now
514 if self.fpga == 'arty_a7':
515 comb += hyperram.phy.rst_n.eq(ResetSignal())
516
517 # add blinky lights so we know FPGA is alive
518 if platform is not None:
519 m.submodules.blinky = Blinky()
520
521 # connect the arbiter (of wishbone masters)
522 # to the decoder (addressing wishbone slaves)
523 comb += self._arbiter.bus.connect(self._decoder.bus)
524
525 if hasattr(self, "cpu"):
526 # wire up the CPU interrupts
527 comb += self.cpu.irq.eq(self.intc.ip)
528
529 if platform is None:
530 return m
531
532 # add uart16550 verilog source. assumes a directory
533 # structure where ls2 has been checked out in a common
534 # subdirectory as:
535 # git clone https://github.com/freecores/uart16550
536 opencores_16550 = "../../uart16550/rtl/verilog"
537 pth = os.path.split(__file__)[0]
538 pth = os.path.join(pth, opencores_16550)
539 fname = os.path.abspath(pth)
540 print (fname)
541 self.uart.add_verilog_source(fname, platform)
542
543 if hasattr(self, "spi0"):
544 # add spi submodule
545 m.submodules.spi0 = spi = self.spi0
546 # gonna drive me nuts, this.
547 comb += spi.bus.stall.eq(spi.bus.cyc & ~spi.bus.ack)
548 comb += spi.cfg_bus.stall.eq(spi.cfg_bus.cyc & ~spi.cfg_bus.ack)
549
550 # add Tercel verilog source. assumes a directory structure where
551 # microwatt has been checked out in a common subdirectory with:
552 # git clone https://git.libre-soc.org/git/microwatt.git tercel-qspi
553 # git checkout 882ace781e4
554 raptor_tercel = "../../tercel-qspi/tercel"
555 pth = os.path.split(__file__)[0]
556 pth = os.path.join(pth, raptor_tercel)
557 fname = os.path.abspath(pth)
558 print (fname)
559 self.spi0.add_verilog_source(fname, platform)
560
561 if hasattr(self, "eth0"):
562 # add ethernet submodule
563 m.submodules.eth0 = ethmac = self.eth0
564
565 # add EthMAC verilog source. assumes a directory
566 # structure where the opencores ethmac has been checked out
567 # in a common subdirectory as:
568 # git clone https://github.com/freecores/ethmac
569 opencores_ethmac = "../../ethmac/rtl/verilog"
570 pth = os.path.split(__file__)[0]
571 pth = os.path.join(pth, opencores_ethmac)
572 fname = os.path.abspath(pth)
573 print (fname)
574 self.eth0.add_verilog_source(fname, platform)
575
576 # add the main core
577 pth = os.path.split(__file__)[0]
578 pth = os.path.join(pth, '../external_core_top.v')
579 fname = os.path.abspath(pth)
580 with open(fname) as f:
581 platform.add_file(fname, f)
582
583 return m
584
585 def ports(self):
586 # puzzlingly the only IO ports needed are peripheral pins,
587 # and at the moment that's just UART tx/rx.
588 ports = []
589 ports += [self.uart.tx_o, self.uart.rx_i]
590 if hasattr(self, "hyperram"):
591 ports += list(self.hyperram.ports())
592 if hasattr(self, "ddrphy"):
593 if hasattr(self.ddrphy, "pads"): # real PHY
594 ports += list(self.ddrphy.pads.fields.values())
595 else: # FakePHY, get at the dfii pads, stops deletion of nets
596 for phase in self.dramcore.dfii.master.phases:
597 print ("dfi master", phase)
598 ports += list(phase.fields.values())
599 for phase in self.dramcore.dfii.slave.phases:
600 print ("dfi master", phase)
601 ports += list(phase.fields.values())
602 for phase in self.dramcore.dfii._inti.phases:
603 print ("dfi master", phase)
604 ports += list(phase.fields.values())
605 ports += [ClockSignal(), ResetSignal()]
606 return ports
607
608 def build_platform(fpga, firmware):
609
610 # create a platform selected from the toolchain.
611 platform_kls = {'versa_ecp5': VersaECP5Platform,
612 'versa_ecp5_85': VersaECP5Platform85,
613 'ulx3s': ULX3S_85F_Platform,
614 'arty_a7': ArtyA7_100Platform,
615 'isim': IcarusVersaPlatform,
616 'sim': None,
617 }[fpga]
618 toolchain = {'arty_a7': "yosys_nextpnr",
619 'versa_ecp5': 'Trellis',
620 'versa_ecp5_85': 'Trellis',
621 'isim': 'Trellis',
622 'ulx3s': 'Trellis',
623 'sim': None,
624 }.get(fpga, None)
625 dram_cls = {'arty_a7': None,
626 'versa_ecp5': MT41K64M16,
627 'versa_ecp5_85': MT41K64M16,
628 #'versa_ecp5': MT41K256M16,
629 'ulx3s': None,
630 'sim': MT41K256M16,
631 'isim': MT41K64M16,
632 }.get(fpga, None)
633 if platform_kls is not None:
634 platform = platform_kls(toolchain=toolchain)
635 if fpga == 'versa_ecp5_85':
636 platform.speed = "7" # HACK. speed grade 7, sigh
637 else:
638 platform = None
639
640 print ("platform", fpga, firmware, platform)
641
642 # set clock frequency
643 clk_freq = 70e6
644 if fpga == 'sim':
645 clk_freq = 100e6
646 if fpga == 'isim':
647 clk_freq = 55e6 # below 50 mhz, stops DRAM being enabled
648 if fpga == 'versa_ecp5':
649 clk_freq = 55e6 # crank right down to test hyperram
650 if fpga == 'versa_ecp5_85':
651 # 50MHz works. 100MHz works. 55MHz does NOT work.
652 # Stick with multiples of 50MHz...
653 clk_freq = 50e6
654 if fpga == 'arty_a7':
655 clk_freq = 50e6
656 if fpga == 'ulx3s':
657 clk_freq = 40.0e6
658
659 # select a firmware address
660 fw_addr = None
661 if firmware is not None:
662 fw_addr = 0x0000_0000
663
664 print ("fpga", fpga, "firmware", firmware)
665
666 # get UART resource pins
667 if platform is not None:
668 uart_pins = platform.request("uart", 0)
669 else:
670 uart_pins = Record([('tx', 1), ('rx', 1)], name="uart_0")
671
672 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
673 ddr_pins = None
674 if (clk_freq >= 50e6 and platform is not None and
675 fpga in ['versa_ecp5', 'versa_ecp5_85', 'arty_a7', 'isim']):
676 ddr_pins = platform.request("ddr3", 0,
677 dir={"dq":"-", "dqs":"-"},
678 xdr={"rst": 1, "clk":4, "a":4,
679 "ba":4, "clk_en":4,
680 "odt":4, "ras":4, "cas":4, "we":4,
681 "cs": 4})
682
683 # Get SPI resource pins
684 spi_0_pins = None
685 if platform is not None and \
686 fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
687 # Override here to get FlashResource out of the way and enable Tercel
688 # direct access to the SPI flash.
689 # each pin needs a separate direction control
690 spi_0_ios = [
691 Resource("spi_0", 0,
692 Subsignal("dq0", Pins("W2", dir="io")),
693 Subsignal("dq1", Pins("V2", dir="io")),
694 Subsignal("dq2", Pins("Y2", dir="io")),
695 Subsignal("dq3", Pins("W1", dir="io")),
696 Subsignal("cs_n", Pins("R2", dir="o")),
697 Attrs(PULLMODE="NONE", DRIVE="4", IO_TYPE="LVCMOS33"))
698 ]
699 platform.add_resources(spi_0_ios)
700 spi_0_pins = platform.request("spi_0", 0, dir={"cs_n":"o"},
701 xdr={"dq0":1, "dq1": 1,
702 "dq2":1, "dq3": 1,
703 "cs_n":0})
704
705 if platform is not None and \
706 fpga in ['arty_a7']:
707 # each pin needs a separate direction control
708 spi_0_ios = [
709 Resource("spi_0", 0,
710 Subsignal("dq0", Pins("K17", dir="io")),
711 Subsignal("dq1", Pins("K18", dir="io")),
712 Subsignal("dq2", Pins("L14", dir="io")),
713 Subsignal("dq3", Pins("M14", dir="io")),
714 Subsignal("cs_n", Pins("L13", dir="o")),
715 Subsignal("clk", Pins("L16", dir="o")),
716 Attrs(PULLMODE="NONE", DRIVE="4", IO_TYPE="LVCMOS33"))
717 ]
718 platform.add_resources(spi_0_ios)
719 spi_0_pins = platform.request("spi_0", 0)
720
721 print ("spiflash pins", spi_0_pins)
722
723 # Get Ethernet RMII resource pins
724 ethmac_0_pins = None
725 if False and platform is not None and \
726 fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
727 # Mainly on X3 connector, MDIO on X4 due to lack of pins
728 ethmac_0_ios = [
729 Resource("ethmac_0", 0,
730 Subsignal("mtx_clk", Pins("B19", dir="i")),
731 Subsignal("mtxd", Pins("B12 B9 E6 D6", dir="o")),
732 Subsignal("mtxen", Pins("E7", dir="o")),
733 Subsignal("mtxerr", Pins("D7", dir="o")),
734 Subsignal("mrx_clk", Pins("B11", dir="i")),
735 Subsignal("mrxd", Pins("B6 E9 D9 B8", dir="i")),
736 Subsignal("mrxdv", Pins("C8", dir="i")),
737 Subsignal("mrxerr", Pins("D8", dir="i")),
738 Subsignal("mcoll", Pins("E8", dir="i")),
739 Subsignal("mcrs", Pins("C7", dir="i")),
740 Subsignal("mdc", Pins("B18", dir="o")),
741 Subsignal("md", Pins("A18", dir="io")),
742 Attrs(PULLMODE="NONE", DRIVE="8", SLEWRATE="FAST",
743 IO_TYPE="LVCMOS33"))
744 ]
745 platform.add_resources(ethmac_0_ios)
746 ethmac_0_pins = platform.request("ethmac_0", 0,
747 dir={"mtx_clk":"i", "mtxd":"o",
748 "mtxen":"o",
749 "mtxerr":"o", "mrx_clk":"i",
750 "mrxd":"i",
751 "mrxdv":"i", "mrxerr":"i",
752 "mcoll":"i",
753 "mcrs":"i", "mdc":"o", "md":"io"},
754 xdr={"mtx_clk": 0, "mtxd": 0,
755 "mtxen": 0,
756 "mtxerr": 0, "mrx_clk": 0,
757 "mrxd": 0,
758 "mrxdv": 0, "mrxerr": 0,
759 "mcoll": 0,
760 "mcrs": 0, "mdc": 0, "md": 0})
761 print ("ethmac pins", ethmac_0_pins)
762
763 # Get HyperRAM pins
764 hyperram_pins = None
765 if platform is None:
766 hyperram_pins = HyperRAMPads()
767 elif fpga in ['isim']:
768 hyperram_ios = HyperRAMResource(0, cs_n="B13",
769 dq="E14 C10 B10 E12 D12 A9 D11 D14",
770 rwds="C14", rst_n="E13", ck_p="D13",
771 attrs=Attrs(IO_TYPE="LVCMOS33"))
772 platform.add_resources(hyperram_ios)
773 hyperram_pins = platform.request("hyperram")
774 print ("isim a7 hyperram", hyperram_ios)
775 # Digilent Arty A7-100t
776 elif platform is not None and fpga in ['arty_a7']:
777 hyperram_ios = HyperRAMResource(0, cs_n="V12 V14 U12 U14",
778 dq="D4 D3 F4 F3 G2 H2 D2 E2",
779 rwds="U13", rst_n="T13", ck_p="V10",
780 # ck_n="V11" - for later (DDR)
781 attrs=Attrs(IOSTANDARD="LVCMOS33"))
782 platform.add_resources(hyperram_ios)
783 hyperram_pins = platform.request("hyperram")
784 print ("arty a7 hyperram", hyperram_ios)
785 # VERSA ECP5
786 elif False and platform is not None and fpga in \
787 ['versa_ecp5', 'versa_ecp5_85']:
788 hyperram_ios = HyperRAMResource(0, cs_n="B13",
789 dq="E14 C10 B10 E12 D12 A9 D11 D14",
790 rwds="C14", rst_n="E13", ck_p="D13",
791 attrs=Attrs(IO_TYPE="LVCMOS33"))
792 platform.add_resources(hyperram_ios)
793 hyperram_pins = platform.request("hyperram")
794 print ("versa ecp5 hyperram", hyperram_ios)
795 print ("hyperram pins", hyperram_pins)
796
797 # set up the SOC
798 soc = DDR3SoC(fpga=fpga, dram_cls=dram_cls,
799 # check microwatt_soc.h for these
800 ddrphy_addr=0xff000000, # DRAM_INIT_BASE firmware base
801 dramcore_addr=0xc8000000, # DRAM_CTRL_BASE
802 ddr_addr=0x40000000, # DRAM_BASE
803 spi0_addr=0x10000000, # SPI0_BASE
804 spi0_cfg_addr=0xc0003000, # SPI0_CTRL_BASE
805 eth0_cfg_addr=0xc0004000, # ETH0_CTRL_BASE (4k)
806 eth0_irqno=0, # ETH0_IRQ number
807 hyperram_addr=0xa0000000, # HYPERRAM_BASE
808 fw_addr=fw_addr,
809 #fw_addr=None,
810 ddr_pins=ddr_pins,
811 uart_pins=uart_pins,
812 spi_0_pins=spi_0_pins,
813 ethmac_0_pins=ethmac_0_pins,
814 hyperram_pins=hyperram_pins,
815 firmware=firmware,
816 clk_freq=clk_freq,
817 add_cpu=True)
818
819 if toolchain == 'Trellis':
820 # add -abc9 option to yosys synth_ecp5
821 #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
822 #os.environ['NMIGEN_synth_opts'] = '-abc9'
823 os.environ['NMIGEN_synth_opts'] = '-nowidelut'
824
825 if platform is not None:
826 # build and upload it
827 if fpga == 'isim':
828 platform.build(soc, do_program=False,
829 do_build=True, build_dir="build_simsoc")
830 else:
831 platform.build(soc, do_program=True)
832 else:
833 # for now, generate verilog
834 vl = verilog.convert(soc, ports=soc.ports())
835 with open("ls2.v", "w") as f:
836 f.write(vl)
837
838
839 # urrr this gets exec()d by the build process without arguments
840 # which screws up. use the arty_a7_ls2.py etc. with no arguments
841 if __name__ == '__main__':
842 fpga = None
843 firmware = None
844 if len(sys.argv) >= 2:
845 fpga = sys.argv[1]
846 if len(sys.argv) >= 3:
847 firmware = sys.argv[2]
848 build_platform(fpga, firmware)