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