# Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
# Copyright (c) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
+# Copyright (C) 2022 Raptor Engineering, LLC <support@raptorengineering.com>
#
# Based on code from LambaConcept, from the gram example which is BSD-2-License
# https://github.com/jeanthom/gram/tree/master/examples
from lambdasoc.periph import Peripheral
from lambdasoc.soc.base import SoC
from soc.bus.uart_16550 import UART16550 # opencores 16550 uart
+from soc.bus.tercel import Tercel # SPI XIP master
from soc.bus.external_core import ExternalCore # external libresoc/microwatt
from soc.bus.wb_downconvert import WishboneDownConvert
from soc.bus.syscon import MicrowattSYSCON
def __init__(self, *,
fpga,
dram_cls,
- uart_pins, ddr_pins,
+ uart_pins, spi_0_pins, ddr_pins,
ddrphy_addr, dramcore_addr,
ddr_addr, fw_addr=0x0000_0000,
firmware=None,
+ spi0_addr, spi0_cfg_addr,
clk_freq=50e6,
add_cpu=True):
# |
# arbiter
# |
- # +---decoder----+--------+
- # | | | |
- # uart XICS CSRs DRAM
+ # +---decoder----+--------+---------+
+ # | | | | |
+ # uart XICS CSRs DRAM XIP SPI
# set up wishbone bus arbiter and decoder. arbiter routes,
# decoder maps local-relative addressed satellites to global addresses
self.drambone = drs(drambone)
self._decoder.add(self.drambone.bus, addr=ddr_addr)
+ # SPI controller
+ if spi_0_pins is not None or fpga == 'sim':
+ # The Lattice ECP5 devices require special handling on the dedicated SPI clock line,
+ # which is shared with the internal SPI controller used for FPGA bitstream loading.
+ spi0_is_lattice_ecp5_clk = False
+ if platform is not None and fpga in ['versa_ecp5', 'rcs_arctic_tern_bmc_card', 'isim']:
+ spi0_is_lattice_ecp5_clk = True
+
+ # Tercel contains two independent Wishbone regions, a configuration
+ # region and the direct API access region,
+ # Set the SPI 0 access region to 16MB, as the FPGA bitstream Flash device
+ # is unlikely to be larger than this.
+ # The main SPI Flash (SPI 1) should be set to at least 28 bits (256MB) to
+ # allow the use of large 4BA devices.
+ self.spi0 = Tercel(data_width=32, spi_region_addr_width=24,
+ clk_freq=clk_freq,
+ pins=spi_0_pins,
+ lattice_ecp5_usrmclk=spi0_is_lattice_ecp5_clk)
+ self._decoder.add(self.spi0.bus, addr=spi0_addr)
+ self._decoder.add(self.spi0.cfg_bus, addr=spi0_cfg_addr)
+
self.memory_map = self._decoder.bus.memory_map
self.clk_freq = clk_freq
print (fname)
self.uart.add_verilog_source(fname, platform)
+ # add Tercel verilog source. assumes a directory
+ # structure where ls2 has been checked out in a common
+ # subdirectory as https://git.libre-soc.org/git/microwatt.git
+ raptor_tercel = "../../microwatt/tercel"
+ pth = os.path.split(__file__)[0]
+ pth = os.path.join(pth, raptor_tercel)
+ fname = os.path.abspath(pth)
+ print (fname)
+ self.spi0.add_verilog_source(fname, platform)
+
# add the main core
pth = os.path.split(__file__)[0]
pth = os.path.join(pth, '../external_core_top.v')
"odt":4, "ras":4, "cas":4, "we":4,
"cs": 4})
+ # Get SPI resource pins
+ if platform is not None:
+ if toolchain == 'Trellis':
+ # The ECP5 series FPGAs handle the SPI clock directly on the FPGA configuration Flash device
+ spi_0_pins = platform.request("spi_0", 0,
+ dir={"dq":"io", "cs_n":"o"},
+ xdr={"dq": 1, "cs_n": 1})
+ else:
+ spi_0_pins = platform.request("spi_0", 0,
+ dir={"dq":"io", "cs_n":"o", "clk":"o"},
+ xdr={"dq": 1, "cs_n": 1, "clk": 0})
+
# set up the SOC
soc = DDR3SoC(fpga=fpga, dram_cls=dram_cls,
# check microwatt_soc.h for these
ddrphy_addr=0xff000000, # DRAM_INIT_BASE firmware base
dramcore_addr=0xc8000000, # DRAM_CTRL_BASE
ddr_addr=0x40000000, # DRAM_BASE
+ spi0_addr=0x10000000, # SPI0_BASE
+ spi0_cfg_addr=0xc0003000, # SPI0_CTRL_BASE
fw_addr=fw_addr,
#fw_addr=None,
ddr_pins=ddr_pins,
uart_pins=uart_pins,
+ spi_0_pins=spi_0_pins,
firmware=firmware,
clk_freq=clk_freq,
add_cpu=True)