first cut at Arty A7 Clock-Reset-Generator with S7 PLL
[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 crg import ECPIX5CRG
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 if fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
275 self.crg = ECPIX5CRG(clk_freq)
276 if fpga in ['arty_a7']:
277 self.crg = ArtyA7CRG(clk_freq)
278
279 # set up CPU, with 64-to-32-bit downconverters
280 if add_cpu:
281 self.cpu = ExternalCore(name="ext_core")
282 cvtdbus = wishbone.Interface(addr_width=30, data_width=32,
283 granularity=8, features={'stall'})
284 cvtibus = wishbone.Interface(addr_width=30, data_width=32,
285 granularity=8, features={'stall'})
286 self.dbusdowncvt = WB64to32Convert(self.cpu.dbus, cvtdbus)
287 self.ibusdowncvt = WB64to32Convert(self.cpu.ibus, cvtibus)
288 self._arbiter.add(cvtibus) # I-Cache Master
289 self._arbiter.add(cvtdbus) # D-Cache Master. TODO JTAG master
290 self.cvtibus = cvtibus
291 self.cvtdbus = cvtdbus
292
293 # CPU interrupt controller
294 self.intc = GenericInterruptController(width=len(self.cpu.irq))
295
296 # SRAM (but actually a ROM, for firmware), at address 0x0
297 if fw_addr is not None:
298 sram_width = 32
299 self.bootmem = SRAMPeripheral(size=0x8000, data_width=sram_width,
300 writable=True)
301 if firmware is not None:
302 with open(firmware, "rb") as f:
303 words = iter(lambda: f.read(sram_width // 8), b'')
304 bios = [int.from_bytes(w, "little") for w in words]
305 self.bootmem.init = bios
306 self._decoder.add(self.bootmem.bus, addr=fw_addr) # ROM at fw_addr
307
308 # System Configuration info
309 self.syscon = MicrowattSYSCON(sys_clk_freq=clk_freq,
310 has_uart=(uart_pins is not None))
311 self._decoder.add(self.syscon.bus, addr=0xc0000000) # at 0xc000_0000
312
313 if False:
314 # SRAM (read-writeable BRAM)
315 self.ram = SRAMPeripheral(size=4096)
316 self._decoder.add(self.ram.bus, addr=0x8000000) # at 0x8000_0000
317
318 # UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
319 if uart_pins is not None:
320 # sigh actual UART in microwatt is 8-bit
321 self.uart = UART16550(data_width=8, pins=uart_pins,
322 features={'stall'})
323 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
324 cvtuartbus = wishbone.Interface(addr_width=5, data_width=32,
325 granularity=8,
326 features={'stall'})
327 umap = MemoryMap(addr_width=7, data_width=8, name="uart_map")
328 cvtuartbus.memory_map = umap
329 self._decoder.add(cvtuartbus, addr=0xc0002000) # 16550 UART addr
330 self.cvtuartbus = cvtuartbus
331
332 # SDRAM module using opencores sdr_ctrl
333 """
334 class MT48LC16M16(SDRModule):
335 # geometry
336 nbanks = 4
337 nrows = 8192
338 ncols = 512
339 # timings
340 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
341 tWTR=(2, None),
342 tCCD=(1, None),
343 tRRD=(None, 15))
344 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
345 tRCD=20,
346 tWR=15,
347 tRFC=(None, 66),
348 tFAW=None,
349 tRAS=44)}
350 """
351
352 # DRAM Module
353 if ddr_pins is not None or fpga == 'sim':
354 ddrmodule = dram_cls(clk_freq, "1:2") # match DDR3 ASIC P/N
355
356 #drs = lambda x: x
357 drs = DomainRenamer("dramsync")
358
359 if fpga == 'sim':
360 self.ddrphy = FakePHY(module=ddrmodule,
361 settings=sim_ddr3_settings(clk_freq),
362 verbosity=SDRAM_VERBOSE_DBG,
363 clk_freq=clk_freq)
364 else:
365 self.ddrphy = drs(ECP5DDRPHY(ddr_pins, sys_clk_freq=clk_freq))
366 self._decoder.add(self.ddrphy.bus, addr=ddrphy_addr)
367
368 dramcore = gramCore(phy=self.ddrphy,
369 geom_settings=ddrmodule.geom_settings,
370 timing_settings=ddrmodule.timing_settings,
371 clk_freq=clk_freq)
372 if fpga == 'sim':
373 self.dramcore = dramcore
374 else:
375 self.dramcore = drs(dramcore)
376 self._decoder.add(self.dramcore.bus, addr=dramcore_addr)
377
378 # map the DRAM onto Wishbone, XXX use stall but set classic below
379 drambone = gramWishbone(dramcore, features={'stall'})
380 if fpga == 'sim':
381 self.drambone = drambone
382 else:
383 self.drambone = drs(drambone)
384 self._decoder.add(self.drambone.bus, addr=ddr_addr)
385
386 # SPI controller
387 if spi_0_pins is not None and fpga in ['sim',
388 'rcs_arctic_tern_bmc_card']:
389 # The Lattice ECP5 devices require special handling on the
390 # dedicated SPI clock line, which is shared with the internal
391 # SPI controller used for FPGA bitstream loading.
392 spi0_is_lattice_ecp5_clk = False
393 if platform is not None and fpga in ['versa_ecp5',
394 'rcs_arctic_tern_bmc_card',
395 'isim']:
396 spi0_is_lattice_ecp5_clk = True
397
398 # Tercel contains two independent Wishbone regions, a
399 # configuration region and the direct API access region,
400 # Set the SPI 0 access region to 16MB, as the FPGA
401 # bitstream Flash device is unlikely to be larger than this.
402 # The main SPI Flash (SPI 1) should be set to at
403 # least 28 bits (256MB) to allow the use of large 4BA devices.
404 self.spi0 = Tercel(data_width=32, spi_region_addr_width=24,
405 clk_freq=clk_freq,
406 pins=spi_0_pins,
407 lattice_ecp5_usrmclk=spi0_is_lattice_ecp5_clk)
408 self._decoder.add(self.spi0.bus, addr=spi0_addr)
409 self._decoder.add(self.spi0.cfg_bus, addr=spi0_cfg_addr)
410
411 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
412 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
413 # moment
414 if hyperram_pins is not None:
415 self.hyperram = HyperRAM(io=hyperram_pins, phy_kls=HyperRAMPHY,
416 features={'stall'})
417 self._decoder.add(self.hyperram.bus, addr=hyperram_addr)
418
419 self.memory_map = self._decoder.bus.memory_map
420
421 self.clk_freq = clk_freq
422
423 def elaborate(self, platform):
424 m = Module()
425 comb = m.d.comb
426
427 # add the peripherals and clock-reset-generator
428 if platform is not None and hasattr(self, "crg"):
429 m.submodules.sysclk = self.crg
430
431 if hasattr(self, "bootmem"):
432 m.submodules.bootmem = self.bootmem
433 m.submodules.syscon = self.syscon
434 if hasattr(self, "ram"):
435 m.submodules.ram = self.ram
436 if hasattr(self, "uart"):
437 m.submodules.uart = self.uart
438 comb += self.uart.cts_i.eq(1)
439 comb += self.uart.dsr_i.eq(1)
440 comb += self.uart.ri_i.eq(0)
441 comb += self.uart.dcd_i.eq(1)
442 # sigh connect up the wishbone bus manually to deal with
443 # the mis-match on the data
444 uartbus = self.uart.bus
445 comb += uartbus.adr.eq(self.cvtuartbus.adr)
446 comb += uartbus.stb.eq(self.cvtuartbus.stb)
447 comb += uartbus.cyc.eq(self.cvtuartbus.cyc)
448 comb += uartbus.sel.eq(self.cvtuartbus.sel)
449 comb += uartbus.we.eq(self.cvtuartbus.we)
450 comb += uartbus.dat_w.eq(self.cvtuartbus.dat_w) # drops 8..31
451 comb += self.cvtuartbus.dat_r.eq(uartbus.dat_r) # drops 8..31
452 comb += self.cvtuartbus.ack.eq(uartbus.ack)
453 # aaand with the WB4-pipeline-to-WB3-classic mismatch, sigh
454 comb += uartbus.stall.eq(uartbus.cyc & ~uartbus.ack)
455 comb += self.cvtuartbus.stall.eq(uartbus.stall)
456 if hasattr(self, "cpu"):
457 m.submodules.intc = self.intc
458 m.submodules.extcore = self.cpu
459 m.submodules.dbuscvt = self.dbusdowncvt
460 m.submodules.ibuscvt = self.ibusdowncvt
461 # create stall sigs, assume wishbone classic
462 #ibus, dbus = self.cvtibus, self.cvtdbus
463 #comb += ibus.stall.eq(ibus.stb & ~ibus.ack)
464 #comb += dbus.stall.eq(dbus.stb & ~dbus.ack)
465
466 m.submodules.arbiter = self._arbiter
467 m.submodules.decoder = self._decoder
468 if hasattr(self, "ddrphy"):
469 m.submodules.ddrphy = self.ddrphy
470 m.submodules.dramcore = self.dramcore
471 m.submodules.drambone = drambone = self.drambone
472 # grrr, same problem with drambone: not WB4-pipe compliant
473 comb += drambone.bus.stall.eq(drambone.bus.cyc & ~drambone.bus.ack)
474
475 # add hyperram module
476 if hasattr(self, "hyperram"):
477 m.submodules.hyperram = hyperram = self.hyperram
478 # grrr, same problem with hyperram: not WB4-pipe compliant
479 comb += hyperram.bus.stall.eq(hyperram.bus.cyc & ~hyperram.bus.ack)
480
481 # add blinky lights so we know FPGA is alive
482 if platform is not None:
483 m.submodules.blinky = Blinky()
484
485 # connect the arbiter (of wishbone masters)
486 # to the decoder (addressing wishbone slaves)
487 comb += self._arbiter.bus.connect(self._decoder.bus)
488
489 if hasattr(self, "cpu"):
490 # wire up the CPU interrupts
491 comb += self.cpu.irq.eq(self.intc.ip)
492
493 if platform is None:
494 return m
495
496 # add uart16550 verilog source. assumes a directory
497 # structure where ls2 has been checked out in a common
498 # subdirectory as https://github.com/freecores/uart16550
499 opencores_16550 = "../../uart16550/rtl/verilog"
500 pth = os.path.split(__file__)[0]
501 pth = os.path.join(pth, opencores_16550)
502 fname = os.path.abspath(pth)
503 print (fname)
504 self.uart.add_verilog_source(fname, platform)
505
506 if hasattr(self, "spi0"):
507 # add Tercel verilog source. assumes a directory
508 # structure where ls2 has been checked out in a common
509 # subdirectory as https://git.libre-soc.org/git/microwatt.git
510 raptor_tercel = "../../microwatt/tercel"
511 pth = os.path.split(__file__)[0]
512 pth = os.path.join(pth, raptor_tercel)
513 fname = os.path.abspath(pth)
514 print (fname)
515 self.spi0.add_verilog_source(fname, platform)
516
517 # add the main core
518 pth = os.path.split(__file__)[0]
519 pth = os.path.join(pth, '../external_core_top.v')
520 fname = os.path.abspath(pth)
521 with open(fname) as f:
522 platform.add_file(fname, f)
523
524 return m
525
526 def ports(self):
527 # puzzlingly the only IO ports needed are peripheral pins,
528 # and at the moment that's just UART tx/rx.
529 ports = []
530 ports += [self.uart.tx_o, self.uart.rx_i]
531 if hasattr(self, "hyperram"):
532 ports += list(self.hyperram.ports())
533 if hasattr(self, "ddrphy"):
534 if hasattr(self.ddrphy, "pads"): # real PHY
535 ports += list(self.ddrphy.pads.fields.values())
536 else: # FakePHY, get at the dfii pads, stops deletion of nets
537 for phase in self.dramcore.dfii.master.phases:
538 print ("dfi master", phase)
539 ports += list(phase.fields.values())
540 for phase in self.dramcore.dfii.slave.phases:
541 print ("dfi master", phase)
542 ports += list(phase.fields.values())
543 for phase in self.dramcore.dfii._inti.phases:
544 print ("dfi master", phase)
545 ports += list(phase.fields.values())
546 ports += [ClockSignal(), ResetSignal()]
547 return ports
548
549 if __name__ == "__main__":
550
551 # create a platform selected from the toolchain. defaults to VERSA_ECP5
552 # only VERSA_ECP5 will work for now because of the DDR3 module
553 fpga = "versa_ecp5"
554 if len(sys.argv) >= 2:
555 fpga = sys.argv[1]
556 platform_kls = {'versa_ecp5': VersaECP5Platform,
557 'versa_ecp5_85': VersaECP5Platform85,
558 'ulx3s': ULX3S_85F_Platform,
559 'arty_a7': ArtyA7_100Platform,
560 'isim': IcarusVersaPlatform,
561 'sim': None,
562 }[fpga]
563 toolchain = {'arty_a7': "yosys_nextpnr",
564 'versa_ecp5': 'Trellis',
565 'versa_ecp5_85': 'Trellis',
566 'isim': 'Trellis',
567 'ulx3s': 'Trellis',
568 'sim': None,
569 }.get(fpga, None)
570 dram_cls = {'arty_a7': None,
571 'versa_ecp5': MT41K64M16,
572 'versa_ecp5_85': MT41K64M16,
573 #'versa_ecp5': MT41K256M16,
574 'ulx3s': None,
575 'sim': MT41K256M16,
576 'isim': MT41K64M16,
577 }.get(fpga, None)
578 if platform_kls is not None:
579 platform = platform_kls(toolchain=toolchain)
580 else:
581 platform = None
582
583 # set clock frequency
584 clk_freq = 70e6
585 if fpga == 'sim':
586 clk_freq = 100e6
587 if fpga == 'versa_ecp5':
588 clk_freq = 50e6 # crank right down to test hyperram
589 if fpga == 'versa_ecp5_85':
590 clk_freq = 55e6
591 if fpga == 'arty_a7':
592 clk_freq = 25e6
593
594 # select a firmware file
595 firmware = None
596 fw_addr = None
597 if len(sys.argv) >= 3:
598 firmware = sys.argv[2]
599 fw_addr = 0x0000_0000
600
601 # get UART resource pins
602 if platform is not None:
603 uart_pins = platform.request("uart", 0)
604 else:
605 uart_pins = Record([('tx', 1), ('rx', 1)], name="uart_0")
606
607 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
608 ddr_pins = None
609 if (clk_freq > 50e6 and platform is not None and
610 fpga in ['versa_ecp5', 'versa_ecp5_85', 'arty_a7', 'isim']):
611 ddr_pins = platform.request("ddr3", 0,
612 dir={"dq":"-", "dqs":"-"},
613 xdr={"rst": 4, "clk":4, "a":4,
614 "ba":4, "clk_en":4,
615 "odt":4, "ras":4, "cas":4, "we":4,
616 "cs": 4})
617
618 # Get SPI resource pins
619 spi_0_pins = None
620 if platform is not None and fpga in ['rcs_arctic_tern_bmc_card']:
621 if toolchain == 'Trellis':
622 # The ECP5 series FPGAs handle the SPI clock directly on
623 # the FPGA configuration Flash device
624 spi_0_pins = platform.request("spi_0", 0,
625 dir={"dq":"io", "cs_n":"o"},
626 xdr={"dq": 1, "cs_n": 1})
627 else:
628 spi_0_pins = platform.request("spi_0", 0,
629 dir={"dq":"io", "cs_n":"o", "clk":"o"},
630 xdr={"dq": 1, "cs_n": 1, "clk": 0})
631
632 # Get HyperRAM pins
633 hyperram_pins = None
634 if platform is not None and fpga in ['versa_ecp5', 'versa_ecp5_85']:
635 hyperram_ios = HyperRAMResource(0, cs_n="B13",
636 dq="E14 C10 B10 E12 D12 A9 D11 D14",
637 rwds="C14", rst_n="E13", ck_p="D13",
638 attrs=Attrs(IO_TYPE="LVCMOS33"))
639 platform.add_resources(hyperram_ios)
640 hyperram_pins = platform.request("hyperram")
641 else:
642 hyperram_pins = HyperRAMPads()
643
644 # set up the SOC
645 soc = DDR3SoC(fpga=fpga, dram_cls=dram_cls,
646 # check microwatt_soc.h for these
647 ddrphy_addr=0xff000000, # DRAM_INIT_BASE firmware base
648 dramcore_addr=0xc8000000, # DRAM_CTRL_BASE
649 ddr_addr=0x40000000, # DRAM_BASE
650 spi0_addr=0x10000000, # SPI0_BASE
651 spi0_cfg_addr=0xc0003000, # SPI0_CTRL_BASE
652 hyperram_addr=0xa0000000, # HYPERRAM_BASE
653 fw_addr=fw_addr,
654 #fw_addr=None,
655 ddr_pins=ddr_pins,
656 uart_pins=uart_pins,
657 spi_0_pins=spi_0_pins,
658 hyperram_pins=hyperram_pins,
659 firmware=firmware,
660 clk_freq=clk_freq,
661 add_cpu=True)
662
663 if toolchain == 'Trellis':
664 # add -abc9 option to yosys synth_ecp5
665 #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
666 #os.environ['NMIGEN_synth_opts'] = '-abc9'
667 os.environ['NMIGEN_synth_opts'] = '-nowidelut'
668
669 if platform is not None:
670 # build and upload it
671 if fpga == 'isim':
672 platform.build(soc, do_program=False,
673 do_build=True, build_dir="build_simsoc")
674 else:
675 platform.build(soc, do_program=True)
676 else:
677 # for now, generate verilog
678 vl = verilog.convert(soc, ports=soc.ports())
679 with open("ls2.v", "w") as f:
680 f.write(vl)
681