more work on orangecrab dram
[ls2.git] / src / ls2.py
index f7586cf2263ea6e0b42945455b12197d0d484374..cb620c21c86e6dbdd08541de76943bab7830f091 100644 (file)
@@ -9,14 +9,15 @@
 # under EU Grants 871528 and 957073, under the LGPLv3+ License
 
 from nmigen import (Module, Elaboratable, DomainRenamer, Record,
-                    Signal, Cat, Const, ClockSignal, ResetSignal)
+                    Signal, Cat, Const, ClockSignal, ResetSignal,
+                    )
 from nmigen.build.dsl import Attrs
 from nmigen.cli import verilog
 from nmigen.lib.cdc import ResetSynchronizer
 from nmigen_soc import wishbone, memory
 from nmigen_soc.memory import MemoryMap
 from nmigen.utils import log2_int
-
+from nmigen_boards.resources.interface import UARTResource
 from nmigen_stdio.serial import AsyncSerial
 
 # HyperRAM
@@ -58,6 +59,7 @@ from nmigen_boards.versa_ecp5 import VersaECP5Platform85 # custom board
 from nmigen_boards.ulx3s import ULX3S_85F_Platform
 from nmigen_boards.arty_a7 import ArtyA7_100Platform
 from nmigen_boards.test.blinky import Blinky
+from nmigen_boards.orangecrab_r0_2 import OrangeCrabR0_2_85k_Platform
 from icarusversa import IcarusVersaPlatform
 # Clock-Reset Generator (works for all ECP5 platforms)
 from ecp5_crg import ECP5CRG
@@ -287,12 +289,19 @@ class DDR3SoC(SoC, Elaboratable):
 
         # set up clock request generator
         pod_bits = 25
+        sync_bits = 26
+        need_bridge=False
         if fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim', 'ulx3s',
-                    'orangecrab']:
-            if fpga in ['isim']:
-                pod_bits = 6
+                    'orangecrab','orangecrab_isim', 'rcs_arctic_tern_bmc_card']:
+            if fpga in ['isim','orangecrab_isim']:
+                pod_bits = 5
+                sync_bits = 6
+            if fpga in ['orangecrab', 'orangecrab_sim',
+                        'rcs_arctic_tern_bmc_card']:
+                need_bridge=True
             self.crg = ECP5CRG(clk_freq, dram_clk_freq=dram_clk_freq,
-                               pod_bits=pod_bits)
+                               pod_bits=pod_bits, sync_bits=sync_bits,
+                               need_bridge=need_bridge)
         if fpga in ['arty_a7']:
             self.crg = ArtyA7CRG(clk_freq)
 
@@ -300,9 +309,10 @@ class DDR3SoC(SoC, Elaboratable):
         if self.dram_clk_freq is None:
             self.dram_clk_freq = clk_freq
 
-        # set up CPU, with 64-to-32-bit downconverters
+        # set up CPU, with 64-to-32-bit downconverters, and a delayed Reset
         if add_cpu:
             self.cpu = ExternalCore(name="ext_core")
+
             cvtdbus = wishbone.Interface(addr_width=30, data_width=32,
                                          granularity=8, features={'stall'})
             cvtibus = wishbone.Interface(addr_width=30, data_width=32,
@@ -484,8 +494,7 @@ class DDR3SoC(SoC, Elaboratable):
                                  slave_clock_domain="dramsync",
                                  address_width=self.ddrphy.bus.addr_width,
                                  data_width=self.ddrphy.bus.data_width,
-                                 granularity=self.ddrphy.bus.granularity,
-                                 master_features={'stall'})
+                                 granularity=self.ddrphy.bus.granularity)
             self.ddrphy_async_br = pabr
 
             # Set up Wishbone asynchronous bridge
@@ -502,8 +511,7 @@ class DDR3SoC(SoC, Elaboratable):
                                 slave_clock_domain="dramsync",
                                 address_width=self.dramcore.bus.addr_width,
                                 data_width=self.dramcore.bus.data_width,
-                                granularity=self.dramcore.bus.granularity,
-                                master_features={'stall'})
+                                granularity=self.dramcore.bus.granularity)
             self.dramcore_async_br = dac
 
             # Set up Wishbone asynchronous bridge
@@ -520,8 +528,7 @@ class DDR3SoC(SoC, Elaboratable):
                                 slave_clock_domain="dramsync",
                                 address_width=self.drambone.bus.addr_width,
                                 data_width=self.drambone.bus.data_width,
-                                granularity=self.drambone.bus.granularity,
-                                master_features={'stall'})
+                                granularity=self.drambone.bus.granularity)
             self.drambone_async_br = bab
 
         if ddr_pins is not None:
@@ -532,7 +539,8 @@ class DDR3SoC(SoC, Elaboratable):
 
         # additional SRAM at address if DRAM is not also at 0x0
         # (TODO, check Flash, and HyperRAM as well)
-        if (ddr_pins is None or ddr_addr != 0x0) and fw_addr != 0:
+        if ((ddr_pins is None or ddr_addr != 0x0) and fw_addr != 0 and
+            hyperram_addr[0] != 0x0):
             print ("SRAM 0x8000 at address 0x0")
             sram_width = 32
             self.sram = SRAMPeripheral(size=0x8000,
@@ -544,6 +552,8 @@ class DDR3SoC(SoC, Elaboratable):
         if spi_0_pins is not None and fpga in ['sim',
                                              'isim',
                                              'rcs_arctic_tern_bmc_card',
+                                             'orangecrab',
+                                             'orangecrab_isim',
                                              'versa_ecp5',
                                              'versa_ecp5_85',
                                              'arty_a7']:
@@ -554,6 +564,8 @@ class DDR3SoC(SoC, Elaboratable):
             if fpga in ['versa_ecp5',
                         'versa_ecp5_85',
                         'rcs_arctic_tern_bmc_card',
+                        'orangecrab',
+                        'orangecrab_isim',
                         'isim']:
                 spi0_is_lattice_ecp5_clk = True
 
@@ -575,7 +587,7 @@ class DDR3SoC(SoC, Elaboratable):
         # Ethernet MAC
         if ethmac_0_pins is not None and fpga in ['versa_ecp5',
                                                   'versa_ecp5_85',
-                                                  'isim']:
+                                                  'isim']: # not orangecrab
             self.eth_irq = IRQLine()
             # The OpenCores Ethernet MAC contains two independent Wishbone
             # interfaces, a slave (configuration) interface and a master (DMA)
@@ -588,11 +600,14 @@ class DDR3SoC(SoC, Elaboratable):
         # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
         # Esden, sold by 1bitsquared, only doing one CS_N enable at the
         # moment
-        if hyperram_pins is not None:
-            self.hyperram = HyperRAM(io=hyperram_pins, phy_kls=HyperRAMPHY,
-                                     features={'stall'},
-                                     latency=7) # Winbond W956D8MBYA
-            self._decoder.add(self.hyperram.bus, addr=hyperram_addr)
+        self.hyperram = []
+        for i, (pins, hraddr) in enumerate(zip(hyperram_pins, hyperram_addr)):
+            hr = HyperRAM(io=pins, phy_kls=HyperRAMPHY,
+                             name="hyperram%d" % i,
+                             features={'stall'},
+                             latency=7) # Winbond W956D8MBYA
+            self._decoder.add(hr.bus, addr=hraddr)
+            self.hyperram.append(hr)
 
         self.memory_map = self._decoder.bus.memory_map
 
@@ -601,7 +616,7 @@ class DDR3SoC(SoC, Elaboratable):
 
     def elaborate(self, platform):
         m = Module()
-        comb = m.d.comb
+        comb, sync = m.d.comb, m.d.sync
 
         # add the peripherals and clock-reset-generator
         if platform is not None and hasattr(self, "crg"):
@@ -640,10 +655,6 @@ class DDR3SoC(SoC, Elaboratable):
             m.submodules.extcore = self.cpu
             m.submodules.dbuscvt = self.dbusdowncvt
             m.submodules.ibuscvt = self.ibusdowncvt
-            # create stall sigs, assume wishbone classic
-            #ibus, dbus = self.cvtibus, self.cvtdbus
-            #comb += ibus.stall.eq(ibus.stb & ~ibus.ack)
-            #comb += dbus.stall.eq(dbus.stb & ~dbus.ack)
 
         m.submodules.arbiter = self._arbiter
         m.submodules.decoder = self._decoder
@@ -685,13 +696,13 @@ class DDR3SoC(SoC, Elaboratable):
                 self.drambone_async_br.add_verilog_source(fname, platform)
 
         # add hyperram module
-        if hasattr(self, "hyperram"):
-            m.submodules.hyperram = hyperram = self.hyperram
+        for i, hr in enumerate(self.hyperram):
+            m.submodules["hyperram%d" % i] = hr
             # grrr, same problem with hyperram: not WB4-pipe compliant
-            comb += hyperram.bus.stall.eq(hyperram.bus.cyc & ~hyperram.bus.ack)
-            # set 3 top CSn lines to zero for now
+            comb += hr.bus.stall.eq(hr.bus.cyc & ~hr.bus.ack)
+            # reset
             if self.fpga == 'arty_a7':
-                comb += hyperram.phy.rst_n.eq(ResetSignal())
+                comb += hr.phy.rst_n.eq(ResetSignal())
 
         # add blinky lights so we know FPGA is alive
         if platform is not None:
@@ -793,8 +804,8 @@ class DDR3SoC(SoC, Elaboratable):
         # and at the moment that's just UART tx/rx.
         ports = []
         ports += [self.uart.tx_o, self.uart.rx_i]
-        if hasattr(self, "hyperram"):
-            ports += list(self.hyperram.ports())
+        for hr in self.hyperram:
+            ports += list(hr.ports())
         if hasattr(self, "ddrphy"):
             if hasattr(self.ddrphy, "pads"): # real PHY
                 ports += list(self.ddrphy.pads.fields.values())
@@ -813,26 +824,34 @@ class DDR3SoC(SoC, Elaboratable):
 
 def build_platform(fpga, firmware):
 
-    # create a platform selected from the toolchain. 
+    # create a platform selected from the toolchain.
     platform_kls =  {'versa_ecp5': VersaECP5Platform,
                      'versa_ecp5_85': VersaECP5Platform85,
                      'ulx3s': ULX3S_85F_Platform,
+                     'orangecrab': OrangeCrabR0_2_85k_Platform,
                      'arty_a7': ArtyA7_100Platform,
                      'isim': IcarusVersaPlatform,
+                     'orangecrab_isim': IcarusVersaPlatform,
+                     'rcs_arctic_tern_bmc_card':None, #TODO
                      'sim': None,
                     }[fpga]
     toolchain = {'arty_a7': "yosys_nextpnr",
                  'versa_ecp5': 'Trellis',
                  'versa_ecp5_85': 'Trellis',
+                 'orangecrab_isim': 'Trellis',
                  'isim': 'Trellis',
                  'ulx3s': 'Trellis',
+                 'rcs_arctic_tern_bmc_card': 'Trellis',
                  'sim': None,
                 }.get(fpga, None)
     dram_cls = {'arty_a7': None,
                  'versa_ecp5': MT41K64M16,
                  'versa_ecp5_85': MT41K64M16,
+                 'orangecrab': MT41K64M16,
+                 'orangecrab_isim': MT41K64M16,
                  #'versa_ecp5': MT41K256M16,
                  'ulx3s': None,
+                 'rcs_arctic_tern_bmc_card': None, #TODO
                  'sim': MT41K256M16,
                  'isim': MT41K64M16,
                 }.get(fpga, None)
@@ -852,24 +871,35 @@ def build_platform(fpga, firmware):
         clk_freq = 100e6
         dram_clk_freq = clk_freq
     if fpga == 'isim':
-        clk_freq = 55e6 # below 50 mhz, stops DRAM being enabled
+        clk_freq = 50e6 # below 50 mhz, stops DRAM being enabled
+        #dram_clk_freq = clk_freq
+        dram_clk_freq = 100e6
     if fpga == 'versa_ecp5':
-        clk_freq = 50e6 # crank right down to test hyperram
-        #dram_clk_freq = 100e6
+        clk_freq = 50e6 # crank right down to timing threshold
+        #dram_clk_freq = 55e6
     if fpga == 'versa_ecp5_85':
         # 50MHz works.  100MHz works.  55MHz does NOT work.
         # Stick with multiples of 50MHz...
         clk_freq = 50e6
         dram_clk_freq = 100e6
     if fpga == 'arty_a7':
-        clk_freq = 50e6
+        clk_freq = 27.0e6 # urrr "working" with the QSPI core (25 mhz does not)
     if fpga == 'ulx3s':
         clk_freq = 40.0e6
+    if fpga == 'orangecrab' or fpga=='orangecrab_isim':
+        clk_freq = 50e6
 
     # merge dram_clk_freq with clk_freq if the same
     if clk_freq == dram_clk_freq:
         dram_clk_freq = None
 
+    # see if dram can be enabled
+    enable_dram = False
+    if dram_clk_freq is not None and dram_clk_freq >= 50e6:
+        enable_dram = True
+    if dram_clk_freq is None and clk_freq >= 50e6:
+        enable_dram = True
+
     # select a firmware address
     fw_addr = None
     if firmware is not None:
@@ -879,20 +909,27 @@ def build_platform(fpga, firmware):
 
     # get UART resource pins
     if platform is not None:
+        if fpga=="orangecrab":
+            # assumes an FT232 USB-UART soldered onto these two pins.
+            orangecrab_uart = UARTResource(0, rx="M18", tx="N17")
+            platform.add_resources([orangecrab_uart])
+
         uart_pins = platform.request("uart", 0)
     else:
         uart_pins = Record([('tx', 1), ('rx', 1)], name="uart_0")
 
     # get DDR resource pins, disable if clock frequency is below 50 mhz for now
     ddr_pins = None
-    if (clk_freq >= 50e6 and platform is not None and
-        fpga in ['versa_ecp5', 'versa_ecp5_85', 'arty_a7', 'isim']):
+    if (enable_dram and platform is not None and
+        fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim',
+                 'orangecrab','orangecrab_isim']): # not yet 'arty_a7',
         ddr_pins = platform.request("ddr3", 0,
                                     dir={"dq":"-", "dqs":"-"},
                                     xdr={"rst": 4, "clk":4, "a":4,
                                          "ba":4, "clk_en":4,
                                          "odt":4, "ras":4, "cas":4, "we":4,
                                          "cs": 4})
+    print ("ddr pins", ddr_pins)
 
     # Get SPI resource pins
     spi_0_pins = None
@@ -932,6 +969,33 @@ def build_platform(fpga, firmware):
         platform.add_resources(spi_0_ios)
         spi_0_pins = platform.request("spi_0", 0)
 
+    orangecrab_enable_spi = False
+    if orangecrab_enable_spi and platform is not None and \
+       fpga in ['orangecrab']:
+       # spi_flash_mosi   <= spi_sdat_o(0) when spi_sdat_oe(0) = '1' else 'Z';
+       # spi_flash_miso   <= spi_sdat_o(1) when spi_sdat_oe(1) = '1' else 'Z';
+       # spi_flash_wp_n   <= spi_sdat_o(2) when spi_sdat_oe(2) = '1' else 'Z';
+       # spi_flash_hold_n <= spi_sdat_o(3) when spi_sdat_oe(3) = '1' else 'Z';
+       # cs_n="U17", clk="U16", miso="T18", mosi="U18", wp_n="R18", hold_n="N18"
+        # each pin needs a separate direction control
+        spi_0_ios = [
+            Resource("spi_0", 0,
+                     Subsignal("dq0",  Pins("U18", dir="io")), #mosi
+                     Subsignal("dq1",  Pins("T18", dir="io")), #miso
+                     Subsignal("dq2",  Pins("R18", dir="io")), #wp_n
+                     Subsignal("dq3",  Pins("N18", dir="io")), #hold_n
+                     # We use USRMCLK instead for clk
+                     # todo: read docs
+                     Subsignal("cs_n", Pins("U17", dir="o")),
+                     # Subsignal("clk",  Pins("U16", dir="o")),
+                     Attrs(PULLMODE="NONE", DRIVE="4", IO_TYPE="LVCMOS33"))
+        ]
+        platform.add_resources(spi_0_ios)
+        spi_0_pins = platform.request("spi_0", 0, dir={"cs_n":"o"},
+                                                  xdr={"dq0":1, "dq1": 1,
+                                                       "dq2":1, "dq3": 1,
+                                                       "cs_n":0})
+
     print ("spiflash pins", spi_0_pins)
 
     # Get Ethernet RMII resource pins
@@ -975,27 +1039,37 @@ def build_platform(fpga, firmware):
     print ("ethmac pins", ethmac_0_pins)
 
     # Get HyperRAM pins
-    hyperram_pins = None
+    hyperram_pins = []
+    hyperram_addr = [0xa000_0000]
     if platform is None:
-        hyperram_pins = HyperRAMPads()
+        hyperram_pins = [HyperRAMPads()]
     elif fpga in ['isim']:
         hyperram_ios = HyperRAMResource(0, cs_n="B13",
                                         dq="E14 C10 B10 E12 D12 A9 D11 D14",
                                         rwds="C14", rst_n="E13", ck_p="D13",
                                         attrs=Attrs(IO_TYPE="LVCMOS33"))
         platform.add_resources(hyperram_ios)
-        hyperram_pins = platform.request("hyperram")
+        hyperram_pins = [platform.request("hyperram")]
         print ("isim a7 hyperram", hyperram_ios)
     # Digilent Arty A7-100t
     elif platform is not None and fpga in ['arty_a7']:
-        hyperram_ios = HyperRAMResource(0, cs_n="V12 V14 U12 U14",
+        hyperram_ios = HyperRAMResource(0, cs_n="B11 B18 G13 D13",
+                                        dq="E15 E16 D15 C15 J15 K15 J18 J17",
+                                        rwds="K16", rst_n="A18", ck_p="A11",
+                                        # ck_n="D12" - for later (DDR)
+                                        attrs=Attrs(IOSTANDARD="LVCMOS33"))
+        platform.add_resources(hyperram_ios)
+        hyperram_ios = HyperRAMResource(1, cs_n="V12 V14 U12 U14",
                                         dq="D4 D3 F4 F3 G2 H2 D2 E2",
                                         rwds="U13", rst_n="T13", ck_p="V10",
                                         # ck_n="V11" - for later (DDR)
                                         attrs=Attrs(IOSTANDARD="LVCMOS33"))
         platform.add_resources(hyperram_ios)
-        hyperram_pins = platform.request("hyperram")
+        hyperram_pins = [platform.request("hyperram", 0),
+                         platform.request("hyperram", 1)]
         print ("arty a7 hyperram", hyperram_ios)
+        hyperram_addr=[0x0000_0000,  # HYPERRAM_BASE1
+                       0x0200_0000] # HYPERRAM_BASE2
     # VERSA ECP5
     elif False and platform is not None and fpga in \
                 ['versa_ecp5', 'versa_ecp5_85']:
@@ -1004,7 +1078,7 @@ def build_platform(fpga, firmware):
                                         rwds="C14", rst_n="E13", ck_p="D13",
                                         attrs=Attrs(IO_TYPE="LVCMOS33"))
         platform.add_resources(hyperram_ios)
-        hyperram_pins = platform.request("hyperram")
+        hyperram_pins = [platform.request("hyperram")]
         print ("versa ecp5 hyperram", hyperram_ios)
     print ("hyperram pins", hyperram_pins)
 
@@ -1018,7 +1092,7 @@ def build_platform(fpga, firmware):
                   spi0_cfg_addr=0xc0006000, # SPI0_CTRL_BASE
                   eth0_cfg_addr=0xc000c000, # ETH0_CTRL_BASE (4k)
                   eth0_irqno=1,             # ETH0_IRQ number (match microwatt)
-                  hyperram_addr=0xa0000000, # HYPERRAM_BASE
+                  hyperram_addr=hyperram_addr, # determined above
                   fw_addr=fw_addr,
                   #fw_addr=None,
                   ddr_pins=ddr_pins,
@@ -1037,13 +1111,18 @@ def build_platform(fpga, firmware):
 
     if toolchain == 'Trellis':
         # add -abc9 option to yosys synth_ecp5
-        #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
-        #os.environ['NMIGEN_synth_opts'] = '-abc9'
-        os.environ['NMIGEN_synth_opts'] = '-nowidelut'
+        os.environ['NMIGEN_synth_opts'] = '-abc9'              # speed
+        # os.environ['NMIGEN_synth_opts'] = '-nowidelut'       # size
+
+    if toolchain == 'yosys_nextpnr':
+        # add --seed 2 to arty a7 compile-time options
+        freq = clk_freq/1e6
+        os.environ['NMIGEN_nextpnr_opts'] = '--seed 3 --freq %.1f' % freq
+        os.environ['NMIGEN_nextpnr_opts'] += ' --timing-allow-fail'
 
     if platform is not None:
         # build and upload it
-        if fpga == 'isim':
+        if fpga == 'isim' or fpga == 'orangecrab_isim':
             platform.build(soc, do_program=False,
                                 do_build=True, build_dir="build_simsoc")
         else: