from litex.soc.interconnect.csr import *
from litex.soc.interconnect import csr_bus
+from litex.soc.interconnect import stream
from litex.soc.interconnect import wishbone
-from litex.soc.interconnect import wishbone2csr
from litex.soc.interconnect import axi
-# TODO:
-# - replace raise with exit on logging error.
-# - cleanup SoCCSRRegion
-
logging.basicConfig(level=logging.INFO)
# Helpers ------------------------------------------------------------------------------------------
# SoCBusHandler ------------------------------------------------------------------------------------
class SoCBusHandler(Module):
- supported_standard = ["wishbone"]
+ supported_standard = ["wishbone", "axi-lite"]
supported_data_width = [32, 64]
supported_address_width = [32]
# Creation -------------------------------------------------------------------------------------
- def __init__(self, standard, data_width=32, address_width=32, timeout=1e6, reserved_regions={}):
- self.logger = logging.getLogger("SoCBusHandler")
+ def __init__(self, name="SoCBusHandler", standard="wishbone", data_width=32, address_width=32, timeout=1e6, reserved_regions={}):
+ self.logger = logging.getLogger(name)
self.logger.info("Creating Bus Handler...")
# Check Standard
if address_width not in self.supported_address_width:
self.logger.error("Unsupported {} {}, supporteds: {:s}".format(
colorer("Address Width", color="red"),
- colorer(data_width),
+ colorer(address_width),
colorer(", ".join(str(x) for x in self.supported_address_width))))
raise
# Add Master/Slave -----------------------------------------------------------------------------
def add_adapter(self, name, interface, direction="m2s"):
assert direction in ["m2s", "s2m"]
+
+ # Data width conversion
if interface.data_width != self.data_width:
- self.logger.info("{} Bus {} from {}-bit to {}-bit.".format(
- colorer(name),
- colorer("converted", color="cyan"),
- colorer(interface.data_width),
- colorer(self.data_width)))
- new_interface = wishbone.Interface(data_width=self.data_width)
+ interface_cls = type(interface)
+ converter_cls = {
+ wishbone.Interface: wishbone.Converter,
+ axi.AXILiteInterface: axi.AXILiteConverter,
+ }[interface_cls]
+ converted_interface = interface_cls(data_width=self.data_width)
if direction == "m2s":
- converter = wishbone.Converter(master=interface, slave=new_interface)
- if direction == "s2m":
- converter = wishbone.Converter(master=new_interface, slave=interface)
+ master, slave = interface, converted_interface
+ elif direction == "s2m":
+ master, slave = converted_interface, interface
+ converter = converter_cls(master=master, slave=slave)
self.submodules += converter
- return new_interface
else:
- return interface
+ converted_interface = interface
+
+ # Wishbone <-> AXILite bridging
+ main_bus_cls = {
+ "wishbone": wishbone.Interface,
+ "axi-lite": axi.AXILiteInterface,
+ }[self.standard]
+ if isinstance(converted_interface, main_bus_cls):
+ bridged_interface = converted_interface
+ else:
+ bridged_interface = main_bus_cls(data_width=self.data_width)
+ if direction == "m2s":
+ master, slave = converted_interface, bridged_interface
+ elif direction == "s2m":
+ master, slave = bridged_interface, converted_interface
+ bridge_cls = {
+ (wishbone.Interface, axi.AXILiteInterface): axi.Wishbone2AXILite,
+ (axi.AXILiteInterface, wishbone.Interface): axi.AXILite2Wishbone,
+ }[type(master), type(slave)]
+ bridge = bridge_cls(master, slave)
+ self.submodules += bridge
+
+ if type(interface) != type(bridged_interface) or interface.data_width != bridged_interface.data_width:
+ fmt = "{name} Bus {converted} from {frombus} {frombits}-bit to {tobus} {tobits}-bit."
+ bus_names = {
+ wishbone.Interface: "Wishbone",
+ axi.AXILiteInterface: "AXI Lite",
+ }
+ self.logger.info(fmt.format(
+ name = colorer(name),
+ converted = colorer("converted", color="cyan"),
+ frombus = colorer(bus_names[type(interface)]),
+ frombits = colorer(interface.data_width),
+ tobus = colorer(bus_names[type(bridged_interface)]),
+ tobits = colorer(bridged_interface.data_width)))
+ return bridged_interface
def add_master(self, name=None, master=None):
if name is None:
class SoCCSRHandler(SoCLocHandler):
supported_data_width = [8, 32]
supported_address_width = [14+i for i in range(4)]
- supported_alignment = [32, 64]
+ supported_alignment = [32]
supported_paging = [0x800*2**i for i in range(4)]
# Creation -------------------------------------------------------------------------------------
self.logger.info("CSR Handler {}.".format(colorer("created", color="green")))
- # Update CSR Alignment ----------------------------------------------------------------------------
- def update_alignment(self, alignment):
- # Check Alignment
- if alignment not in self.supported_alignment:
- self.logger.error("Unsupported {}: {} supporteds: {:s}".format(
- colorer("Alignment", color="red"),
- colorer(alignment),
- colorer(", ".join(str(x) for x in self.supported_alignment))))
- raise
- self.logger.info("Alignment {} from {}-bit to {}-bit.".format(
- colorer("updated", color="cyan"),
- colorer(self.alignment),
- colorer(alignment)))
- self.alignment = alignment
-
# Add Master -----------------------------------------------------------------------------------
def add_master(self, name=None, master=None):
if name is None:
class SoC(Module):
mem_map = {}
def __init__(self, platform, sys_clk_freq,
-
bus_standard = "wishbone",
bus_data_width = 32,
bus_address_width = 32,
csr_data_width = 32,
csr_address_width = 14,
- csr_alignment = 32,
csr_paging = 0x800,
csr_reserved_csrs = {},
self.submodules.csr = SoCCSRHandler(
data_width = csr_data_width,
address_width = csr_address_width,
- alignment = csr_alignment,
+ alignment = 32,
paging = csr_paging,
reserved_csrs = csr_reserved_csrs,
)
self.csr.add(name, use_loc_if_exists=True)
def add_ram(self, name, origin, size, contents=[], mode="rw"):
- ram_bus = wishbone.Interface(data_width=self.bus.data_width)
- ram = wishbone.SRAM(size, bus=ram_bus, init=contents, read_only=(mode == "r"))
+ ram_cls = {
+ "wishbone": wishbone.SRAM,
+ "axi-lite": axi.AXILiteSRAM,
+ }[self.bus.standard]
+ interface_cls = {
+ "wishbone": wishbone.Interface,
+ "axi-lite": axi.AXILiteInterface,
+ }[self.bus.standard]
+ ram_bus = interface_cls(data_width=self.bus.data_width)
+ ram = ram_cls(size, bus=ram_bus, init=contents, read_only=(mode == "r"))
self.bus.add_slave(name, ram.bus, SoCRegion(origin=origin, size=size, mode=mode))
self.check_if_exists(name)
self.logger.info("RAM {} {} {}.".format(
self.add_ram(name, origin, size, contents, mode="r")
def add_csr_bridge(self, origin):
- self.submodules.csr_bridge = wishbone2csr.WB2CSR(
+ csr_bridge_cls = {
+ "wishbone": wishbone.Wishbone2CSR,
+ "axi-lite": axi.AXILite2CSR,
+ }[self.bus.standard]
+ self.submodules.csr_bridge = csr_bridge_cls(
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)
+ bus = getattr(self.csr_bridge, self.bus.standard.replace('-', '_'))
+ self.bus.add_slave("csr", bus, csr_region)
self.csr.add_master(name="bridge", master=self.csr_bridge.csr)
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", cls=None, reset_address=None):
if name not in cpu.CPUS.keys():
- self.logger.error("{} CPU {}, supporteds: {}".format(
+ self.logger.error("{} CPU {}, supporteds: {}.".format(
colorer(name),
colorer("not supported", color="red"),
colorer(", ".join(cpu.CPUS.keys()))))
raise
# Add CPU
cpu_cls = cls if cls is not None else cpu.CPUS[name]
+ if variant not in cpu_cls.variants:
+ self.logger.error("{} CPU variant {}, supporteds: {}.".format(
+ colorer(variant),
+ colorer("not supported", color="red"),
+ colorer(", ".join(cpu_cls.variants))))
+ raise
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
# Add Bus Masters/CSR/IRQs
- if not isinstance(self.cpu, cpu.CPUNone):
- self.csr.update_alignment(self.cpu.data_width)
+ if not isinstance(self.cpu, (cpu.CPUNone, cpu.Zynq7000)):
if reset_address is None:
reset_address = self.mem_map["rom"]
self.cpu.set_reset_address(reset_address)
self.irq.add(name, loc)
self.add_config("CPU_HAS_INTERRUPT")
+ # Create optional DMA Bus (for Cache Coherence)
+ if hasattr(self.cpu, "dma_bus"):
+ self.submodules.dma_bus = SoCBusHandler(
+ name = "SoCDMABusHandler",
+ standard = "wishbone",
+ data_width = self.bus.data_width,
+ )
+ dma_bus = wishbone.Interface(data_width=self.bus.data_width)
+ self.dma_bus.add_slave("dma", slave=dma_bus, region=SoCRegion(origin=0x00000000, size=0x80000000)) # FIXME: size
+ self.submodules += wishbone.Converter(dma_bus, self.cpu.dma_bus)
+ # Connect SoCController's reset to CPU reset
if hasattr(self, "ctrl"):
if hasattr(self.ctrl, "reset"):
self.comb += self.cpu.reset.eq(self.ctrl.reset)
self.logger.info(colorer("Finalized SoC:"))
self.logger.info(colorer("-"*80, color="bright"))
self.logger.info(self.bus)
+ if hasattr(self, "dma_bus"):
+ self.logger.info(self.dma_bus)
self.logger.info(self.csr)
self.logger.info(self.irq)
self.logger.info(colorer("-"*80, color="bright"))
+ interconnect_p2p_cls = {
+ "wishbone": wishbone.InterconnectPointToPoint,
+ "axi-lite": axi.AXILiteInterconnectPointToPoint,
+ }[self.bus.standard]
+ interconnect_shared_cls = {
+ "wishbone": wishbone.InterconnectShared,
+ "axi-lite": axi.AXILiteInterconnectShared,
+ }[self.bus.standard]
+
# SoC Bus Interconnect ---------------------------------------------------------------------
if len(self.bus.masters) and len(self.bus.slaves):
# If 1 bus_master, 1 bus_slave and no address translation, use InterconnectPointToPoint.
if ((len(self.bus.masters) == 1) and
(len(self.bus.slaves) == 1) and
(next(iter(self.bus.regions.values())).origin == 0)):
- self.submodules.bus_interconnect = wishbone.InterconnectPointToPoint(
+ self.submodules.bus_interconnect = interconnect_p2p_cls(
master = next(iter(self.bus.masters.values())),
slave = next(iter(self.bus.slaves.values())))
# Otherwise, use InterconnectShared.
else:
- self.submodules.bus_interconnect = wishbone.InterconnectShared(
+ self.submodules.bus_interconnect = interconnect_shared_cls(
masters = self.bus.masters.values(),
slaves = [(self.bus.regions[n].decoder(self.bus), s) for n, s in self.bus.slaves.items()],
register = True,
colorer(self.bus_interconnect.__class__.__name__),
colorer(len(self.bus.masters)),
colorer(len(self.bus.slaves))))
+ self.add_constant("CONFIG_BUS_STANDARD", self.bus.standard.upper())
+ self.add_constant("CONFIG_BUS_DATA_WIDTH", self.bus.data_width)
+ self.add_constant("CONFIG_BUS_ADDRESS_WIDTH", self.bus.address_width)
+
+ # SoC DMA Bus Interconnect (Cache Coherence) -----------------------------------------------
+ if hasattr(self, "dma_bus"):
+ if len(self.dma_bus.masters) and len(self.dma_bus.slaves):
+ # If 1 bus_master, 1 bus_slave and no address translation, use InterconnectPointToPoint.
+ if ((len(self.dma_bus.masters) == 1) and
+ (len(self.dma_bus.slaves) == 1) and
+ (next(iter(self.dma_bus.regions.values())).origin == 0)):
+ self.submodules.bus_interconnect = wishbone.InterconnectPointToPoint(
+ master = next(iter(self.dma_bus.masters.values())),
+ slave = next(iter(self.dma_bus.slaves.values())))
+ # Otherwise, use InterconnectShared.
+ else:
+ self.submodules.dma_bus_interconnect = wishbone.InterconnectShared(
+ masters = self.dma_bus.masters.values(),
+ slaves = [(self.dma_bus.regions[n].decoder(self.dma_bus), s) for n, s in self.dma_bus.slaves.items()],
+ register = True)
+ self.bus.logger.info("DMA Interconnect: {} ({} <-> {}).".format(
+ colorer(self.dma_bus_interconnect.__class__.__name__),
+ colorer(len(self.dma_bus.masters)),
+ colorer(len(self.dma_bus.slaves))))
+ self.add_constant("CONFIG_CPU_HAS_DMA_BUS")
# SoC CSR Interconnect ---------------------------------------------------------------------
self.submodules.csr_bankarray = csr_bus.CSRBankArray(self,
self.add_constant(name + "_" + constant.name, constant.value.value)
# SoC CPU Check ----------------------------------------------------------------------------
- if not isinstance(self.cpu, cpu.CPUNone):
+ if not isinstance(self.cpu, (cpu.CPUNone, cpu.Zynq7000)):
if "sram" not in self.bus.regions.keys():
self.logger.error("CPU needs {} Region to be {} as Bus or Linker Region.".format(
colorer("sram"),
**kwargs)
self.csr.add("sdram")
+ # Save SPD data to be able to verify it at runtime
+ if hasattr(module, "_spd_data"):
+ # pack the data into words of bus width
+ bytes_per_word = self.bus.data_width // 8
+ mem = [0] * ceil(len(module._spd_data) / bytes_per_word)
+ for i in range(len(mem)):
+ for offset in range(bytes_per_word):
+ mem[i] <<= 8
+ if self.cpu.endianness == "little":
+ offset = bytes_per_word - 1 - offset
+ spd_byte = i * bytes_per_word + offset
+ if spd_byte < len(module._spd_data):
+ mem[i] |= module._spd_data[spd_byte]
+ self.add_rom(
+ name="spd",
+ origin=self.mem_map.get("spd", None),
+ size=len(module._spd_data),
+ contents=mem,
+ )
+
if not with_soc_interconnect: return
# Compute/Check SDRAM size
eth_tx_clk)
# Add Etherbone --------------------------------------------------------------------------------
- def add_etherbone(self, name="etherbone", phy=None, clock_domain=None,
+ def add_etherbone(self, name="etherbone", phy=None,
mac_address = 0x10e2d5000000,
ip_address = "192.168.1.50",
udp_port = 1234):
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)
+ 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"
+ self.clock_domains.cd_etherbone = ClockDomain("etherbone")
+ self.comb += self.cd_etherbone.clk.eq(ClockSignal("sys"))
+ self.comb += self.cd_etherbone.rst.eq(ResetSignal("sys"))
# Etherbone
- etherbone = LiteEthEtherbone(ethcore.udp, udp_port, cd=clock_domain)
+ etherbone = LiteEthEtherbone(ethcore.udp, udp_port, cd="etherbone")
setattr(self.submodules, name, etherbone)
self.add_wb_master(etherbone.wishbone.bus)
# Timing constraints
self.add_csr(name)
# Add SPI SDCard -------------------------------------------------------------------------------
- def add_spi_sdcard(self, name="spisdcard", clk_freq=400e3):
+ def add_spi_sdcard(self, name="spisdcard", spi_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 = SPIMaster(pads, 8, self.sys_clk_freq, spi_clk_freq)
spisdcard.add_clk_divider()
setattr(self.submodules, name, spisdcard)
self.add_csr(name)
+
+ # Add SDCard -----------------------------------------------------------------------------------
+ def add_sdcard(self, name="sdcard", mode="read+write", use_emulator=False):
+ assert mode in ["read", "write", "read+write"]
+ # Imports
+ from litesdcard.emulator import SDEmulator
+ from litesdcard.phy import SDPHY
+ from litesdcard.core import SDCore
+ from litesdcard.frontend.dma import SDBlock2MemDMA, SDMem2BlockDMA
+
+ # Emulator / Pads
+ if use_emulator:
+ sdemulator = SDEmulator(self.platform)
+ self.submodules += sdemulator
+ sdcard_pads = sdemulator.pads
+ else:
+ sdcard_pads = self.platform.request(name)
+
+ # Core
+ self.submodules.sdphy = SDPHY(sdcard_pads, self.platform.device, self.clk_freq)
+ self.submodules.sdcore = SDCore(self.sdphy)
+ self.add_csr("sdphy")
+ self.add_csr("sdcore")
+
+ # Block2Mem DMA
+ if "read" in mode:
+ bus = wishbone.Interface(data_width=self.bus.data_width, adr_width=self.bus.address_width)
+ self.submodules.sdblock2mem = SDBlock2MemDMA(bus=bus, endianness=self.cpu.endianness)
+ self.comb += self.sdcore.source.connect(self.sdblock2mem.sink)
+ dma_bus = self.bus if not hasattr(self, "dma_bus") else self.dma_bus
+ dma_bus.add_master("sdblock2mem", master=bus)
+ self.add_csr("sdblock2mem")
+
+ # Mem2Block DMA
+ if "write" in mode:
+ bus = wishbone.Interface(data_width=self.bus.data_width, adr_width=self.bus.address_width)
+ self.submodules.sdmem2block = SDMem2BlockDMA(bus=bus, endianness=self.cpu.endianness)
+ self.comb += self.sdmem2block.source.connect(self.sdcore.sink)
+ dma_bus = self.bus if not hasattr(self, "dma_bus") else self.dma_bus
+ dma_bus.add_master("sdmem2block", master=bus)
+ self.add_csr("sdmem2block")