import logging
import time
import datetime
-from math import log2
+from math import log2, ceil
from migen import *
from litex.soc.cores import cpu
from litex.soc.cores.identifier import Identifier
from litex.soc.cores.timer import Timer
+from litex.soc.cores.spi_flash import SpiFlash
+from litex.soc.cores.spi import SPIMaster
from litex.soc.interconnect.csr import *
from litex.soc.interconnect import csr_bus
return is_io
# Add Master/Slave -----------------------------------------------------------------------------
- def add_adapter(self, name, interface):
+ def add_adapter(self, name, interface, direction="m2s"):
+ assert direction in ["m2s", "s2m"]
if interface.data_width != self.data_width:
self.logger.info("{} Bus {} from {}-bit to {}-bit.".format(
colorer(name),
colorer(interface.data_width),
colorer(self.data_width)))
new_interface = wishbone.Interface(data_width=self.data_width)
- self.submodules += wishbone.Converter(interface, new_interface)
+ if direction == "m2s":
+ converter = wishbone.Converter(master=interface, slave=new_interface)
+ if direction == "s2m":
+ converter = wishbone.Converter(master=new_interface, slave=interface)
+ self.submodules += converter
return new_interface
else:
return interface
colorer("already declared", color="red")))
self.logger.error(self)
raise
- master = self.add_adapter(name, master)
+ master = self.add_adapter(name, master, "m2s")
self.masters[name] = master
self.logger.info("{} {} as Bus Master.".format(
colorer(name, color="underline"),
colorer("already declared", color="red")))
self.logger.error(self)
raise
- slave = self.add_adapter(name, slave)
+ slave = self.add_adapter(name, slave, "s2m")
self.slaves[name] = slave
self.logger.info("{} {} as Bus Slave.".format(
colorer(name, color="underline"),
raise
self.constants[name] = SoCConstant(value)
- def add_config(self, name, value):
+ def add_config(self, name, value=None):
name = "CONFIG_" + name
if isinstance(value, str):
self.add_constant(name + "_" + value)
def add_csr_bridge(self, origin):
self.submodules.csr_bridge = wishbone2csr.WB2CSR(
bus_csr = csr_bus.Interface(
- address_width = self.csr.address_width,
- data_width = self.csr.data_width))
+ address_width = self.csr.address_width,
+ data_width = self.csr.data_width))
csr_size = 2**(self.csr.address_width + 2)
csr_region = SoCRegion(origin=origin, size=csr_size, cached=False)
self.bus.add_slave("csr", self.csr_bridge.wishbone, csr_region)
self.add_config("CSR_DATA_WIDTH", self.csr.data_width)
self.add_config("CSR_ALIGNMENT", self.csr.alignment)
- def add_cpu(self, name="vexriscv", variant="standard", reset_address=None):
+ def add_cpu(self, name="vexriscv", variant="standard", cls=None, reset_address=None):
if name not in cpu.CPUS.keys():
self.logger.error("{} CPU {}, supporteds: {}".format(
colorer(name),
colorer(", ".join(cpu.CPUS.keys()))))
raise
# Add CPU
- self.submodules.cpu = cpu.CPUS[name](self.platform, variant)
+ cpu_cls = cls if cls is not None else cpu.CPUS[name]
+ self.submodules.cpu = cpu_cls(self.platform, variant)
# Update SoC with CPU constraints
for n, (origin, size) in enumerate(self.cpu.io_regions.items()):
self.bus.add_region("io{}".format(n), SoCIORegion(origin=origin, size=size, cached=False))
self.mem_map.update(self.cpu.mem_map) # FIXME
- self.csr.update_alignment(self.cpu.data_width)
+
+ # We don't want the CSR alignemnt reduced from 64-bit to 32-bit on
+ # a standalone system with a 64-bit WB and no CPU.
+ # Should we instead only update alignment if the CPU is *bigger*
+ # than the CSR ?
+ if name != "None":
+ self.csr.update_alignment(self.cpu.data_width)
# Add Bus Masters/CSR/IRQs
if not isinstance(self.cpu, cpu.CPUNone):
if reset_address is None:
reset_address = self.mem_map["rom"]
self.cpu.set_reset_address(reset_address)
- for n, cpu_bus in enumerate(self.cpu.buses):
+ for n, cpu_bus in enumerate(self.cpu.periph_buses):
self.bus.add_master(name="cpu_bus{}".format(n), master=cpu_bus)
self.csr.add("cpu", use_loc_if_exists=True)
- for name, loc in self.cpu.interrupts.items():
- self.irq.add(name, loc)
+ if hasattr(self.cpu, "interrupt"):
+ for name, loc in self.cpu.interrupts.items():
+ self.irq.add(name, loc)
+ self.add_config("CPU_HAS_INTERRUPT")
if hasattr(self, "ctrl"):
self.comb += self.cpu.reset.eq(self.ctrl.reset)
self.add_config("CPU_RESET_ADDR", reset_address)
# Add constants
self.add_config("CPU_TYPE", str(name))
self.add_config("CPU_VARIANT", str(variant.split('+')[0]))
+ self.add_constant("CONFIG_CPU_HUMAN_NAME", getattr(self.cpu, "human_name", "Unknown"))
+ if hasattr(self.cpu, "nop"):
+ self.add_constant("CONFIG_CPU_NOP", self.cpu.nop)
def add_timer(self, name="timer0"):
self.check_if_exists(name)
setattr(self.submodules, name, Timer())
self.csr.add(name, use_loc_if_exists=True)
- self.irq.add(name, use_loc_if_exists=True)
+ if hasattr(self.cpu, "interrupt"):
+ self.irq.add(name, use_loc_if_exists=True)
# SoC finalization -----------------------------------------------------------------------------
def do_finalize(self):
# SoC Bus Interconnect ---------------------------------------------------------------------
bus_masters = self.bus.masters.values()
bus_slaves = [(self.bus.regions[n].decoder(self.bus), s) for n, s in self.bus.slaves.items()]
- if len(bus_masters) and len(bus_slaves):
+ # One master and one slave, use a point to point interconnect, this is useful for
+ # generating standalone components such as LiteDRAM whose external control
+ # interface is a wishbone.
+ if len(bus_masters) == 1 and len(bus_slaves) == 1:
+ self.submodules.bus_interconnect = wishbone.InterconnectPointToPoint(
+ master = list(bus_masters)[0],
+ slave = list(self.bus.slaves.values())[0])
+ elif len(bus_masters) and len(bus_slaves):
self.submodules.bus_interconnect = wishbone.InterconnectShared(
masters = bus_masters,
slaves = bus_slaves,
# Add UART -------------------------------------------------------------------------------------
def add_uart(self, name, baudrate=115200, fifo_depth=16):
from litex.soc.cores import uart
+
+ # Stub / Stream
if name in ["stub", "stream"]:
self.submodules.uart = uart.UART(tx_fifo_depth=0, rx_fifo_depth=0)
if name == "stub":
self.comb += self.uart.sink.ready.eq(1)
- elif name == "bridge":
- self.submodules.uart = uart.UARTWishboneBridge(
- pads = self.platform.request("serial"),
- clk_freq = self.sys_clk_freq,
- baudrate = baudrate)
- self.bus.add_master(name="uart_bridge", master=self.uart.wishbone)
- elif name == "crossover":
+
+ # UARTBone / Bridge
+ elif name in ["uartbone", "bridge"]:
+ self.add_uartbone(baudrate=baudrate)
+
+ # Crossover
+ elif name in ["crossover"]:
self.submodules.uart = uart.UARTCrossover()
+
+ # Model/Sim
+ elif name in ["model", "sim"]:
+ self.submodules.uart_phy = uart.RS232PHYModel(self.platform.request("serial"))
+ self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
+ tx_fifo_depth = fifo_depth,
+ rx_fifo_depth = fifo_depth))
+
+ # JTAG Atlantic
+ elif name in ["jtag_atlantic"]:
+ from litex.soc.cores.jtag import JTAGAtlantic
+ self.submodules.uart_phy = JTAGAtlantic()
+ self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
+ tx_fifo_depth = fifo_depth,
+ rx_fifo_depth = fifo_depth))
+
+ # JTAG UART
+ elif name in ["jtag_uart"]:
+ from litex.soc.cores.jtag import JTAGPHY
+ self.submodules.uart_phy = JTAGPHY(device=self.platform.device)
+ self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
+ tx_fifo_depth = fifo_depth,
+ rx_fifo_depth = fifo_depth))
+
+ # USB ACM (with ValentyUSB core)
+ elif name in ["usb_acm"]:
+ import valentyusb.usbcore.io as usbio
+ import valentyusb.usbcore.cpu.cdc_eptri as cdc_eptri
+ usb_pads = self.platform.request("usb")
+ usb_iobuf = usbio.IoBuf(usb_pads.d_p, usb_pads.d_n, usb_pads.pullup)
+ self.submodules.uart = cdc_eptri.CDCUsb(usb_iobuf)
+
+ # Classic UART
else:
- if name == "jtag_atlantic":
- from litex.soc.cores.jtag import JTAGAtlantic
- self.submodules.uart_phy = JTAGAtlantic()
- elif name == "jtag_uart":
- from litex.soc.cores.jtag import JTAGPHY
- self.submodules.uart_phy = JTAGPHY(device=self.platform.device)
- else:
- self.submodules.uart_phy = uart.UARTPHY(
- pads = self.platform.request(name),
- clk_freq = self.sys_clk_freq,
- baudrate = baudrate)
+ self.submodules.uart_phy = uart.UARTPHY(
+ pads = self.platform.request(name),
+ clk_freq = self.sys_clk_freq,
+ baudrate = baudrate)
self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy,
tx_fifo_depth = fifo_depth,
rx_fifo_depth = fifo_depth))
+
self.csr.add("uart_phy", use_loc_if_exists=True)
self.csr.add("uart", use_loc_if_exists=True)
- self.irq.add("uart", use_loc_if_exists=True)
+ if hasattr(self.cpu, "interrupt"):
+ self.irq.add("uart", use_loc_if_exists=True)
+ else:
+ self.add_constant("UART_POLLING")
+
+ # Add UARTbone ---------------------------------------------------------------------------------
+ def add_uartbone(self, name="serial", baudrate=115200):
+ from litex.soc.cores import uart
+ self.submodules.uartbone = uart.UARTBone(
+ pads = self.platform.request(name),
+ clk_freq = self.sys_clk_freq,
+ baudrate = baudrate)
+ self.bus.add_master(name="uartbone", master=self.uartbone.wishbone)
# Add SDRAM ------------------------------------------------------------------------------------
def add_sdram(self, name, phy, module, origin, size=None,
**kwargs):
# Imports
+ from litedram.common import LiteDRAMNativePort
from litedram.core import LiteDRAMCore
from litedram.frontend.wishbone import LiteDRAMWishbone2Native
from litedram.frontend.axi import LiteDRAMAXI2Native
**kwargs)
self.csr.add("sdram")
- # LiteDRAM port
- port = self.sdram.crossbar.get_port()
- port.data_width = 2**int(log2(port.data_width)) # Round to nearest power of 2
-
- # SDRAM size
+ # Compute/Check SDRAM size
sdram_size = 2**(module.geom_settings.bankbits +
module.geom_settings.rowbits +
module.geom_settings.colbits)*phy.settings.databits//8
if size is not None:
sdram_size = min(sdram_size, size)
- self.bus.add_region("main_ram", SoCRegion(origin=origin, size=sdram_size))
+
+ # Add SDRAM region
+ if self.cpu_type is not None:
+ self.bus.add_region("main_ram", SoCRegion(origin=origin, size=sdram_size))
# SoC [<--> L2 Cache] <--> LiteDRAM --------------------------------------------------------
- if self.cpu.name == "rocket":
- # Rocket has its own I/D L1 cache: connect directly to LiteDRAM when possible.
- if port.data_width == self.cpu.mem_axi.data_width:
- self.logger.info("Matching AXI MEM data width ({})\n".format(port.data_width))
- self.submodules += LiteDRAMAXI2Native(
- axi = self.cpu.mem_axi,
- port = port,
- base_address = self.bus.regions["main_ram"].origin)
- else:
- self.logger.info("Converting MEM data width: {} to {} via Wishbone".format(
- port.data_width,
- self.cpu.mem_axi.data_width))
- # FIXME: replace WB data-width converter with native AXI converter!!!
- mem_wb = wishbone.Interface(
- data_width = self.cpu.mem_axi.data_width,
- adr_width = 32-log2_int(self.cpu.mem_axi.data_width//8))
- # NOTE: AXI2Wishbone FSMs must be reset with the CPU!
- mem_a2w = ResetInserter()(axi.AXI2Wishbone(
- axi = self.cpu.mem_axi,
- wishbone = mem_wb,
- base_address = 0))
- self.comb += mem_a2w.reset.eq(ResetSignal() | self.cpu.reset)
- self.submodules += mem_a2w
- litedram_wb = wishbone.Interface(port.data_width)
- self.submodules += LiteDRAMWishbone2Native(
- wishbone = litedram_wb,
- port = port,
- base_address = origin)
- self.submodules += wishbone.Converter(mem_wb, litedram_wb)
- elif self.with_wishbone:
- # Wishbone Slave SDRAM interface
+ if len(self.cpu.memory_buses):
+ # When CPU has at least a direct memory bus, connect them directly to LiteDRAM.
+ for mem_bus in self.cpu.memory_buses:
+ # Request a LiteDRAM native port.
+ port = self.sdram.crossbar.get_port()
+ port.data_width = 2**int(log2(port.data_width)) # Round to nearest power of 2.
+
+ # Check if bus is an AXI bus and connect it.
+ if isinstance(mem_bus, axi.AXIInterface):
+ # If same data_width, connect it directly.
+ if port.data_width == mem_bus.data_width:
+ self.logger.info("Matching AXI MEM data width ({})\n".format(port.data_width))
+ self.submodules += LiteDRAMAXI2Native(
+ axi = self.cpu.mem_axi,
+ port = port,
+ base_address = self.bus.regions["main_ram"].origin)
+ # If different data_width, do the adaptation and connect it via Wishbone.
+ else:
+ self.logger.info("Converting MEM data width: {} to {} via Wishbone".format(
+ port.data_width,
+ self.cpu.mem_axi.data_width))
+ # FIXME: replace WB data-width converter with native AXI converter!!!
+ mem_wb = wishbone.Interface(
+ data_width = self.cpu.mem_axi.data_width,
+ adr_width = 32-log2_int(self.cpu.mem_axi.data_width//8))
+ # NOTE: AXI2Wishbone FSMs must be reset with the CPU!
+ mem_a2w = ResetInserter()(axi.AXI2Wishbone(
+ axi = self.cpu.mem_axi,
+ wishbone = mem_wb,
+ base_address = 0))
+ self.comb += mem_a2w.reset.eq(ResetSignal() | self.cpu.reset)
+ self.submodules += mem_a2w
+ litedram_wb = wishbone.Interface(port.data_width)
+ self.submodules += LiteDRAMWishbone2Native(
+ wishbone = litedram_wb,
+ port = port,
+ base_address = origin)
+ self.submodules += wishbone.Converter(mem_wb, litedram_wb)
+ # Check if bus is a Native bus and connect it.
+ if isinstance(mem_bus, LiteDRAMNativePort):
+ # If same data_width, connect it directly.
+ if port.data_width == mem_bus.data_width:
+ self.comb += mem_bus.cmd.connect(port.cmd)
+ self.comb += mem_bus.wdata.connect(port.wdata)
+ self.comb += port.rdata.connect(mem_bus.rdata)
+ # Else raise Error.
+ else:
+ raise NotImplementedError
+ elif self.cpu_type is not None:
+ # When CPU has no direct memory interface, create a Wishbone Slave interface to LiteDRAM.
+
+ # Request a LiteDRAM native port.
+ port = self.sdram.crossbar.get_port()
+ port.data_width = 2**int(log2(port.data_width)) # Round to nearest power of 2.
+
+ # Create Wishbone Slave.
wb_sdram = wishbone.Interface()
self.bus.add_slave("main_ram", wb_sdram)
base_address = self.bus.regions["main_ram"].origin)
# Add Ethernet ---------------------------------------------------------------------------------
- def add_ethernet(self, phy):
+ def add_ethernet(self, name="ethmac", phy=None):
# Imports
from liteeth.mac import LiteEthMAC
# MAC
- self.submodules.ethmac = LiteEthMAC(
+ ethmac = LiteEthMAC(
phy = phy,
dw = 32,
interface = "wishbone",
endianness = self.cpu.endianness)
- ethmac_region = SoCRegion(origin=self.mem_map.get("ethmac", None),
- size=0x2000, cached=False)
- self.bus.add_slave(name="ethmac", slave=self.ethmac.bus, region=ethmac_region)
- self.add_csr("ethmac")
- self.add_interrupt("ethmac")
+ setattr(self.submodules, name, ethmac)
+ ethmac_region = SoCRegion(origin=self.mem_map.get(name, None), size=0x2000, cached=False)
+ self.bus.add_slave(name=name, slave=ethmac.bus, region=ethmac_region)
+ self.add_csr(name)
+ self.add_interrupt(name)
# Timing constraints
- self.platform.add_period_constraint(phy.crg.cd_eth_rx.clk, 1e9/phy.rx_clk_freq)
- self.platform.add_period_constraint(phy.crg.cd_eth_tx.clk, 1e9/phy.tx_clk_freq)
+ if hasattr(phy, "crg"):
+ eth_rx_clk = phy.crg.cd_eth_rx.clk
+ eth_tx_clk = phy.crg.cd_eth_tx.clk
+ else:
+ eth_rx_clk = phy.cd_eth_rx.clk
+ eth_tx_clk = phy.cd_eth_tx.clk
+ self.platform.add_period_constraint(eth_rx_clk, 1e9/phy.rx_clk_freq)
+ self.platform.add_period_constraint(eth_tx_clk, 1e9/phy.tx_clk_freq)
+ self.platform.add_false_path_constraints(
+ self.crg.cd_sys.clk,
+ eth_rx_clk,
+ eth_tx_clk)
+
+ # Add Etherbone --------------------------------------------------------------------------------
+ def add_etherbone(self, name="etherbone", phy=None, clock_domain=None,
+ mac_address = 0x10e2d5000000,
+ ip_address = "192.168.1.50",
+ udp_port = 1234):
+ # Imports
+ from liteeth.core import LiteEthUDPIPCore
+ from liteeth.frontend.etherbone import LiteEthEtherbone
+ # Core
+ ethcore = LiteEthUDPIPCore(
+ phy = self.ethphy,
+ mac_address = mac_address,
+ ip_address = ip_address,
+ clk_freq = self.clk_freq)
+ if clock_domain is not None: # FIXME: Could probably be avoided.
+ ethcore = ClockDomainsRenamer("eth_tx")(ethcore)
+ self.submodules += ethcore
+
+ # Clock domain renaming
+ if clock_domain is not None: # FIXME: Could probably be avoided.
+ self.clock_domains.cd_etherbone = ClockDomain("etherbone")
+ self.comb += self.cd_etherbone.clk.eq(ClockSignal(clock_domain))
+ self.comb += self.cd_etherbone.rst.eq(ResetSignal(clock_domain))
+ clock_domain = "etherbone"
+ else:
+ clock_domain = "sys"
+
+ # Etherbone
+ etherbone = LiteEthEtherbone(ethcore.udp, udp_port, cd=clock_domain)
+ setattr(self.submodules, name, etherbone)
+ self.add_wb_master(etherbone.wishbone.bus)
+ # Timing constraints
+ if hasattr(phy, "crg"):
+ eth_rx_clk = phy.crg.cd_eth_rx.clk
+ eth_tx_clk = phy.crg.cd_eth_tx.clk
+ else:
+ eth_rx_clk = phy.cd_eth_rx.clk
+ eth_tx_clk = phy.cd_eth_tx.clk
+ self.platform.add_period_constraint(eth_rx_clk, 1e9/phy.rx_clk_freq)
+ self.platform.add_period_constraint(eth_tx_clk, 1e9/phy.tx_clk_freq)
self.platform.add_false_path_constraints(
self.crg.cd_sys.clk,
- phy.crg.cd_eth_rx.clk,
- phy.crg.cd_eth_tx.clk)
+ eth_rx_clk,
+ eth_tx_clk)
+
+ # Add SPI Flash --------------------------------------------------------------------------------
+ def add_spi_flash(self, name="spiflash", mode="4x", dummy_cycles=None, clk_freq=None):
+ assert dummy_cycles is not None # FIXME: Get dummy_cycles from SPI Flash
+ assert mode in ["1x", "4x"]
+ if clk_freq is None: clk_freq = self.clk_freq/2 # FIXME: Get max clk_freq from SPI Flash
+ spiflash = SpiFlash(
+ pads = self.platform.request(name if mode == "1x" else name + mode),
+ dummy = dummy_cycles,
+ div = ceil(self.clk_freq/clk_freq),
+ with_bitbang = True,
+ endianness = self.cpu.endianness)
+ spiflash.add_clk_primitive(self.platform.device)
+ setattr(self.submodules, name, spiflash)
+ self.add_memory_region(name, self.mem_map[name], 0x1000000) # FIXME: Get size from SPI Flash
+ self.add_wb_slave(self.mem_map[name], spiflash.bus)
+ self.add_csr(name)
+
+ # Add SPI SDCard -------------------------------------------------------------------------------
+ def add_spi_sdcard(self, name="spisdcard", clk_freq=400e3):
+ pads = self.platform.request(name)
+ if hasattr(pads, "rst"):
+ self.comb += pads.rst.eq(0)
+ spisdcard = SPIMaster(pads, 8, self.sys_clk_freq, 400e3)
+ spisdcard.add_clk_divider()
+ setattr(self.submodules, name, spisdcard)
+ self.add_csr(name)