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
.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
20 from nmigen_stdio
.serial
import AsyncSerial
23 from nmigen_boards
.resources
.memory
import HyperRAMResource
24 from lambdasoc
.periph
.hyperram
import HyperRAM
, HyperRAMPads
, HyperRAMPHY
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
38 from gram
.common
import (PhySettings
, get_cl_cw
, get_sys_latency
,
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
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
58 def sim_ddr3_settings(clk_freq
=100e6
):
59 tck
= 2/(2*2*clk_freq
)
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
)
74 dfi_databits
=4*databits
,
79 rdcmdphase
=rdcmdphase
,
80 wrcmdphase
=wrcmdphase
,
83 read_latency
=2 + cl_sys_latency
+ 2 + log2_int(4//nphases
) + 4,
84 write_latency
=cwl_sys_latency
88 class WB64to32Convert(Elaboratable
):
89 """Microwatt IO wishbone slave 64->32 bits converter
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.
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.
98 def __init__(self
, master
, slave
):
102 def elaborate(self
, platform
):
104 comb
, sync
= m
.d
.comb
, m
.d
.sync
105 master
, slave
= self
.master
, self
.slave
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)
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)
123 # Start cycle downstream
124 sync
+= slave
.cyc
.eq(1)
125 sync
+= slave
.stb
.eq(1)
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
)
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
))
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.
142 with m
.If(master
.we
):
143 sync
+= slave
.dat_w
.eq(master
.dat_w
[:32])
144 sync
+= slave
.sel
.eq(master
.sel
[:4])
146 # Wait for ack on BOTTOM half
147 m
.next
= "WAIT_ACK_BOT"
150 with m
.If(master
.we
):
151 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
152 sync
+= slave
.sel
.eq(master
.sel
[4:])
154 # Bump LSB of address
155 sync
+= slave
.adr
[0].eq(1)
157 # Wait for ack on TOP half
158 m
.next
= "WAIT_ACK_TOP"
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)
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
)
173 # Do we have a "top" part as well ?
174 with m
.If(has_top_r
):
176 with m
.If(master
.we
):
177 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
178 sync
+= slave
.sel
.eq(master
.sel
[4:])
180 # Bump address and set STB
181 sync
+= slave
.adr
[0].eq(1)
182 sync
+= slave
.stb
.eq(1)
185 m
.next
= "WAIT_ACK_TOP"
188 # We are done, ack up, clear cyc downstram
189 sync
+= slave
.cyc
.eq(0)
190 sync
+= slave
.stb
.eq(0)
192 # And ack & unstall upstream
193 sync
+= master
.ack
.eq(1)
194 if hasattr(master
, "stall"):
195 sync
+= master
.stall
.eq(0)
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)
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
)
212 # We are done, ack up, clear cyc downstram
213 sync
+= slave
.cyc
.eq(0)
214 sync
+= slave
.stb
.eq(0)
216 # And ack & unstall upstream
217 sync
+= master
.ack
.eq(1)
218 if hasattr(master
, "stall"):
219 sync
+= master
.stall
.eq(0)
227 class DDR3SoC(SoC
, Elaboratable
):
228 def __init__(self
, *,
231 uart_pins
, spi_0_pins
,
232 ddr_pins
, ddrphy_addr
, dramcore_addr
, ddr_addr
,
235 spi0_addr
, spi0_cfg_addr
,
241 # wishbone routing is as follows:
254 # +---decoder----+--------+---------+-------+
256 # uart XICS CSRs DRAM XIP SPI HyperRAM
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,
262 features
={"cti", "bte", "stall"})
263 self
._decoder
= wishbone
.Decoder(addr_width
=30, data_width
=32,
265 features
={"cti", "bte", "stall"})
267 # default firmware name
269 firmware
= "firmware/main.bin"
271 # set up clock request generator
272 self
.crg
= ECPIX5CRG(clk_freq
)
274 # set up CPU, with 64-to-32-bit downconverters
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
288 # CPU interrupt controller
289 self
.intc
= GenericInterruptController(width
=len(self
.cpu
.irq
))
291 # SRAM (but actually a ROM, for firmware), at address 0x0
292 if fw_addr
is not None:
294 self
.bootmem
= SRAMPeripheral(size
=0x8000, data_width
=sram_width
,
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
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
309 # SRAM (read-writeable BRAM)
310 self
.ram
= SRAMPeripheral(size
=4096)
311 self
._decoder
.add(self
.ram
.bus
, addr
=0x8000000) # at 0x8000_0000
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
,
318 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
319 cvtuartbus
= wishbone
.Interface(addr_width
=5, data_width
=32,
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
327 # SDRAM module using opencores sdr_ctrl
329 class MT48LC16M16(SDRModule):
335 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
339 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
348 if ddr_pins
is not None or fpga
== 'sim':
349 ddrmodule
= dram_cls(clk_freq
, "1:2") # match DDR3 ASIC P/N
352 drs
= DomainRenamer("dramsync")
355 self
.ddrphy
= FakePHY(module
=ddrmodule
,
356 settings
=sim_ddr3_settings(clk_freq
),
357 verbosity
=SDRAM_VERBOSE_DBG
,
360 self
.ddrphy
= drs(ECP5DDRPHY(ddr_pins
, sys_clk_freq
=clk_freq
))
361 self
._decoder
.add(self
.ddrphy
.bus
, addr
=ddrphy_addr
)
363 dramcore
= gramCore(phy
=self
.ddrphy
,
364 geom_settings
=ddrmodule
.geom_settings
,
365 timing_settings
=ddrmodule
.timing_settings
,
368 self
.dramcore
= dramcore
370 self
.dramcore
= drs(dramcore
)
371 self
._decoder
.add(self
.dramcore
.bus
, addr
=dramcore_addr
)
373 # map the DRAM onto Wishbone, XXX use stall but set classic below
374 drambone
= gramWishbone(dramcore
, features
={'stall'})
376 self
.drambone
= drambone
378 self
.drambone
= drs(drambone
)
379 self
._decoder
.add(self
.drambone
.bus
, addr
=ddr_addr
)
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',
391 spi0_is_lattice_ecp5_clk
= True
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,
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
)
406 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
407 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
409 if hyperram_pins
is not None:
410 self
.hyperram
= HyperRAM(io
=hyperram_pins
, phy_kls
=HyperRAMPHY
,
412 self
._decoder
.add(self
.hyperram
.bus
, addr
=hyperram_addr
)
414 self
.memory_map
= self
._decoder
.bus
.memory_map
416 self
.clk_freq
= clk_freq
418 def elaborate(self
, platform
):
422 # add the peripherals and clock-reset-generator
423 if platform
is not None:
424 m
.submodules
.sysclk
= self
.crg
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)
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
)
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
)
476 # add blinky lights so we know FPGA is alive
477 if platform
is not None:
478 m
.submodules
.blinky
= Blinky()
480 # connect the arbiter (of wishbone masters)
481 # to the decoder (addressing wishbone slaves)
482 comb
+= self
._arbiter
.bus
.connect(self
._decoder
.bus
)
484 if hasattr(self
, "cpu"):
485 # wire up the CPU interrupts
486 comb
+= self
.cpu
.irq
.eq(self
.intc
.ip
)
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
)
499 self
.uart
.add_verilog_source(fname
, platform
)
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
)
510 self
.spi0
.add_verilog_source(fname
, platform
)
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
)
522 # puzzlingly the only IO ports needed are peripheral pins,
523 # and at the moment that's just UART tx/rx.
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()]
544 if __name__
== "__main__":
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
549 if len(sys
.argv
) >= 2:
551 platform_kls
= {'versa_ecp5': VersaECP5Platform
,
552 'ulx3s': ULX3S_85F_Platform
,
553 'arty_a7': ArtyA7_100Platform
,
554 'isim': IcarusVersaPlatform
,
557 toolchain
= {'arty_a7': "yosys_nextpnr",
558 'versa_ecp5': 'Trellis',
563 dram_cls
= {'arty_a7': None,
564 'versa_ecp5': MT41K64M16
,
565 #'versa_ecp5': MT41K256M16,
570 if platform_kls
is not None:
571 platform
= platform_kls(toolchain
=toolchain
)
575 # set clock frequency
579 if fpga
== 'versa_ecp5':
582 # select a firmware file
585 if len(sys
.argv
) >= 3:
586 firmware
= sys
.argv
[2]
587 fw_addr
= 0x0000_0000
589 # get UART resource pins
590 if platform
is not None:
591 uart_pins
= platform
.request("uart", 0)
593 uart_pins
= Record([('tx', 1), ('rx', 1)], name
="uart_0")
595 # get DDR resource pins
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,
602 "odt":4, "ras":4, "cas":4, "we":4,
605 # Get SPI resource pins
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})
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})
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")
629 hyperram_pins
= HyperRAMPads()
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
644 spi_0_pins
=spi_0_pins
,
645 hyperram_pins
=hyperram_pins
,
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'
656 if platform
is not None:
657 # build and upload it
659 platform
.build(soc
, do_program
=False,
660 do_build
=True, build_dir
="build_simsoc")
662 platform
.build(soc
, do_program
=True)
664 # for now, generate verilog
665 vl
= verilog
.convert(soc
, ports
=soc
.ports())
666 with
open("ls2.v", "w") as f
: