267f88fdd70e1a96c84d379473000fd9873c584d
[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.external_core import ExternalCore # external libresoc/microwatt
34 from soc.bus.wb_downconvert import WishboneDownConvert
35 from soc.bus.syscon import MicrowattSYSCON
36
37 # DDR3
38 from gram.common import (PhySettings, get_cl_cw, get_sys_latency,
39 get_sys_phases,)
40 from gram.core import gramCore
41 from gram.phy.ecp5ddrphy import ECP5DDRPHY
42 from gram.phy.fakephy import FakePHY, SDRAM_VERBOSE_STD, SDRAM_VERBOSE_DBG
43 from gram.modules import MT41K256M16, MT41K64M16
44 from gram.frontend.wishbone import gramWishbone
45
46 # Board (and simulation) platforms
47 from nmigen_boards.versa_ecp5 import VersaECP5Platform
48 from nmigen_boards.versa_ecp5 import VersaECP5Platform85 # custom board
49 from nmigen_boards.ulx3s import ULX3S_85F_Platform
50 from nmigen_boards.arty_a7 import ArtyA7_100Platform
51 from nmigen_boards.test.blinky import Blinky
52 from icarusversa import IcarusVersaPlatform
53 # Clock-Reset Generator (works for all ECP5 platforms)
54 from ecp5_crg import ECP5CRG
55 from arty_crg import ArtyA7CRG
56
57 import sys
58 import os
59
60 def sim_ddr3_settings(clk_freq=100e6):
61 tck = 2/(2*2*clk_freq)
62 nphases = 2
63 databits = 16
64 nranks = 1
65 addressbits = 14
66 bankbits = 3
67 cl, cwl = get_cl_cw("DDR3", tck)
68 cl_sys_latency = get_sys_latency(nphases, cl)
69 cwl_sys_latency = get_sys_latency(nphases, cwl)
70 rdcmdphase, rdphase = get_sys_phases(nphases, cl_sys_latency, cl)
71 wrcmdphase, wrphase = get_sys_phases(nphases, cwl_sys_latency, cwl)
72 return PhySettings(
73 phytype="ECP5DDRPHY",
74 memtype="DDR3",
75 databits=databits,
76 dfi_databits=4*databits,
77 nranks=nranks,
78 nphases=nphases,
79 rdphase=rdphase,
80 wrphase=wrphase,
81 rdcmdphase=rdcmdphase,
82 wrcmdphase=wrcmdphase,
83 cl=cl,
84 cwl=cwl,
85 read_latency=2 + cl_sys_latency + 2 + log2_int(4//nphases) + 4,
86 write_latency=cwl_sys_latency
87 )
88
89
90 class WB64to32Convert(Elaboratable):
91 """Microwatt IO wishbone slave 64->32 bits converter
92
93 For timing reasons, this adds a one cycle latch on the way both
94 in and out. This relaxes timing and routing pressure on the "main"
95 memory bus by moving all simple IOs to a slower 32-bit bus.
96
97 This implementation is rather dumb at the moment, no stash buffer,
98 so we stall whenever that latch is busy. This can be improved.
99 """
100 def __init__(self, master, slave):
101 self.master = master
102 self.slave = slave
103
104 def elaborate(self, platform):
105 m = Module()
106 comb, sync = m.d.comb, m.d.sync
107 master, slave = self.master, self.slave
108
109 has_top = Signal()
110 has_top_r = Signal()
111 has_bot = Signal()
112
113 with m.FSM() as fsm:
114 with m.State("IDLE"):
115 # Clear ACK (and has_top_r) in case it was set
116 sync += master.ack.eq(0)
117 sync += has_top_r.eq(0)
118
119 # Do we have a cycle ?
120 with m.If(master.cyc & master.stb):
121 # Stall master until we are done, we are't (yet) pipelining
122 # this, it's all slow IOs.
123 sync += master.stall.eq(1)
124
125 # Start cycle downstream
126 sync += slave.cyc.eq(1)
127 sync += slave.stb.eq(1)
128
129 # Do we have a top word and/or a bottom word ?
130 comb += has_top.eq(master.sel[4:].bool())
131 comb += has_bot.eq(master.sel[:4].bool())
132 # record the has_top flag for the next FSM state
133 sync += has_top_r.eq(has_top)
134
135 # Copy write enable to IO out, copy address as well,
136 # LSB is set later based on HI/LO
137 sync += slave.we.eq(master.we)
138 sync += slave.adr.eq(Cat(0, master.adr))
139
140 # If we have a bottom word, handle it first, otherwise
141 # send the top word down. XXX Split the actual mux out
142 # and only generate a control signal.
143 with m.If(has_bot):
144 with m.If(master.we):
145 sync += slave.dat_w.eq(master.dat_w[:32])
146 sync += slave.sel.eq(master.sel[:4])
147
148 # Wait for ack on BOTTOM half
149 m.next = "WAIT_ACK_BOT"
150
151 with m.Else():
152 with m.If(master.we):
153 sync += slave.dat_w.eq(master.dat_w[32:])
154 sync += slave.sel.eq(master.sel[4:])
155
156 # Bump LSB of address
157 sync += slave.adr[0].eq(1)
158
159 # Wait for ack on TOP half
160 m.next = "WAIT_ACK_TOP"
161
162
163 with m.State("WAIT_ACK_BOT"):
164 # If we aren't stalled by the device, clear stb
165 if hasattr(slave, "stall"):
166 with m.If(~slave.stall):
167 sync += slave.stb.eq(0)
168
169 # Handle ack
170 with m.If(slave.ack):
171 # If it's a read, latch the data
172 with m.If(~slave.we):
173 sync += master.dat_r[:32].eq(slave.dat_r)
174
175 # Do we have a "top" part as well ?
176 with m.If(has_top_r):
177 # Latch data & sel
178 with m.If(master.we):
179 sync += slave.dat_w.eq(master.dat_w[32:])
180 sync += slave.sel.eq(master.sel[4:])
181
182 # Bump address and set STB
183 sync += slave.adr[0].eq(1)
184 sync += slave.stb.eq(1)
185
186 # Wait for new ack
187 m.next = "WAIT_ACK_TOP"
188
189 with m.Else():
190 # We are done, ack up, clear cyc downstram
191 sync += slave.cyc.eq(0)
192 sync += slave.stb.eq(0)
193
194 # And ack & unstall upstream
195 sync += master.ack.eq(1)
196 if hasattr(master , "stall"):
197 sync += master.stall.eq(0)
198
199 # Wait for next one
200 m.next = "IDLE"
201
202 with m.State("WAIT_ACK_TOP"):
203 # If we aren't stalled by the device, clear stb
204 if hasattr(slave, "stall"):
205 with m.If(~slave.stall):
206 sync += slave.stb.eq(0)
207
208 # Handle ack
209 with m.If(slave.ack):
210 # If it's a read, latch the data
211 with m.If(~slave.we):
212 sync += master.dat_r[32:].eq(slave.dat_r)
213
214 # We are done, ack up, clear cyc downstram
215 sync += slave.cyc.eq(0)
216 sync += slave.stb.eq(0)
217
218 # And ack & unstall upstream
219 sync += master.ack.eq(1)
220 if hasattr(master, "stall"):
221 sync += master.stall.eq(0)
222
223 # Wait for next one
224 m.next = "IDLE"
225
226 return m
227
228
229 class DDR3SoC(SoC, Elaboratable):
230 def __init__(self, *,
231 fpga,
232 dram_cls,
233 uart_pins, spi_0_pins,
234 ddr_pins, ddrphy_addr, dramcore_addr, ddr_addr,
235 fw_addr=0x0000_0000,
236 firmware=None,
237 spi0_addr, spi0_cfg_addr,
238 hyperram_addr=None,
239 hyperram_pins=None,
240 clk_freq=50e6,
241 add_cpu=True):
242
243 # wishbone routing is as follows:
244 #
245 # SoC
246 # +--+--+
247 # | |
248 # ibus dbus
249 # | |
250 # +--+--+
251 # |
252 # 64to32DownCvt
253 # |
254 # arbiter
255 # |
256 # +---decoder----+--------+---------+-------+
257 # | | | | | |
258 # uart XICS CSRs DRAM XIP SPI HyperRAM
259
260 # set up wishbone bus arbiter and decoder. arbiter routes,
261 # decoder maps local-relative addressed satellites to global addresses
262 self._arbiter = wishbone.Arbiter(addr_width=30, data_width=32,
263 granularity=8,
264 features={"cti", "bte", "stall"})
265 self._decoder = wishbone.Decoder(addr_width=30, data_width=32,
266 granularity=8,
267 features={"cti", "bte", "stall"})
268
269 # default firmware name
270 if firmware is None:
271 firmware = "firmware/main.bin"
272
273 # set up clock request generator
274 pod_bits = 25
275 if fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim', 'ulx3s']:
276 if fpga in ['isim']:
277 pod_bits = 2
278 self.crg = ECP5CRG(clk_freq, pod_bits)
279 if fpga in ['arty_a7']:
280 self.crg = ArtyA7CRG(clk_freq)
281
282 # set up CPU, with 64-to-32-bit downconverters
283 if add_cpu:
284 self.cpu = ExternalCore(name="ext_core")
285 cvtdbus = wishbone.Interface(addr_width=30, data_width=32,
286 granularity=8, features={'stall'})
287 cvtibus = wishbone.Interface(addr_width=30, data_width=32,
288 granularity=8, features={'stall'})
289 self.dbusdowncvt = WB64to32Convert(self.cpu.dbus, cvtdbus)
290 self.ibusdowncvt = WB64to32Convert(self.cpu.ibus, cvtibus)
291 self._arbiter.add(cvtibus) # I-Cache Master
292 self._arbiter.add(cvtdbus) # D-Cache Master. TODO JTAG master
293 self.cvtibus = cvtibus
294 self.cvtdbus = cvtdbus
295
296 # CPU interrupt controller
297 self.intc = GenericInterruptController(width=len(self.cpu.irq))
298
299 # SRAM (but actually a ROM, for firmware), at address 0x0
300 if fw_addr is not None:
301 sram_width = 32
302 self.bootmem = SRAMPeripheral(size=0x8000, data_width=sram_width,
303 writable=True)
304 if firmware is not None:
305 with open(firmware, "rb") as f:
306 words = iter(lambda: f.read(sram_width // 8), b'')
307 bios = [int.from_bytes(w, "little") for w in words]
308 self.bootmem.init = bios
309 self._decoder.add(self.bootmem.bus, addr=fw_addr) # ROM at fw_addr
310
311 # System Configuration info
312 self.syscon = MicrowattSYSCON(sys_clk_freq=clk_freq,
313 has_uart=(uart_pins is not None))
314 self._decoder.add(self.syscon.bus, addr=0xc0000000) # at 0xc000_0000
315
316 if False:
317 # SRAM (read-writeable BRAM)
318 self.ram = SRAMPeripheral(size=4096)
319 self._decoder.add(self.ram.bus, addr=0x8000000) # at 0x8000_0000
320
321 # UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
322 if uart_pins is not None:
323 # sigh actual UART in microwatt is 8-bit
324 self.uart = UART16550(data_width=8, pins=uart_pins,
325 features={'stall'})
326 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
327 cvtuartbus = wishbone.Interface(addr_width=5, data_width=32,
328 granularity=8,
329 features={'stall'})
330 umap = MemoryMap(addr_width=7, data_width=8, name="uart_map")
331 cvtuartbus.memory_map = umap
332 self._decoder.add(cvtuartbus, addr=0xc0002000) # 16550 UART addr
333 self.cvtuartbus = cvtuartbus
334
335 # SDRAM module using opencores sdr_ctrl
336 """
337 class MT48LC16M16(SDRModule):
338 # geometry
339 nbanks = 4
340 nrows = 8192
341 ncols = 512
342 # timings
343 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
344 tWTR=(2, None),
345 tCCD=(1, None),
346 tRRD=(None, 15))
347 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
348 tRCD=20,
349 tWR=15,
350 tRFC=(None, 66),
351 tFAW=None,
352 tRAS=44)}
353 """
354
355 # DRAM Module
356 if ddr_pins is not None or fpga == 'sim':
357 ddrmodule = dram_cls(clk_freq, "1:2") # match DDR3 ASIC P/N
358
359 #drs = lambda x: x
360 drs = DomainRenamer("dramsync")
361
362 if fpga == 'sim':
363 self.ddrphy = FakePHY(module=ddrmodule,
364 settings=sim_ddr3_settings(clk_freq),
365 verbosity=SDRAM_VERBOSE_DBG,
366 clk_freq=clk_freq)
367 else:
368 self.ddrphy = drs(ECP5DDRPHY(ddr_pins, sys_clk_freq=clk_freq))
369 self._decoder.add(self.ddrphy.bus, addr=ddrphy_addr)
370
371 dramcore = gramCore(phy=self.ddrphy,
372 geom_settings=ddrmodule.geom_settings,
373 timing_settings=ddrmodule.timing_settings,
374 clk_freq=clk_freq)
375 if fpga == 'sim':
376 self.dramcore = dramcore
377 else:
378 self.dramcore = drs(dramcore)
379 self._decoder.add(self.dramcore.bus, addr=dramcore_addr)
380
381 # map the DRAM onto Wishbone, XXX use stall but set classic below
382 drambone = gramWishbone(dramcore, features={'stall'})
383 if fpga == 'sim':
384 self.drambone = drambone
385 else:
386 self.drambone = drs(drambone)
387 self._decoder.add(self.drambone.bus, addr=ddr_addr)
388
389 # SPI controller
390 if spi_0_pins is not None and fpga in ['sim',
391 'rcs_arctic_tern_bmc_card']:
392 # The Lattice ECP5 devices require special handling on the
393 # dedicated SPI clock line, which is shared with the internal
394 # SPI controller used for FPGA bitstream loading.
395 spi0_is_lattice_ecp5_clk = False
396 if platform is not None and fpga in ['versa_ecp5',
397 'rcs_arctic_tern_bmc_card',
398 'isim']:
399 spi0_is_lattice_ecp5_clk = True
400
401 # Tercel contains two independent Wishbone regions, a
402 # configuration region and the direct API access region,
403 # Set the SPI 0 access region to 16MB, as the FPGA
404 # bitstream Flash device is unlikely to be larger than this.
405 # The main SPI Flash (SPI 1) should be set to at
406 # least 28 bits (256MB) to allow the use of large 4BA devices.
407 self.spi0 = Tercel(data_width=32, spi_region_addr_width=24,
408 clk_freq=clk_freq,
409 pins=spi_0_pins,
410 lattice_ecp5_usrmclk=spi0_is_lattice_ecp5_clk)
411 self._decoder.add(self.spi0.bus, addr=spi0_addr)
412 self._decoder.add(self.spi0.cfg_bus, addr=spi0_cfg_addr)
413
414 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
415 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
416 # moment
417 if hyperram_pins is not None:
418 self.hyperram = HyperRAM(io=hyperram_pins, phy_kls=HyperRAMPHY,
419 features={'stall'})
420 self._decoder.add(self.hyperram.bus, addr=hyperram_addr)
421
422 self.memory_map = self._decoder.bus.memory_map
423
424 self.clk_freq = clk_freq
425
426 def elaborate(self, platform):
427 m = Module()
428 comb = m.d.comb
429
430 # add the peripherals and clock-reset-generator
431 if platform is not None and hasattr(self, "crg"):
432 m.submodules.sysclk = self.crg
433
434 if hasattr(self, "bootmem"):
435 m.submodules.bootmem = self.bootmem
436 m.submodules.syscon = self.syscon
437 if hasattr(self, "ram"):
438 m.submodules.ram = self.ram
439 if hasattr(self, "uart"):
440 m.submodules.uart = self.uart
441 comb += self.uart.cts_i.eq(1)
442 comb += self.uart.dsr_i.eq(1)
443 comb += self.uart.ri_i.eq(0)
444 comb += self.uart.dcd_i.eq(1)
445 # sigh connect up the wishbone bus manually to deal with
446 # the mis-match on the data
447 uartbus = self.uart.bus
448 comb += uartbus.adr.eq(self.cvtuartbus.adr)
449 comb += uartbus.stb.eq(self.cvtuartbus.stb)
450 comb += uartbus.cyc.eq(self.cvtuartbus.cyc)
451 comb += uartbus.sel.eq(self.cvtuartbus.sel)
452 comb += uartbus.we.eq(self.cvtuartbus.we)
453 comb += uartbus.dat_w.eq(self.cvtuartbus.dat_w) # drops 8..31
454 comb += self.cvtuartbus.dat_r.eq(uartbus.dat_r) # drops 8..31
455 comb += self.cvtuartbus.ack.eq(uartbus.ack)
456 # aaand with the WB4-pipeline-to-WB3-classic mismatch, sigh
457 comb += uartbus.stall.eq(uartbus.cyc & ~uartbus.ack)
458 comb += self.cvtuartbus.stall.eq(uartbus.stall)
459 if hasattr(self, "cpu"):
460 m.submodules.intc = self.intc
461 m.submodules.extcore = self.cpu
462 m.submodules.dbuscvt = self.dbusdowncvt
463 m.submodules.ibuscvt = self.ibusdowncvt
464 # create stall sigs, assume wishbone classic
465 #ibus, dbus = self.cvtibus, self.cvtdbus
466 #comb += ibus.stall.eq(ibus.stb & ~ibus.ack)
467 #comb += dbus.stall.eq(dbus.stb & ~dbus.ack)
468
469 m.submodules.arbiter = self._arbiter
470 m.submodules.decoder = self._decoder
471 if hasattr(self, "ddrphy"):
472 m.submodules.ddrphy = self.ddrphy
473 m.submodules.dramcore = self.dramcore
474 m.submodules.drambone = drambone = self.drambone
475 # grrr, same problem with drambone: not WB4-pipe compliant
476 comb += drambone.bus.stall.eq(drambone.bus.cyc & ~drambone.bus.ack)
477
478 # add hyperram module
479 if hasattr(self, "hyperram"):
480 m.submodules.hyperram = hyperram = self.hyperram
481 # grrr, same problem with hyperram: not WB4-pipe compliant
482 comb += hyperram.bus.stall.eq(hyperram.bus.cyc & ~hyperram.bus.ack)
483
484 # add blinky lights so we know FPGA is alive
485 if platform is not None:
486 m.submodules.blinky = Blinky()
487
488 # connect the arbiter (of wishbone masters)
489 # to the decoder (addressing wishbone slaves)
490 comb += self._arbiter.bus.connect(self._decoder.bus)
491
492 if hasattr(self, "cpu"):
493 # wire up the CPU interrupts
494 comb += self.cpu.irq.eq(self.intc.ip)
495
496 if platform is None:
497 return m
498
499 # add uart16550 verilog source. assumes a directory
500 # structure where ls2 has been checked out in a common
501 # subdirectory as https://github.com/freecores/uart16550
502 opencores_16550 = "../../uart16550/rtl/verilog"
503 pth = os.path.split(__file__)[0]
504 pth = os.path.join(pth, opencores_16550)
505 fname = os.path.abspath(pth)
506 print (fname)
507 self.uart.add_verilog_source(fname, platform)
508
509 if hasattr(self, "spi0"):
510 # add Tercel verilog source. assumes a directory
511 # structure where ls2 has been checked out in a common
512 # subdirectory as https://git.libre-soc.org/git/microwatt.git
513 raptor_tercel = "../../microwatt/tercel"
514 pth = os.path.split(__file__)[0]
515 pth = os.path.join(pth, raptor_tercel)
516 fname = os.path.abspath(pth)
517 print (fname)
518 self.spi0.add_verilog_source(fname, platform)
519
520 # add the main core
521 pth = os.path.split(__file__)[0]
522 pth = os.path.join(pth, '../external_core_top.v')
523 fname = os.path.abspath(pth)
524 with open(fname) as f:
525 platform.add_file(fname, f)
526
527 return m
528
529 def ports(self):
530 # puzzlingly the only IO ports needed are peripheral pins,
531 # and at the moment that's just UART tx/rx.
532 ports = []
533 ports += [self.uart.tx_o, self.uart.rx_i]
534 if hasattr(self, "hyperram"):
535 ports += list(self.hyperram.ports())
536 if hasattr(self, "ddrphy"):
537 if hasattr(self.ddrphy, "pads"): # real PHY
538 ports += list(self.ddrphy.pads.fields.values())
539 else: # FakePHY, get at the dfii pads, stops deletion of nets
540 for phase in self.dramcore.dfii.master.phases:
541 print ("dfi master", phase)
542 ports += list(phase.fields.values())
543 for phase in self.dramcore.dfii.slave.phases:
544 print ("dfi master", phase)
545 ports += list(phase.fields.values())
546 for phase in self.dramcore.dfii._inti.phases:
547 print ("dfi master", phase)
548 ports += list(phase.fields.values())
549 ports += [ClockSignal(), ResetSignal()]
550 return ports
551
552 def build_platform(fpga, firmware):
553
554 # create a platform selected from the toolchain.
555 platform_kls = {'versa_ecp5': VersaECP5Platform,
556 'versa_ecp5_85': VersaECP5Platform85,
557 'ulx3s': ULX3S_85F_Platform,
558 'arty_a7': ArtyA7_100Platform,
559 'isim': IcarusVersaPlatform,
560 'sim': None,
561 }[fpga]
562 toolchain = {'arty_a7': "yosys_nextpnr",
563 'versa_ecp5': 'Trellis',
564 'versa_ecp5_85': 'Trellis',
565 'isim': 'Trellis',
566 'ulx3s': 'Trellis',
567 'sim': None,
568 }.get(fpga, None)
569 dram_cls = {'arty_a7': None,
570 'versa_ecp5': MT41K64M16,
571 'versa_ecp5_85': MT41K64M16,
572 #'versa_ecp5': MT41K256M16,
573 'ulx3s': None,
574 'sim': MT41K256M16,
575 'isim': MT41K64M16,
576 }.get(fpga, None)
577 if platform_kls is not None:
578 platform = platform_kls(toolchain=toolchain)
579 if fpga == 'versa_ecp5_85':
580 platform.speed = "7" # HACK. speed grade 7, sigh
581 else:
582 platform = None
583
584 print ("platform", fpga, firmware, platform)
585
586 # set clock frequency
587 clk_freq = 70e6
588 if fpga == 'sim':
589 clk_freq = 100e6
590 if fpga == 'isim':
591 clk_freq = 50e6 # below 50 mhz, stops DRAM being enabled
592 if fpga == 'versa_ecp5':
593 clk_freq = 50e6 # crank right down to test hyperram
594 if fpga == 'versa_ecp5_85':
595 clk_freq = 55e6
596 if fpga == 'arty_a7':
597 clk_freq = 50e6
598 if fpga == 'ulx3s':
599 clk_freq = 40.0e6
600
601 # select a firmware address
602 fw_addr = None
603 if firmware is not None:
604 fw_addr = 0x0000_0000
605
606 print ("fpga", fpga, "firmware", firmware)
607
608 # get UART resource pins
609 if platform is not None:
610 uart_pins = platform.request("uart", 0)
611 else:
612 uart_pins = Record([('tx', 1), ('rx', 1)], name="uart_0")
613
614 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
615 ddr_pins = None
616 if (clk_freq > 50e6 and platform is not None and
617 fpga in ['versa_ecp5', 'versa_ecp5_85', 'arty_a7', 'isim']):
618 ddr_pins = platform.request("ddr3", 0,
619 dir={"dq":"-", "dqs":"-"},
620 xdr={"rst": 4, "clk":4, "a":4,
621 "ba":4, "clk_en":4,
622 "odt":4, "ras":4, "cas":4, "we":4,
623 "cs": 4})
624
625 # Get SPI resource pins
626 spi_0_pins = None
627 if platform is not None and fpga in ['rcs_arctic_tern_bmc_card']:
628 if toolchain == 'Trellis':
629 # The ECP5 series FPGAs handle the SPI clock directly on
630 # the FPGA configuration Flash device
631 spi_0_pins = platform.request("spi_0", 0,
632 dir={"dq":"io", "cs_n":"o"},
633 xdr={"dq": 1, "cs_n": 1})
634 else:
635 spi_0_pins = platform.request("spi_0", 0,
636 dir={"dq":"io", "cs_n":"o", "clk":"o"},
637 xdr={"dq": 1, "cs_n": 1, "clk": 0})
638
639 # Get HyperRAM pins
640 hyperram_pins = None
641 if platform is None:
642 hyperram_pins = HyperRAMPads()
643 elif fpga in ['isim']:
644 hyperram_ios = HyperRAMResource(0, cs_n="B11",
645 dq="D4 D3 F4 F3 G2 H2 D2 E2",
646 rwds="U13", rst_n="T13", ck_p="V10",
647 # ck_n="D12" - for later (DDR)
648 attrs=Attrs(IOSTANDARD="LVCMOS33"))
649 platform.add_resources(hyperram_ios)
650 hyperram_pins = platform.request("hyperram")
651 print ("isim a7 hyperram", hyperram_ios)
652 # Digilent Arty A7-100t
653 elif platform is not None and fpga in ['arty_a7']:
654 hyperram_ios = HyperRAMResource(0, cs_n="B11",
655 dq="D4 D3 F4 F3 G2 H2 D2 E2",
656 rwds="U13", rst_n="T13", ck_p="V10",
657 # ck_n="D12" - for later (DDR)
658 attrs=Attrs(IOSTANDARD="LVCMOS33"))
659 platform.add_resources(hyperram_ios)
660 hyperram_pins = platform.request("hyperram")
661 print ("arty a7 hyperram", hyperram_ios)
662 # VERSA ECP5
663 elif platform is not None and fpga in ['versa_ecp5', 'versa_ecp5_85']:
664 hyperram_ios = HyperRAMResource(0, cs_n="B13",
665 dq="E14 C10 B10 E12 D12 A9 D11 D14",
666 rwds="C14", rst_n="E13", ck_p="D13",
667 attrs=Attrs(IO_TYPE="LVCMOS33"))
668 platform.add_resources(hyperram_ios)
669 hyperram_pins = platform.request("hyperram")
670 print ("versa ecp5 hyperram", hyperram_ios)
671 print ("hyperram pins", hyperram_pins)
672
673 # set up the SOC
674 soc = DDR3SoC(fpga=fpga, dram_cls=dram_cls,
675 # check microwatt_soc.h for these
676 ddrphy_addr=0xff000000, # DRAM_INIT_BASE firmware base
677 dramcore_addr=0xc8000000, # DRAM_CTRL_BASE
678 ddr_addr=0x40000000, # DRAM_BASE
679 spi0_addr=0x10000000, # SPI0_BASE
680 spi0_cfg_addr=0xc0003000, # SPI0_CTRL_BASE
681 hyperram_addr=0xa0000000, # HYPERRAM_BASE
682 fw_addr=fw_addr,
683 #fw_addr=None,
684 ddr_pins=ddr_pins,
685 uart_pins=uart_pins,
686 spi_0_pins=spi_0_pins,
687 hyperram_pins=hyperram_pins,
688 firmware=firmware,
689 clk_freq=clk_freq,
690 add_cpu=True)
691
692 if toolchain == 'Trellis':
693 # add -abc9 option to yosys synth_ecp5
694 #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
695 #os.environ['NMIGEN_synth_opts'] = '-abc9'
696 os.environ['NMIGEN_synth_opts'] = '-nowidelut'
697
698 if platform is not None:
699 # build and upload it
700 if fpga == 'isim':
701 platform.build(soc, do_program=False,
702 do_build=True, build_dir="build_simsoc")
703 else:
704 platform.build(soc, do_program=True)
705 else:
706 # for now, generate verilog
707 vl = verilog.convert(soc, ports=soc.ports())
708 with open("ls2.v", "w") as f:
709 f.write(vl)
710
711
712 # urrr this gets exec()d by the build process without arguments
713 # which screws up. use the arty_a7_ls2.py etc. with no arguments
714 if __name__ == '__main__':
715 fpga = None
716 firmware = None
717 if len(sys.argv) >= 2:
718 fpga = sys.argv[1]
719 if len(sys.argv) >= 3:
720 firmware = sys.argv[2]
721 build_platform(fpga, firmware)