Add initial Tercel SPI controller
authorRaptor Engineering Development Team <support@raptorengineering.com>
Mon, 14 Mar 2022 00:33:24 +0000 (19:33 -0500)
committerRaptor Engineering Development Team <support@raptorengineering.com>
Wed, 16 Mar 2022 18:12:11 +0000 (13:12 -0500)
NOTE: Still needs testing on physical hardware,
waiting for Arctic Tern support.

src/ls2.py

index bcb4b3b39c13fd401ba48a80c3edba7fce169efb..9ca2e86564f916cf071ca5b9aec751c7aee9f459 100644 (file)
@@ -1,5 +1,6 @@
 # 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
@@ -22,6 +23,7 @@ from lambdasoc.periph.timer import TimerPeripheral
 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
@@ -219,10 +221,11 @@ class DDR3SoC(SoC, Elaboratable):
     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):
 
@@ -239,9 +242,9 @@ class DDR3SoC(SoC, Elaboratable):
         #          |
         #       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
@@ -366,6 +369,27 @@ class DDR3SoC(SoC, Elaboratable):
                 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
@@ -447,6 +471,16 @@ class DDR3SoC(SoC, Elaboratable):
         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')
@@ -536,16 +570,31 @@ if __name__ == "__main__":
                                          "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)