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