# 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
from nmigen_boards.resources.memory import HyperRAMResource
from lambdasoc.periph.hyperram import HyperRAM, HyperRAMPads, HyperRAMPHY
+from lambdasoc.periph.event import IRQLine
from lambdasoc.periph.intc import GenericInterruptController
from lambdasoc.periph.sram import SRAMPeripheral
from lambdasoc.periph.timer import TimerPeripheral
from soc.bus.opencores_ethmac import EthMAC # OpenCores 10/100 Ethernet MAC
from soc.bus.external_core import ExternalCore # external libresoc/microwatt
from soc.bus.wb_downconvert import WishboneDownConvert
+from soc.bus.wb_async import WBAsyncBridge
from soc.bus.syscon import MicrowattSYSCON
+from soc.interrupts.xics import XICS_ICP, XICS_ICS
# DDR3
from gram.common import (PhySettings, get_cl_cw, get_sys_latency,
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
class DDR3SoC(SoC, Elaboratable):
def __init__(self, *,
fpga,
- dram_cls,
- uart_pins, spi_0_pins, ethmac_0_pins,
- ddr_pins, ddrphy_addr, dramcore_addr, ddr_addr,
- fw_addr=0x0000_0000,
- firmware=None,
- spi0_addr, spi0_cfg_addr,
- eth0_cfg_addr, eth0_irqno,
+ dram_cls=None,
+ uart_pins=None, spi_0_pins=None, ethmac_0_pins=None,
+ ddr_pins=None, ddrphy_addr=None,
+ dramcore_addr=None, ddr_addr=None,
+ fw_addr=0x0000_0000, firmware=None,
+ uart_addr=None, uart_irqno=0,
+ spi0_addr=None, spi0_cfg_addr=None,
+ eth0_cfg_addr=None, eth0_irqno=None,
hyperram_addr=None,
hyperram_pins=None,
+ xics_icp_addr=None, xics_ics_addr=None,
clk_freq=50e6,
+ dram_clk_freq=None,
add_cpu=True):
# wishbone routing is as follows:
# |
# 64to32DownCvt
# |
- # arbiter------------------------------------------+
- # | |
- # +---decoder----+--------+---------+-------+--------+ |
- # | | | | | | | |
- # uart XICS CSRs DRAM XIP SPI HyperRAM EthMAC
+ # arbiter------------------------------------------------------+
+ # | |
+ # +---decoder----+--------+---------------+-------------+--------+ |
+ # | | | | | | | |
+ # | | | WBAsyncBridge | | | |
+ # | | | | | | | |
+ # uart XICS CSRs DRAM XIP SPI HyperRAM EthMAC
# set up wishbone bus arbiter and decoder. arbiter routes,
# decoder maps local-relative addressed satellites to global addresses
# set up clock request generator
pod_bits = 25
- if fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim', 'ulx3s']:
+ sync_bits = 26
+ if fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim', 'ulx3s',
+ 'orangecrab']:
if fpga in ['isim']:
- pod_bits = 6
- self.crg = ECP5CRG(clk_freq, pod_bits)
+ pod_bits = 5
+ sync_bits = 6
+ self.crg = ECP5CRG(clk_freq, dram_clk_freq=dram_clk_freq,
+ pod_bits=pod_bits, sync_bits=sync_bits)
if fpga in ['arty_a7']:
self.crg = ArtyA7CRG(clk_freq)
- # set up CPU, with 64-to-32-bit downconverters
+ self.dram_clk_freq = dram_clk_freq
+ if self.dram_clk_freq is None:
+ self.dram_clk_freq = clk_freq
+
+ # 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,
self.cvtibus = cvtibus
self.cvtdbus = cvtdbus
- # CPU interrupt controller
+ # CPU interrupt controller, needs stall to be added, also
+ # compat with wishbone.Interface
self.intc = GenericInterruptController(width=len(self.cpu.irq))
+ self.xics_icp = icp = XICS_ICP()
+ self.xics_ics = ics = XICS_ICS()
+ self.int_level_i = self.xics_ics.int_level_i
+
+ self.pbus = pbus = wishbone.Interface(name="xics_icp_bus",
+ addr_width=6, data_width=32,
+ granularity=8, features={'stall'})
+ self.sbus = sbus = wishbone.Interface(name="xics_ics_bus",
+ addr_width=10, data_width=32,
+ granularity=8, features={'stall'})
+ pmap = MemoryMap(addr_width=8, data_width=8, name="icp_map")
+ pbus.memory_map = pmap
+ self._decoder.add(pbus, addr=xics_icp_addr) # ICP addr
+
+ smap = MemoryMap(addr_width=12, data_width=8, name="ics_map")
+ sbus.memory_map = smap
+ self._decoder.add(sbus, addr=xics_ics_addr) # ICP addr
+
# SRAM (but actually a ROM, for firmware)
if fw_addr is not None:
self._decoder.add(self.bootmem.bus, addr=fw_addr) # ROM at fw_addr
# System Configuration info
- # offset executable ELF payload at 1 megabyte offset (1<<20)
- spi_offset = 1<<20 if (spi_0_pins is not None) else None
+ # offset executable ELF payload at 6 megabyte offset (2<<20)
+ spi_offset = 2<<20 if (spi_0_pins is not None) else None
dram_offset = ddr_addr if (ddr_pins is not None) else None
self.syscon = MicrowattSYSCON(sys_clk_freq=clk_freq,
+ mem_clk_freq=self.dram_clk_freq,
has_uart=(uart_pins is not None),
spi_offset=spi_offset,
dram_addr=dram_offset)
# UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
if uart_pins is not None:
# sigh actual UART in microwatt is 8-bit
+ self.uart_irq = IRQLine()
self.uart = UART16550(data_width=8, pins=uart_pins,
- features={'stall'})
+ features={'stall'},
+ irq=self.uart_irq)
# but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
+ # strictly speaking this is a nmigen-soc "sparse" arrangement
+ # which should be handled by MemoryMap, but needs investigation
cvtuartbus = wishbone.Interface(addr_width=5, data_width=32,
granularity=8,
features={'stall'})
umap = MemoryMap(addr_width=7, data_width=8, name="uart_map")
cvtuartbus.memory_map = umap
- self._decoder.add(cvtuartbus, addr=0xc0002000) # 16550 UART addr
+ self._decoder.add(cvtuartbus, addr=uart_addr) # 16550 UART addr
self.cvtuartbus = cvtuartbus
+ self.intc.add_irq(self.uart.irq, index=uart_irqno)
# SDRAM module using opencores sdr_ctrl
"""
tRAS=44)}
"""
- # DRAM Module
- if ddr_pins is not None or fpga == 'sim':
- ddrmodule = dram_cls(clk_freq, "1:2") # match DDR3 ASIC P/N
+ # DRAM Module. first, create the (triple) modules:
+ # * DDR PHY
+ # * gram Core: presents PHY with a DFI Interface
+ # * gram Bone (aka gram-with-wishbone) connects wishbone to DFI
+ # from there it gets a little complicated because of supporting
+ # several options: simulation, synchronous, and asynchronous clocks.
+ # dram_clk_freq can *never* be set equal to clk_freq, if it is,
+ # it's assumed to be synchronous, and the dram Domains need renaming
+
+ if ddr_pins is not None: # or fpga == 'sim':
+ ddrmodule = dram_cls(self.dram_clk_freq, "1:2") # match DDR3 P/N
+
+ # remap both the sync domain (wherever it occurs) and
+ # the sync2x domain, if dram frequency is specified and
+ # not equal to the core clock
+ drs = None
+ if dram_clk_freq is not None or fpga == 'sim':
+ drs = lambda x: x
+ else:
+ drs = DomainRenamer({"sync": "dramsync",
+ "sync2x": "dramsync2x"})
- #drs = lambda x: x
- drs = DomainRenamer("dramsync")
+ features = set()
+ if dram_clk_freq is None:
+ features.add("stall")
+ # create the PHY (fake one for sim)
if fpga == 'sim':
+ settings = sim_ddr3_settings(self.dram_clk_freq)
self.ddrphy = FakePHY(module=ddrmodule,
- settings=sim_ddr3_settings(clk_freq),
+ settings=settings,
verbosity=SDRAM_VERBOSE_DBG,
- clk_freq=clk_freq)
+ clk_freq=self.dram_clk_freq)
else:
- self.ddrphy = drs(ECP5DDRPHY(ddr_pins, sys_clk_freq=clk_freq))
- self._decoder.add(self.ddrphy.bus, addr=ddrphy_addr)
+ self.ddrphy = drs(ECP5DDRPHY(ddr_pins,
+ #features=features,
+ sys_clk_freq=self.dram_clk_freq))
+ # create the core (bridge from PHY to DFI)
dramcore = gramCore(phy=self.ddrphy,
geom_settings=ddrmodule.geom_settings,
timing_settings=ddrmodule.timing_settings,
- clk_freq=clk_freq)
- if fpga == 'sim':
- self.dramcore = dramcore
- else:
- self.dramcore = drs(dramcore)
- self._decoder.add(self.dramcore.bus, addr=dramcore_addr)
-
- # map the DRAM onto Wishbone, XXX use stall but set classic below
- drambone = gramWishbone(dramcore, features={'stall'})
- if fpga == 'sim':
- self.drambone = drambone
- else:
- self.drambone = drs(drambone)
- self._decoder.add(self.drambone.bus, addr=ddr_addr)
-
- # additional SRAM at address if DRAM is not also at 0x0
- # (TODO, check Flash, and HyperRAM as well)
- if ddr_addr != 0x0:
- sram_width = 32
- self.bootmem = SRAMPeripheral(size=0x8000,
- data_width=sram_width,
- writable=True)
- self._decoder.add(self.bootmem.bus, addr=0x0) # RAM at 0x0
+ #features=features,
+ clk_freq=self.dram_clk_freq)
+ self.dramcore = drs(dramcore)
+
+ # create the wishbone presentation (wishbone to DFI)
+ drambone = gramWishbone(dramcore, features=features)
+ self.drambone = drs(drambone)
+
+ # this is the case where sys_clk === dram_clk. no ASync Bridge
+ # needed, so just let the phy core and wb-dfi be connected
+ # directly to WB decoder. both are running in "sync" domain
+ # (because of the DomainRenamer, above)
+
+ if ddr_pins is not None and dram_clk_freq is None:
+ self.ddrphy_bus = self.ddrphy.bus
+ self.dramcore_bus = self.dramcore.bus
+ self.drambone_bus = self.drambone.bus
+
+ # this covers the case where sys_clk != dram_clk: three separate
+ # ASync Bridges are constructed (!) and the interface that's to
+ # be wired to the WB decoder is the async bus because that's running
+ # in the "sync" domain.
+
+ if ddr_pins is not None and dram_clk_freq is not None:
+ # Set up Wishbone asynchronous bridge
+ pabus = wishbone.Interface(addr_width=self.ddrphy.bus.addr_width,
+ data_width=self.ddrphy.bus.data_width,
+ granularity=self.ddrphy.bus.granularity,
+ features={'stall'})
+ self.ddrphy_bus = pabus
+ self.ddrphy_bus.memory_map = self.ddrphy.bus.memory_map
+
+ pabr = WBAsyncBridge(master_bus=self.ddrphy_bus,
+ slave_bus=self.ddrphy.bus,
+ master_clock_domain=None,
+ slave_clock_domain="dramsync",
+ address_width=self.ddrphy.bus.addr_width,
+ data_width=self.ddrphy.bus.data_width,
+ granularity=self.ddrphy.bus.granularity)
+ self.ddrphy_async_br = pabr
+
+ # Set up Wishbone asynchronous bridge
+ dab = wishbone.Interface(addr_width=self.dramcore.bus.addr_width,
+ data_width=self.dramcore.bus.data_width,
+ granularity=self.dramcore.bus.granularity,
+ features={'stall'})
+ self.dramcore_bus = dab
+ self.dramcore_bus.memory_map = self.dramcore.bus.memory_map
+
+ dac = WBAsyncBridge(master_bus=self.dramcore_bus,
+ slave_bus=self.dramcore.bus,
+ master_clock_domain=None,
+ slave_clock_domain="dramsync",
+ address_width=self.dramcore.bus.addr_width,
+ data_width=self.dramcore.bus.data_width,
+ granularity=self.dramcore.bus.granularity)
+ self.dramcore_async_br = dac
+
+ # Set up Wishbone asynchronous bridge
+ bab = wishbone.Interface(addr_width=self.drambone.bus.addr_width,
+ data_width=self.drambone.bus.data_width,
+ granularity=self.drambone.bus.granularity,
+ features={'stall'})
+ self.drambone_bus = bab
+ self.drambone_bus.memory_map = self.drambone.bus.memory_map
+
+ bab = WBAsyncBridge(master_bus=self.drambone_bus,
+ slave_bus=self.drambone.bus,
+ master_clock_domain=None,
+ slave_clock_domain="dramsync",
+ address_width=self.drambone.bus.addr_width,
+ data_width=self.drambone.bus.data_width,
+ granularity=self.drambone.bus.granularity)
+ self.drambone_async_br = bab
+
+ if ddr_pins is not None:
+ # Add wishbone decoders
+ self._decoder.add(self.dramcore_bus, addr=dramcore_addr)
+ self._decoder.add(self.drambone_bus, addr=ddr_addr)
+ self._decoder.add(self.ddrphy_bus, addr=ddrphy_addr)
+
+ # 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 and
+ hyperram_addr[0] != 0x0):
+ print ("SRAM 0x8000 at address 0x0")
+ sram_width = 32
+ self.sram = SRAMPeripheral(size=0x8000,
+ data_width=sram_width,
+ writable=True)
+ self._decoder.add(self.sram.bus, addr=0x0) # RAM at 0x0
# SPI controller
if spi_0_pins is not None and fpga in ['sim',
'isim',
'rcs_arctic_tern_bmc_card',
+ 'orangecrab',
'versa_ecp5',
'versa_ecp5_85',
'arty_a7']:
if fpga in ['versa_ecp5',
'versa_ecp5_85',
'rcs_arctic_tern_bmc_card',
+ 'orangecrab',
'isim']:
spi0_is_lattice_ecp5_clk = True
if ethmac_0_pins is not None and fpga in ['versa_ecp5',
'versa_ecp5_85',
'isim']:
+ self.eth_irq = IRQLine()
# The OpenCores Ethernet MAC contains two independent Wishbone
# interfaces, a slave (configuration) interface and a master (DMA)
# interface.
- self.eth0 = EthMAC(pins=ethmac_0_pins)
+ self.eth0 = EthMAC(pins=ethmac_0_pins, irq=self.eth_irq)
self._arbiter.add(self.eth0.master_bus)
self._decoder.add(self.eth0.slave_bus, addr=eth0_cfg_addr)
self.intc.add_irq(self.eth0.irq, index=eth0_irqno)
# 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
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"):
m.submodules.sysclk = self.crg
+ if hasattr(self, "sram"):
+ m.submodules.sram = self.sram
if hasattr(self, "bootmem"):
m.submodules.bootmem = self.bootmem
m.submodules.syscon = self.syscon
comb += self.uart.ri_i.eq(0)
comb += self.uart.dcd_i.eq(1)
# sigh connect up the wishbone bus manually to deal with
- # the mis-match on the data
+ # the mis-match on the data. nmigen-soc "sparse" MemoryMap
+ # should be able to deal with this. TODO, investigate
uartbus = self.uart.bus
comb += uartbus.adr.eq(self.cvtuartbus.adr)
comb += uartbus.stb.eq(self.cvtuartbus.stb)
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
m.submodules.ddrphy = self.ddrphy
m.submodules.dramcore = self.dramcore
m.submodules.drambone = drambone = self.drambone
- # grrr, same problem with drambone: not WB4-pipe compliant
- comb += drambone.bus.stall.eq(drambone.bus.cyc & ~drambone.bus.ack)
+
+ # add async wishbone bridges
+ if hasattr(self, "ddrphy_async_br"):
+ m.submodules.ddrphy_async_br = self.ddrphy_async_br
+ if hasattr(self, "dramcore_async_br"):
+ m.submodules.dramcore_async_br = self.dramcore_async_br
+ if hasattr(self, "drambone_async_br"):
+ m.submodules.drambone_async_br = self.drambone_async_br
+
+ # grrr, same problem with WB async bridge: not WB4-pipe compliant
+ dab = self.ddrphy_bus
+ if hasattr(dab, "stall"):
+ comb += dab.stall.eq(dab.cyc & ~dab.ack)
+ dab = self.dramcore_bus
+ if hasattr(dab, "stall"):
+ comb += dab.stall.eq(dab.cyc & ~dab.ack)
+ dab = self.drambone_bus
+ comb += dab.stall.eq(dab.cyc & ~dab.ack)
+
+ # add wb async bridge verilog source. assumes directory structure
+ # where bridge has been checked out in a common subdirectory with:
+ # git clone https://github.com/alexforencich/verilog-wishbone.git
+ # git checkout d1fa24a0
+ verilog_wishbone = "../../verilog-wishbone/rtl"
+ pth = os.path.split(__file__)[0]
+ pth = os.path.join(pth, verilog_wishbone)
+ fname = os.path.abspath(pth)
+ print (fname)
+ if hasattr(self, "ddrphy_async_br"):
+ self.dramcore_async_br.add_verilog_source(fname, platform)
+ if hasattr(self, "drambone_async_br"):
+ 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:
comb += self._arbiter.bus.connect(self._decoder.bus)
if hasattr(self, "cpu"):
- # wire up the CPU interrupts
- comb += self.cpu.irq.eq(self.intc.ip)
+ m.submodules.xics_icp = icp = self.xics_icp
+ m.submodules.xics_ics = ics = self.xics_ics
+ comb += icp.ics_i.eq(ics.icp_o) # connect ICS to ICP
+ comb += self.cpu.irq.eq(icp.core_irq_o) # connect ICP to core
+
+ # wire up the CPU interrupts from the GenericInterrupt
+ comb += self.int_level_i.eq(self.intc.ip)
+
+ # grrr
+ comb += self.pbus.stall.eq(self.pbus.cyc & ~self.pbus.ack)
+ comb += self.sbus.stall.eq(self.sbus.cyc & ~self.sbus.ack)
+
+ # and also wire up make_wb_layout() to wishbone.Interface.
+ # really, XICS_ICS and XICS_ICP both need to be converted
+ # to use wishbone.Interface and this all goes
+ comb += icp.bus.adr.eq(self.pbus.adr)
+ comb += icp.bus.dat_w.eq(self.pbus.dat_w)
+ comb += icp.bus.cyc.eq(self.pbus.cyc)
+ comb += icp.bus.stb.eq(self.pbus.stb)
+ comb += icp.bus.we.eq(self.pbus.we)
+ comb += self.pbus.ack.eq(icp.bus.ack)
+ comb += self.pbus.dat_r.eq(icp.bus.dat_r)
+ comb += ics.bus.adr.eq(self.sbus.adr)
+ comb += ics.bus.dat_w.eq(self.sbus.dat_w)
+ comb += ics.bus.cyc.eq(self.sbus.cyc)
+ comb += ics.bus.stb.eq(self.sbus.stb)
+ comb += ics.bus.we.eq(self.sbus.we)
+ comb += self.sbus.ack.eq(ics.bus.ack)
+ comb += self.sbus.dat_r.eq(ics.bus.dat_r)
if platform is None:
return m
# 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())
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,
'sim': None,
toolchain = {'arty_a7': "yosys_nextpnr",
'versa_ecp5': 'Trellis',
'versa_ecp5_85': 'Trellis',
+ 'orangecrab': 'Trellis',
'isim': 'Trellis',
'ulx3s': 'Trellis',
'sim': None,
dram_cls = {'arty_a7': None,
'versa_ecp5': MT41K64M16,
'versa_ecp5_85': MT41K64M16,
+ 'orangecrab': MT41K64M16,
#'versa_ecp5': MT41K256M16,
'ulx3s': None,
'sim': MT41K256M16,
# set clock frequency
clk_freq = 70e6
+ dram_clk_freq = None
if fpga == 'sim':
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 = 55e6 # crank right down to test hyperram
+ 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':
+ 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
# 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']): # not yet 'arty_a7',
ddr_pins = platform.request("ddr3", 0,
dir={"dq":"-", "dqs":"-"},
- xdr={"rst": 1, "clk":4, "a":4,
+ 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
- if platform is not None and \
+ if False and platform is not None and \
fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
# Override here to get FlashResource out of the way and enable Tercel
# direct access to the SPI flash.
platform.add_resources(spi_0_ios)
spi_0_pins = platform.request("spi_0", 0)
+
+ if 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
ethmac_0_pins = platform.request("ethmac_0", 0,
dir={"mtx_clk":"i", "mtxd":"o",
"mtxen":"o",
- "mtxerr":"o", "mrx_clk":"i",
+ "mtxerr":"o", "mrx_clk":"i",
"mrxd":"i",
- "mrxdv":"i", "mrxerr":"i",
+ "mrxdv":"i", "mrxerr":"i",
"mcoll":"i",
"mcrs":"i", "mdc":"o", "md":"io"},
- xdr={"mtx_clk": 0, "mtxd": 0,
+ xdr={"mtx_clk": 0, "mtxd": 0,
"mtxen": 0,
- "mtxerr": 0, "mrx_clk": 0,
+ "mtxerr": 0, "mrx_clk": 0,
"mrxd": 0,
- "mrxdv": 0, "mrxerr": 0,
+ "mrxdv": 0, "mrxerr": 0,
"mcoll": 0,
"mcrs": 0, "mdc": 0, "md": 0})
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']:
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)
ddr_addr=0x00000000, # DRAM_BASE
spi0_addr=0xf0000000, # SPI0_BASE
spi0_cfg_addr=0xc0006000, # SPI0_CTRL_BASE
- eth0_cfg_addr=0xc0004000, # ETH0_CTRL_BASE (4k)
- eth0_irqno=0, # ETH0_IRQ number
- hyperram_addr=0xa0000000, # HYPERRAM_BASE
+ eth0_cfg_addr=0xc000c000, # ETH0_CTRL_BASE (4k)
+ eth0_irqno=1, # ETH0_IRQ number (match microwatt)
+ hyperram_addr=hyperram_addr, # determined above
fw_addr=fw_addr,
#fw_addr=None,
ddr_pins=ddr_pins,
uart_pins=uart_pins,
+ uart_irqno=0, # UART_IRQ number (match microwatt)
+ uart_addr=0xc0002000, # UART0_ADDR
spi_0_pins=spi_0_pins,
ethmac_0_pins=ethmac_0_pins,
hyperram_pins=hyperram_pins,
firmware=firmware,
+ xics_icp_addr=0xc000_4000, # XICS_ICP_BASE
+ xics_ics_addr=0xc000_5000, # XICS_ICS_BASE
clk_freq=clk_freq,
+ dram_clk_freq=dram_clk_freq,
add_cpu=True)
if toolchain == 'Trellis':
#os.environ['NMIGEN_synth_opts'] = '-abc9'
os.environ['NMIGEN_synth_opts'] = '-nowidelut'
+ 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':