soc/integration: initial adaptation to new SoC class
This commit is contained in:
parent
6baa07a69b
commit
8bc420679a
|
@ -25,6 +25,7 @@ from litex.soc.cores import cpu
|
|||
from litex.soc.interconnect.csr import *
|
||||
from litex.soc.interconnect import wishbone, csr_bus, wishbone2csr
|
||||
from litex.soc.integration.common import *
|
||||
from litex.soc.integration.soc import SoCRegion, SoC
|
||||
|
||||
__all__ = [
|
||||
"mem_decoder",
|
||||
|
@ -69,7 +70,7 @@ class SoCController(Module, AutoCSR):
|
|||
|
||||
# SoCCore ------------------------------------------------------------------------------------------
|
||||
|
||||
class SoCCore(Module):
|
||||
class SoCCore(SoC):
|
||||
# default register/interrupt/memory mappings (can be redefined by user)
|
||||
csr_map = {}
|
||||
interrupt_map = {}
|
||||
|
@ -106,6 +107,24 @@ class SoCCore(Module):
|
|||
self.platform = platform
|
||||
self.clk_freq = clk_freq
|
||||
|
||||
SoC.__init__(self,
|
||||
bus_standard = "wishbone",
|
||||
bus_data_width = 32,
|
||||
bus_address_width = 32,
|
||||
bus_timeout = wishbone_timeout_cycles,
|
||||
bus_reserved_regions = {},
|
||||
|
||||
csr_data_width = csr_data_width,
|
||||
csr_address_width = csr_address_width,
|
||||
csr_alignment = csr_alignment,
|
||||
csr_paging = 0x800,
|
||||
csr_reserved_csrs = self.csr_map,
|
||||
|
||||
irq_n_irqs = 32,
|
||||
irq_reserved_irqs = {},
|
||||
)
|
||||
self.mem_regions = self.bus.regions
|
||||
|
||||
# SoC's CSR/Mem/Interrupt mapping (default or user defined + dynamically allocateds)
|
||||
self.soc_csr_map = {}
|
||||
self.soc_interrupt_map = {}
|
||||
|
@ -115,18 +134,11 @@ class SoCCore(Module):
|
|||
# SoC's Config/Constants/Regions
|
||||
self.config = {}
|
||||
self.constants = {}
|
||||
self.mem_regions = {}
|
||||
self.csr_regions = {}
|
||||
|
||||
# Wishbone masters/slaves lists
|
||||
self._wb_masters = []
|
||||
self._wb_slaves = []
|
||||
|
||||
# CSR masters list
|
||||
self._csr_masters = []
|
||||
|
||||
self.add_retro_compat(kwargs)
|
||||
|
||||
# Parameters managment ---------------------------------------------------------------------
|
||||
if cpu_type == "None":
|
||||
cpu_type = None
|
||||
|
@ -142,12 +154,9 @@ class SoCCore(Module):
|
|||
self.integrated_sram_size = integrated_sram_size
|
||||
self.integrated_main_ram_size = integrated_main_ram_size
|
||||
|
||||
assert csr_data_width in [8, 16, 32]
|
||||
self.csr_data_width = csr_data_width
|
||||
self.csr_address_width = csr_address_width
|
||||
|
||||
assert csr_alignment in [32, 64]
|
||||
|
||||
self.with_ctrl = with_ctrl
|
||||
|
||||
self.with_uart = with_uart
|
||||
|
@ -158,14 +167,10 @@ class SoCCore(Module):
|
|||
|
||||
# Modules instances ------------------------------------------------------------------------
|
||||
|
||||
# Add user's CSRs (needs to be done before the first dynamic allocation)
|
||||
for _name, _id in self.csr_map.items():
|
||||
self.add_csr(_name, _id)
|
||||
|
||||
# Add SoCController
|
||||
if with_ctrl:
|
||||
self.submodules.ctrl = SoCController()
|
||||
self.add_csr("ctrl", allow_user_defined=True)
|
||||
self.add_csr("ctrl")
|
||||
|
||||
# Add CPU
|
||||
self.config["CPU_TYPE"] = str(cpu_type).upper()
|
||||
|
@ -197,12 +202,12 @@ class SoCCore(Module):
|
|||
# Add CPU buses as 32-bit Wishbone masters
|
||||
for cpu_bus in self.cpu.buses:
|
||||
assert cpu_bus.data_width in [32, 64, 128]
|
||||
soc_bus = wishbone.Interface(data_width=32)
|
||||
soc_bus = wishbone.Interface(data_width=self.bus.data_width)
|
||||
self.submodules += wishbone.Converter(cpu_bus, soc_bus)
|
||||
self.add_wb_master(soc_bus)
|
||||
|
||||
# Add CPU CSR (dynamic)
|
||||
self.add_csr("cpu", allow_user_defined=True)
|
||||
self.add_csr("cpu")
|
||||
|
||||
# Add CPU interrupts
|
||||
for _name, _id in self.cpu.interrupts.items():
|
||||
|
@ -258,23 +263,23 @@ class SoCCore(Module):
|
|||
else:
|
||||
self.submodules.uart_phy = uart.UARTPHY(platform.request(uart_name), clk_freq, uart_baudrate)
|
||||
self.submodules.uart = ResetInserter()(uart.UART(self.uart_phy))
|
||||
self.add_csr("uart_phy", allow_user_defined=True)
|
||||
self.add_csr("uart", allow_user_defined=True)
|
||||
self.add_interrupt("uart", allow_user_defined=True)
|
||||
self.add_csr("uart_phy")
|
||||
self.add_csr("uart")
|
||||
self.add_interrupt("uart")
|
||||
|
||||
# Add Identifier
|
||||
if ident:
|
||||
if ident_version:
|
||||
ident = ident + " " + get_version()
|
||||
self.submodules.identifier = identifier.Identifier(ident)
|
||||
self.add_csr("identifier_mem", allow_user_defined=True)
|
||||
self.add_csr("identifier_mem")
|
||||
self.config["CLOCK_FREQUENCY"] = int(clk_freq)
|
||||
|
||||
# Add Timer
|
||||
if with_timer:
|
||||
self.submodules.timer0 = timer.Timer()
|
||||
self.add_csr("timer0", allow_user_defined=True)
|
||||
self.add_interrupt("timer0", allow_user_defined=True)
|
||||
self.add_csr("timer0")
|
||||
self.add_interrupt("timer0")
|
||||
|
||||
# Add Wishbone to CSR bridge
|
||||
self.config["CSR_DATA_WIDTH"] = csr_data_width
|
||||
|
@ -293,77 +298,24 @@ class SoCCore(Module):
|
|||
# Methods --------------------------------------------------------------------------------------
|
||||
|
||||
def add_interrupt(self, interrupt_name, interrupt_id=None, allow_user_defined=False):
|
||||
# Check that interrupt_name is not already used
|
||||
if interrupt_name in self.soc_interrupt_map.keys():
|
||||
if allow_user_defined:
|
||||
return
|
||||
else:
|
||||
raise ValueError("Interrupt conflict, {} name already used".format(interrupt_name))
|
||||
|
||||
# Check that interrupt_id is in range
|
||||
if interrupt_id is not None and interrupt_id >= 32:
|
||||
raise ValueError("{} Interrupt ID out of range ({}, max=31)".format(
|
||||
interrupt_name, interrupt_id))
|
||||
|
||||
# Interrupt_id not provided: allocate interrupt to the first available id
|
||||
if interrupt_id is None:
|
||||
for n in range(32):
|
||||
if n not in self.soc_interrupt_map.values():
|
||||
self.soc_interrupt_map.update({interrupt_name: n})
|
||||
return
|
||||
raise ValueError("No more space to allocate {} interrupt".format(interrupt_name))
|
||||
# Interrupt_id provided: check that interrupt_id is not already used and add interrupt
|
||||
else:
|
||||
for _name, _id in self.soc_interrupt_map.items():
|
||||
if interrupt_id == _id:
|
||||
raise ValueError("Interrupt conflict, {} already used by {} interrupt".format(
|
||||
interrupt_id, _name))
|
||||
self.soc_interrupt_map.update({interrupt_name: interrupt_id})
|
||||
self.irq.add(interrupt_name, interrupt_id)
|
||||
|
||||
def add_csr(self, csr_name, csr_id=None, allow_user_defined=False):
|
||||
# Check that csr_name is not already used
|
||||
if csr_name in self.soc_csr_map.keys():
|
||||
if allow_user_defined:
|
||||
return
|
||||
else:
|
||||
raise ValueError("CSR conflict, {} name already used".format(csr_name))
|
||||
|
||||
# Check that csr_id is in range
|
||||
if csr_id is not None and csr_id >= 2**self.csr_address_width:
|
||||
raise ValueError("{} CSR ID out of range ({}, max=31)".format(
|
||||
csr_name, csr_id))
|
||||
|
||||
# csr_id not provided: allocate csr to the first available id
|
||||
if csr_id is None:
|
||||
for n in range(2**self.csr_address_width):
|
||||
if n not in self.soc_csr_map.values():
|
||||
self.soc_csr_map.update({csr_name: n})
|
||||
return
|
||||
raise ValueError("No more space to allocate {} csr".format(csr_name))
|
||||
# csr_id provided: check that csr_id is not already used and add csr
|
||||
else:
|
||||
for _name, _id in self.soc_csr_map.items():
|
||||
if csr_id == _id:
|
||||
raise ValueError("CSR conflict, {} already used by {} csr".format(
|
||||
csr_id, _name))
|
||||
self.soc_csr_map.update({csr_name: csr_id})
|
||||
self.csr.add(csr_name, csr_id)
|
||||
|
||||
def initialize_rom(self, data):
|
||||
self.rom.mem.init = data
|
||||
|
||||
def add_wb_master(self, wbm):
|
||||
if self.finalized:
|
||||
raise FinalizeError
|
||||
self._wb_masters.append(wbm)
|
||||
self.bus.add_master(master=wbm)
|
||||
|
||||
def add_wb_slave(self, address_or_address_decoder, interface, size=None):
|
||||
if self.finalized:
|
||||
raise FinalizeError
|
||||
if size is not None:
|
||||
address_decoder = mem_decoder(address_or_address_decoder, size)
|
||||
else:
|
||||
address_decoder = address_or_address_decoder
|
||||
self._wb_slaves.append((address_decoder, interface))
|
||||
def add_wb_slave(self, address, interface, size=None):
|
||||
wb_name = None
|
||||
for name, region in self.bus.regions.items():
|
||||
if address == region.origin:
|
||||
wb_name = name
|
||||
break
|
||||
self.bus.add_slave(name=wb_name, slave=interface)
|
||||
|
||||
def add_csr_master(self, csrm):
|
||||
# CSR masters are not arbitrated, use this with precaution.
|
||||
|
@ -386,107 +338,17 @@ class SoCCore(Module):
|
|||
msg += "- 0x{:08x}-0x{:08x}\n".format(region_origin, region_origin + region_length - 1)
|
||||
raise ValueError(msg)
|
||||
|
||||
@staticmethod
|
||||
def check_regions_overlap(regions):
|
||||
i = 0
|
||||
while i < len(regions):
|
||||
n0 = list(regions.keys())[i]
|
||||
r0 = regions[n0]
|
||||
for n1 in list(regions.keys())[i+1:]:
|
||||
r1 = regions[n1]
|
||||
if ("linker" in r0.type) or ("linker" in r1.type):
|
||||
continue
|
||||
if r0.origin >= (r1.origin + r1.length):
|
||||
continue
|
||||
if r1.origin >= (r0.origin + r0.length):
|
||||
continue
|
||||
return (n0, n1)
|
||||
i += 1
|
||||
return None
|
||||
|
||||
def alloc_mem_region(self, name, length, type):
|
||||
# Use type to limit search regions
|
||||
search_regions = []
|
||||
if "io" in type:
|
||||
for _origin, _length in self.soc_io_regions.items():
|
||||
search_regions.append(SoCMemRegion(origin=_origin, length=_length, type=type))
|
||||
else:
|
||||
search_regions.append(SoCMemRegion(origin=0x00000000, length=0x80000000, type=type))
|
||||
# Iterate over search_regions and origin to find a candidate region
|
||||
for search_region in search_regions:
|
||||
origin = search_region.origin
|
||||
while (origin + length) < (search_region.origin + search_region.length):
|
||||
# Create a candidate mem region.
|
||||
candidate_region = SoCMemRegion(origin=origin, length=length, type=type)
|
||||
candidate_overlap = False
|
||||
# Check candidate does not overlap with allocated mem regions.
|
||||
for _, allocated_region in self.mem_regions.items():
|
||||
if self.check_regions_overlap({"0": allocated_region, "1": candidate_region}) is not None:
|
||||
origin = allocated_region.origin + allocated_region.length
|
||||
candidate_overlap = True
|
||||
break
|
||||
if not candidate_overlap:
|
||||
# If no overlap, the candidate is selected.
|
||||
#print("{} region allocated at: {:08x}".format(name, candidate_region.origin))
|
||||
return candidate_region
|
||||
msg = "Not enough addressable memory space to allocate mem region {} of length 0x{:0x}".format(
|
||||
name, length)
|
||||
raise ValueError(msg)
|
||||
|
||||
def add_mem_region(self, name, length, type="cached"):
|
||||
# Check name conflicts.
|
||||
if name in self.mem_regions.keys():
|
||||
raise ValueError("Memory region conflict, {} name already used".format(name))
|
||||
# Round length to next power of 2.
|
||||
length = 2**log2_int(length, False)
|
||||
# Get mem origin; if not defined in mem_map, allocate a new mem region, else use mem_map
|
||||
# mapping and check for type/overlap conflicts.
|
||||
origin = self.mem_map.get(name, None)
|
||||
if origin is None:
|
||||
self.mem_regions[name] = self.alloc_mem_region(name, length, type)
|
||||
else:
|
||||
# Check type
|
||||
if "io" in type:
|
||||
self.check_io_region(name,origin, length)
|
||||
# Add region
|
||||
self.mem_regions[name] = SoCMemRegion(origin, length, type)
|
||||
# Check overlap
|
||||
overlap = self.check_regions_overlap(self.mem_regions)
|
||||
if overlap is not None:
|
||||
o0, o1 = overlap[0], overlap[1]
|
||||
raise ValueError("Memory region conflict between {} ({}) and {} ({})".format(
|
||||
o0, self.mem_regions[o0],
|
||||
o1, self.mem_regions[o1],
|
||||
))
|
||||
|
||||
# FIXME: add deprecated warning?
|
||||
def add_memory_region(self, name, origin, length, type="cached", io_region=False):
|
||||
if io_region: # 2019-10-30: io_region retro-compatibility
|
||||
deprecated_warning(": io_region replaced by type=\"io\".")
|
||||
type = "io"
|
||||
assert name in self.mem_map.keys()
|
||||
self.add_mem_region(name, length, type)
|
||||
def add_memory_region(self, name, origin, length, type="cached"):
|
||||
self.bus.add_region(name, SoCRegion(origin=origin, size=length, cached="cached" in type))
|
||||
|
||||
def register_mem(self, name, address, interface, size=0x10000000):
|
||||
self.add_wb_slave(address, interface, size)
|
||||
self.add_memory_region(name, address, size)
|
||||
self.bus.add_slave(name, interface, SoCRegion(origin=address, size=size))
|
||||
|
||||
def register_rom(self, interface, rom_size=0xa000):
|
||||
self.add_wb_slave(self.soc_mem_map["rom"], interface, rom_size)
|
||||
self.add_memory_region("rom", self.cpu.reset_address, rom_size)
|
||||
|
||||
def check_csr_range(self, name, addr):
|
||||
if addr >= 1<<(self.csr_address_width+2):
|
||||
raise ValueError("{} CSR out of range, increase csr_address_width".format(name))
|
||||
|
||||
def check_csr_region(self, name, origin):
|
||||
for n, r in self.csr_regions.items():
|
||||
if n == name or r.origin == origin:
|
||||
raise ValueError("CSR region conflict between {} and {}".format(n, name))
|
||||
self.bus.add_slave("rom", interface, SoCRegion(origin=self.cpu.reset_address, size=rom_size))
|
||||
|
||||
def add_csr_region(self, name, origin, busword, obj):
|
||||
self.check_io_region(name, origin, 0x800)
|
||||
self.check_csr_region(name, origin)
|
||||
self.csr_regions[name] = SoCCSRRegion(origin, busword, obj)
|
||||
|
||||
def add_constant(self, name, value=None):
|
||||
|
@ -498,12 +360,12 @@ class SoCCore(Module):
|
|||
if memory is not None:
|
||||
name = name + "_" + memory.name_override
|
||||
try:
|
||||
return self.soc_csr_map[name]
|
||||
return self.csr.csrs[name]
|
||||
except KeyError as e:
|
||||
msg = "Undefined \"{}\" CSR.\n".format(name)
|
||||
msg += "Avalaible CSRs in {} ({}):\n".format(
|
||||
self.__class__.__name__, inspect.getfile(self.__class__))
|
||||
for k in sorted(self.soc_csr_map.keys()):
|
||||
for k in sorted(self.csr.csrs.keys()):
|
||||
msg += "- {}\n".format(k)
|
||||
raise RuntimeError(msg)
|
||||
except ValueError:
|
||||
|
@ -515,18 +377,22 @@ class SoCCore(Module):
|
|||
# Finalization ---------------------------------------------------------------------------------
|
||||
|
||||
def do_finalize(self):
|
||||
# retro-compat
|
||||
for region in self.bus.regions.values():
|
||||
region.length = region.size
|
||||
region.type = "cached" if region.cached else "io"
|
||||
|
||||
# Verify CPU has required memories
|
||||
if not isinstance(self.cpu, cpu.CPUNone):
|
||||
for name in ["rom", "sram"]:
|
||||
if name not in self.mem_regions.keys():
|
||||
if name not in self.bus.regions.keys():
|
||||
raise FinalizeError("CPU needs \"{}\" to be defined as memory or linker region".format(name))
|
||||
|
||||
SoC.do_finalize(self)
|
||||
|
||||
# Add the Wishbone Masters/Slaves interconnect
|
||||
if len(self._wb_masters):
|
||||
self.submodules.wishbonecon = wishbone.InterconnectShared(self._wb_masters,
|
||||
self._wb_slaves, register=True, timeout_cycles=self.wishbone_timeout_cycles)
|
||||
if self.with_ctrl and (self.wishbone_timeout_cycles is not None):
|
||||
self.comb += self.ctrl.bus_error.eq(self.wishbonecon.timeout.error)
|
||||
self.comb += self.ctrl.bus_error.eq(self.bus_interconnect.timeout.error)
|
||||
|
||||
# Collect and create CSRs
|
||||
self.submodules.csrbankarray = csr_bus.CSRBankArray(self,
|
||||
|
@ -543,13 +409,11 @@ class SoCCore(Module):
|
|||
|
||||
# Check and add CSRs regions
|
||||
for name, csrs, mapaddr, rmap in self.csrbankarray.banks:
|
||||
self.check_csr_range(name, 0x800*mapaddr)
|
||||
self.add_csr_region(name, (self.soc_mem_map["csr"] + 0x800*mapaddr),
|
||||
self.csr_data_width, csrs)
|
||||
|
||||
# Check and add Memory regions
|
||||
for name, memory, mapaddr, mmap in self.csrbankarray.srams:
|
||||
self.check_csr_range(name, 0x800*mapaddr)
|
||||
self.add_csr_region(name + "_" + memory.name_override,
|
||||
(self.soc_mem_map["csr"] + 0x800*mapaddr),
|
||||
self.csr_data_width, memory)
|
||||
|
@ -559,8 +423,7 @@ class SoCCore(Module):
|
|||
|
||||
# Add CSRs / Config items to constants
|
||||
for name, constant in self.csrbankarray.constants:
|
||||
self.add_constant(name.upper() + "_" + constant.name.upper(),
|
||||
constant.value.value)
|
||||
self.add_constant(name.upper() + "_" + constant.name.upper(), constant.value.value)
|
||||
for name, value in sorted(self.config.items(), key=itemgetter(0)):
|
||||
self.add_constant("CONFIG_" + name.upper(), value)
|
||||
if isinstance(value, str):
|
||||
|
@ -568,7 +431,7 @@ class SoCCore(Module):
|
|||
|
||||
# Connect interrupts
|
||||
if hasattr(self.cpu, "interrupt"):
|
||||
for _name, _id in sorted(self.soc_interrupt_map.items()):
|
||||
for _name, _id in sorted(self.irq.irqs.items()):
|
||||
if _name in self.cpu.interrupts.keys():
|
||||
continue
|
||||
if hasattr(self, _name):
|
||||
|
@ -577,26 +440,6 @@ class SoCCore(Module):
|
|||
self.comb += self.cpu.interrupt[_id].eq(module.ev.irq)
|
||||
self.constants[_name.upper() + "_INTERRUPT"] = _id
|
||||
|
||||
|
||||
# API retro-compatibility layer ----------------------------------------------------------------
|
||||
# Allow user to build the design the old API for ~3 months after the API change is introduced.
|
||||
# Adds warning and artificical delay to encourage user to update.
|
||||
|
||||
def add_retro_compat(self, kwargs):
|
||||
# 2019-10-09 : deprecate shadow_base, introduce io_regions
|
||||
if "shadow_base" in kwargs.keys():
|
||||
deprecated_warning(": shadow_base replaced by IO regions.")
|
||||
self.retro_compat_shadow_base = kwargs.get("shadow_base", 0x80000000)
|
||||
self.config["SHADOW_BASE"] = self.retro_compat_shadow_base
|
||||
|
||||
def __getattr__(self, name):
|
||||
# 2019-10-09: deprecate shadow_base, introduce io_regions
|
||||
if name == "shadow_base":
|
||||
deprecated_warning(": shadow_base replaced by IO regions.")
|
||||
return self.retro_compat_shadow_base
|
||||
else:
|
||||
return Module.__getattr__(self, name)
|
||||
|
||||
# SoCCore arguments --------------------------------------------------------------------------------
|
||||
|
||||
def soc_core_args(parser):
|
||||
|
|
Loading…
Reference in New Issue