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