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