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>
5 # Based on code from LambaConcept, from the gram example which is BSD-2-License
6 # https://github.com/jeanthom/gram/tree/master/examples
8 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
9 # under EU Grants 871528 and 957073, under the LGPLv3+ License
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
19 from nmigen_stdio
.serial
import AsyncSerial
22 from nmigen_boards
.resources
.memory
import HyperRAMResource
23 from lambdasoc
.periph
.hyperram
import HyperRAM
, HyperRAMPads
, HyperRAMPHY
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
37 from gram
.common
import (PhySettings
, get_cl_cw
, get_sys_latency
,
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
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
57 def sim_ddr3_settings(clk_freq
=100e6
):
58 tck
= 2/(2*2*clk_freq
)
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
)
73 dfi_databits
=4*databits
,
78 rdcmdphase
=rdcmdphase
,
79 wrcmdphase
=wrcmdphase
,
82 read_latency
=2 + cl_sys_latency
+ 2 + log2_int(4//nphases
) + 4,
83 write_latency
=cwl_sys_latency
87 class WB64to32Convert(Elaboratable
):
88 """Microwatt IO wishbone slave 64->32 bits converter
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.
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.
97 def __init__(self
, master
, slave
):
101 def elaborate(self
, platform
):
103 comb
, sync
= m
.d
.comb
, m
.d
.sync
104 master
, slave
= self
.master
, self
.slave
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)
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)
122 # Start cycle downstream
123 sync
+= slave
.cyc
.eq(1)
124 sync
+= slave
.stb
.eq(1)
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
)
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
))
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.
141 with m
.If(master
.we
):
142 sync
+= slave
.dat_w
.eq(master
.dat_w
[:32])
143 sync
+= slave
.sel
.eq(master
.sel
[:4])
145 # Wait for ack on BOTTOM half
146 m
.next
= "WAIT_ACK_BOT"
149 with m
.If(master
.we
):
150 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
151 sync
+= slave
.sel
.eq(master
.sel
[4:])
153 # Bump LSB of address
154 sync
+= slave
.adr
[0].eq(1)
156 # Wait for ack on TOP half
157 m
.next
= "WAIT_ACK_TOP"
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)
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
)
172 # Do we have a "top" part as well ?
173 with m
.If(has_top_r
):
175 with m
.If(master
.we
):
176 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
177 sync
+= slave
.sel
.eq(master
.sel
[4:])
179 # Bump address and set STB
180 sync
+= slave
.adr
[0].eq(1)
181 sync
+= slave
.stb
.eq(1)
184 m
.next
= "WAIT_ACK_TOP"
187 # We are done, ack up, clear cyc downstram
188 sync
+= slave
.cyc
.eq(0)
189 sync
+= slave
.stb
.eq(0)
191 # And ack & unstall upstream
192 sync
+= master
.ack
.eq(1)
193 if hasattr(master
, "stall"):
194 sync
+= master
.stall
.eq(0)
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)
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
)
211 # We are done, ack up, clear cyc downstram
212 sync
+= slave
.cyc
.eq(0)
213 sync
+= slave
.stb
.eq(0)
215 # And ack & unstall upstream
216 sync
+= master
.ack
.eq(1)
217 if hasattr(master
, "stall"):
218 sync
+= master
.stall
.eq(0)
226 class DDR3SoC(SoC
, Elaboratable
):
227 def __init__(self
, *,
230 uart_pins
, spi_0_pins
,
231 ddr_pins
, ddrphy_addr
, dramcore_addr
, ddr_addr
,
234 spi0_addr
, spi0_cfg_addr
,
240 # wishbone routing is as follows:
253 # +---decoder----+--------+---------+-------+
255 # uart XICS CSRs DRAM XIP SPI HyperRAM
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,
261 features
={"cti", "bte", "stall"})
262 self
._decoder
= wishbone
.Decoder(addr_width
=30, data_width
=32,
264 features
={"cti", "bte", "stall"})
266 # default firmware name
268 firmware
= "firmware/main.bin"
270 # set up clock request generator
271 self
.crg
= ECPIX5CRG(clk_freq
)
273 # set up CPU, with 64-to-32-bit downconverters
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
287 # CPU interrupt controller
288 self
.intc
= GenericInterruptController(width
=len(self
.cpu
.irq
))
290 # SRAM (but actually a ROM, for firmware), at address 0x0
291 if fw_addr
is not None:
293 self
.bootmem
= SRAMPeripheral(size
=0x8000, data_width
=sram_width
,
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
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
308 # SRAM (read-writeable BRAM)
309 self
.ram
= SRAMPeripheral(size
=4096)
310 self
._decoder
.add(self
.ram
.bus
, addr
=0x8000000) # at 0x8000_0000
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
,
317 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
318 cvtuartbus
= wishbone
.Interface(addr_width
=5, data_width
=32,
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
326 # SDRAM module using opencores sdr_ctrl
328 class MT48LC16M16(SDRModule):
334 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
338 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
347 if ddr_pins
is not None or fpga
== 'sim':
348 ddrmodule
= dram_cls(clk_freq
, "1:2") # match DDR3 ASIC P/N
351 drs
= DomainRenamer("dramsync")
354 self
.ddrphy
= FakePHY(module
=ddrmodule
,
355 settings
=sim_ddr3_settings(clk_freq
),
356 verbosity
=SDRAM_VERBOSE_DBG
,
359 self
.ddrphy
= drs(ECP5DDRPHY(ddr_pins
, sys_clk_freq
=clk_freq
))
360 self
._decoder
.add(self
.ddrphy
.bus
, addr
=ddrphy_addr
)
362 dramcore
= gramCore(phy
=self
.ddrphy
,
363 geom_settings
=ddrmodule
.geom_settings
,
364 timing_settings
=ddrmodule
.timing_settings
,
367 self
.dramcore
= dramcore
369 self
.dramcore
= drs(dramcore
)
370 self
._decoder
.add(self
.dramcore
.bus
, addr
=dramcore_addr
)
372 # map the DRAM onto Wishbone, XXX use stall but set classic below
373 drambone
= gramWishbone(dramcore
, features
={'stall'})
375 self
.drambone
= drambone
377 self
.drambone
= drs(drambone
)
378 self
._decoder
.add(self
.drambone
.bus
, addr
=ddr_addr
)
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',
390 spi0_is_lattice_ecp5_clk
= True
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,
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
)
405 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
406 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
408 if hyperram_pins
is not None:
409 self
.hyperram
= HyperRAM(io
=hyperram_pins
, phy_kls
=HyperRAMPHY
,
411 self
._decoder
.add(self
.hyperram
.bus
, addr
=hyperram_addr
)
413 self
.memory_map
= self
._decoder
.bus
.memory_map
415 self
.clk_freq
= clk_freq
417 def elaborate(self
, platform
):
421 # add the peripherals and clock-reset-generator
422 if platform
is not None:
423 m
.submodules
.sysclk
= self
.crg
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)
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
)
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
)
475 # add blinky lights so we know FPGA is alive
476 if platform
is not None:
477 m
.submodules
.blinky
= Blinky()
479 # connect the arbiter (of wishbone masters)
480 # to the decoder (addressing wishbone slaves)
481 comb
+= self
._arbiter
.bus
.connect(self
._decoder
.bus
)
483 if hasattr(self
, "cpu"):
484 # wire up the CPU interrupts
485 comb
+= self
.cpu
.irq
.eq(self
.intc
.ip
)
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
)
498 self
.uart
.add_verilog_source(fname
, platform
)
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
)
508 self
.spi0
.add_verilog_source(fname
, platform
)
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
)
520 # puzzlingly the only IO ports needed are peripheral pins,
521 # and at the moment that's just UART tx/rx.
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()]
542 if __name__
== "__main__":
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
547 if len(sys
.argv
) >= 2:
549 platform_kls
= {'versa_ecp5': VersaECP5Platform
,
550 'ulx3s': ULX3S_85F_Platform
,
551 'arty_a7': ArtyA7_100Platform
,
552 'isim': IcarusVersaPlatform
,
555 toolchain
= {'arty_a7': "yosys_nextpnr",
556 'versa_ecp5': 'Trellis',
561 dram_cls
= {'arty_a7': None,
562 'versa_ecp5': MT41K64M16
,
563 #'versa_ecp5': MT41K256M16,
568 if platform_kls
is not None:
569 platform
= platform_kls(toolchain
=toolchain
)
573 # set clock frequency
577 if fpga
== 'versa_ecp5':
580 # select a firmware file
583 if len(sys
.argv
) >= 3:
584 firmware
= sys
.argv
[2]
585 fw_addr
= 0x0000_0000
587 # get UART resource pins
588 if platform
is not None:
589 uart_pins
= platform
.request("uart", 0)
591 uart_pins
= Record([('tx', 1), ('rx', 1)], name
="uart_0")
593 # get DDR resource pins
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,
600 "odt":4, "ras":4, "cas":4, "we":4,
603 # Get SPI resource pins
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})
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})
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")
627 hyperram_pins
= HyperRAMPads()
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
642 spi_0_pins
=spi_0_pins
,
643 hyperram_pins
=hyperram_pins
,
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'
654 if platform
is not None:
655 # build and upload it
657 platform
.build(soc
, do_program
=False,
658 do_build
=True, build_dir
="build_simsoc")
660 platform
.build(soc
, do_program
=True)
662 # for now, generate verilog
663 vl
= verilog
.convert(soc
, ports
=soc
.ports())
664 with
open("ls2.v", "w") as f
: