From 8be5824e258b84df240d34636aaa539124b92c65 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 30 Sep 2019 10:59:36 +0200 Subject: [PATCH] soc/integration: use dicts for constants/mem_regions/csr_regions to cleanup/simplify iterations on theses --- litex/soc/integration/builder.py | 35 ++++------------ litex/soc/integration/common.py | 18 ++++++++ litex/soc/integration/export.py | 70 +++++++++++++------------------ litex/soc/integration/soc_core.py | 62 +++++++++++---------------- 4 files changed, 79 insertions(+), 106 deletions(-) diff --git a/litex/soc/integration/builder.py b/litex/soc/integration/builder.py index fe0fbbc72e..1120e4ccbb 100644 --- a/litex/soc/integration/builder.py +++ b/litex/soc/integration/builder.py @@ -66,18 +66,11 @@ def add_software_package(self, name, src_dir=None): self.software_packages.append((name, src_dir)) def _generate_includes(self): - cpu_type = self.soc.cpu_type - memory_regions = self.soc.get_memory_regions() - flash_boot_address = getattr(self.soc, "flash_boot_address", None) - shadow_base = getattr(self.soc, "shadow_base", None) - csr_regions = self.soc.get_csr_regions() - constants = self.soc.get_constants() - - buildinc_dir = os.path.join(self.output_dir, "software", "include") + buildinc_dir = os.path.join(self.output_dir, "software", "include") generated_dir = os.path.join(buildinc_dir, "generated") os.makedirs(generated_dir, exist_ok=True) - if cpu_type is not None: + if self.soc.cpu_type is not None: variables_contents = [] def define(k, v): variables_contents.append("{}={}\n".format(k, _makefile_escape(v))) @@ -91,7 +84,7 @@ def define(k, v): "COPY_TO_MAIN_RAM" : "0", "EXECUTE_IN_PLACE" : "0" } - if "main_ram" in (m[0] for m in memory_regions): + if "main_ram" in self.soc.mem_regions.keys(): exec_profiles["COPY_TO_MAIN_RAM"] = "1" else: exec_profiles["EXECUTE_IN_PLACE"] = "1" @@ -110,13 +103,13 @@ def define(k, v): cpu_interface.get_linker_output_format(self.soc.cpu)) write_to_file( os.path.join(generated_dir, "regions.ld"), - cpu_interface.get_linker_regions(memory_regions)) + cpu_interface.get_linker_regions(self.soc.mem_regions)) write_to_file( os.path.join(generated_dir, "mem.h"), - cpu_interface.get_mem_header(memory_regions, flash_boot_address, shadow_base)) + cpu_interface.get_mem_header(self.soc.mem_regions)) write_to_file( os.path.join(generated_dir, "csr.h"), - cpu_interface.get_csr_header(csr_regions, constants)) + cpu_interface.get_csr_header(self.soc.csr_regions, self.soc.constants)) write_to_file( os.path.join(generated_dir, "git.h"), cpu_interface.get_git_header() @@ -131,27 +124,15 @@ def define(k, v): self.soc.sdram.controller.settings.timing)) def _generate_csr_map(self, csr_json=None, csr_csv=None): - memory_regions = self.soc.get_memory_regions() - csr_regions = self.soc.get_csr_regions() - constants = self.soc.get_constants() - - shadow_base = getattr(self.soc, "shadow_base", None) - if shadow_base: - constants.append(('shadow_base', shadow_base)) - - flash_boot_address = getattr(self.soc, "flash_boot_address", None) - if flash_boot_address: - constants.append(('flash_boot_address', flash_boot_address)) - if csr_json is not None: csr_dir = os.path.dirname(os.path.realpath(csr_json)) os.makedirs(csr_dir, exist_ok=True) - write_to_file(csr_json, cpu_interface.get_csr_json(csr_regions, constants, memory_regions)) + write_to_file(csr_json, cpu_interface.get_csr_json(self.soc.csr_regions, self.soc.constants, self.soc.mem_regions)) if csr_csv is not None: csr_dir = os.path.dirname(os.path.realpath(csr_csv)) os.makedirs(csr_dir, exist_ok=True) - write_to_file(csr_csv, cpu_interface.get_csr_csv(csr_regions, constants, memory_regions)) + write_to_file(csr_csv, cpu_interface.get_csr_csv(self.soc.csr_regions, self.soc.constants, self.soc.mem_regions)) def _prepare_software(self): for name, src_dir in self.software_packages: diff --git a/litex/soc/integration/common.py b/litex/soc/integration/common.py index 991680a60b..0a34d59d1d 100644 --- a/litex/soc/integration/common.py +++ b/litex/soc/integration/common.py @@ -10,6 +10,8 @@ from migen import * +# Helpers ---------------------------------------------------------------------------------------- + def mem_decoder(address, size=0x10000000): address &= ~0x80000000 size = 2**log2_int(size, False) @@ -68,3 +70,19 @@ def get_mem_data(filename_or_regions, endianness="big", mem_size=None): data[int(base, 16)//4 + i] = struct.unpack(">I", w)[0] i += 1 return data + +# SoC primitives ----------------------------------------------------------------------------------- + +def SoCConstant(value): + return value + +class SoCMemRegion: + def __init__(self, origin, length): + self.origin = origin + self.length = length + +class SoCCSRRegion: + def __init__(self, origin, busword, obj): + self.origin = origin + self.busword = busword + self.obj = obj diff --git a/litex/soc/integration/export.py b/litex/soc/integration/export.py index 74c1ea2435..ef1d37a4de 100644 --- a/litex/soc/integration/export.py +++ b/litex/soc/integration/export.py @@ -23,15 +23,6 @@ from litex.build.tools import generated_banner -# Helpers ---------------------------------------------------------------------------------------- - -# FIXME: use OrderedDict for constants? -def get_constant(name, constants): - for n, v in constants: - if n == name: - return v - return None - # CPU files ---------------------------------------------------------------------------------------- def get_cpu_mak(cpu, compile_software): @@ -98,8 +89,8 @@ def get_linker_output_format(cpu): def get_linker_regions(regions): r = "MEMORY {\n" - for name, origin, length in regions: - r += "\t{} : ORIGIN = 0x{:08x}, LENGTH = 0x{:08x}\n".format(name, origin, length) + for name, region in regions.items(): + r += "\t{} : ORIGIN = 0x{:08x}, LENGTH = 0x{:08x}\n".format(name, region.origin, region.length) r += "}\n" return r @@ -115,20 +106,15 @@ def get_git_header(): r += "#endif\n" return r -def get_mem_header(regions, flash_boot_address, shadow_base): +def get_mem_header(regions): r = generated_banner("//") r += "#ifndef __GENERATED_MEM_H\n#define __GENERATED_MEM_H\n\n" - for name, base, size in regions: + for name, region in regions.items(): r += "#define {name}_BASE 0x{base:08x}L\n#define {name}_SIZE 0x{size:08x}\n\n".format( - name=name.upper(), base=base, size=size) - if flash_boot_address is not None: - r += "#define FLASH_BOOT_ADDRESS 0x{:08x}L\n\n".format(flash_boot_address) - if shadow_base is not None: - r += "#define SHADOW_BASE 0x{:08x}L\n\n".format(shadow_base) + name=name.upper(), base=region.origin, size=region.length) r += "#endif\n" return r - def _get_rw_functions_c(reg_name, reg_base, nwords, busword, alignment, read_only, with_access_functions): r = "" @@ -171,7 +157,7 @@ def _get_rw_functions_c(reg_name, reg_base, nwords, busword, alignment, read_onl def get_csr_header(regions, constants, with_access_functions=True, with_shadow_base=True, shadow_base=0x80000000): - alignment = get_constant("CONFIG_CSR_ALIGNMENT", constants) + alignment = constants.get("CONFIG_CSR_ALIGNMENT", 32) r = generated_banner("//") r += "#ifndef __GENERATED_CSR_H\n#define __GENERATED_CSR_H\n" if with_access_functions: @@ -186,15 +172,16 @@ def get_csr_header(regions, constants, with_access_functions=True, with_shadow_b r += "#else /* ! CSR_ACCESSORS_DEFINED */\n" r += "#include \n" r += "#endif /* ! CSR_ACCESSORS_DEFINED */\n" - for name, origin, busword, obj in regions: + for name, region in regions.items(): + origin = region.origin if not with_shadow_base: origin &= (~shadow_base) r += "\n/* "+name+" */\n" r += "#define CSR_"+name.upper()+"_BASE "+hex(origin)+"L\n" - if not isinstance(obj, Memory): - for csr in obj: - nr = (csr.size + busword - 1)//busword - r += _get_rw_functions_c(name + "_" + csr.name, origin, nr, busword, alignment, + if not isinstance(region.obj, Memory): + for csr in region.obj: + nr = (csr.size + region.busword - 1)//region.busword + r += _get_rw_functions_c(name + "_" + csr.name, origin, nr, region.busword, alignment, isinstance(csr, CSRStatus), with_access_functions) origin += alignment//8*nr if hasattr(csr, "fields"): @@ -203,7 +190,7 @@ def get_csr_header(regions, constants, with_access_functions=True, with_shadow_b r += "#define CSR_"+name.upper()+"_"+csr.name.upper()+"_"+field.name.upper()+"_SIZE "+str(field.size)+"\n" r += "\n/* constants */\n" - for name, value in constants: + for name, value in constants.items(): if value is None: r += "#define "+name+"\n" continue @@ -223,8 +210,8 @@ def get_csr_header(regions, constants, with_access_functions=True, with_shadow_b # JSON Export -------------------------------------------------------------------------------------- -def get_csr_json(csr_regions=[], constants=[], memory_regions=[]): - alignment = 32 if constants is None else get_constant("CONFIG_CSR_ALIGNMENT", constants) +def get_csr_json(csr_regions={}, constants={}, mem_regions={}): + alignment = constants.get("CONFIG_CSR_ALIGNMENT", 32) d = { "csr_bases": {}, @@ -233,25 +220,26 @@ def get_csr_json(csr_regions=[], constants=[], memory_regions=[]): "memories": {}, } - for name, origin, busword, obj in csr_regions: - d["csr_bases"][name] = origin - if not isinstance(obj, Memory): - for csr in obj: - size = (csr.size + busword - 1)//busword + for name, region in csr_regions.items(): + d["csr_bases"][name] = region.origin + region_origin = region.origin + if not isinstance(region.obj, Memory): + for csr in region.obj: + size = (csr.size + region.busword - 1)//region.busword d["csr_registers"][name + "_" + csr.name] = { - "addr": origin, + "addr": region_origin, "size": size, "type": "ro" if isinstance(csr, CSRStatus) else "rw" } - origin += alignment//8*size + region_origin += alignment//8*size - for name, value in constants: + for name, value in constants.items(): d["constants"][name.lower()] = value.lower() if isinstance(value, str) else value - for name, origin, length in memory_regions: + for name, region in mem_regions.items(): d["memories"][name.lower()] = { - "base": origin, - "size": length + "base": region.origin, + "size": region.length } return json.dumps(d, indent=4) @@ -259,8 +247,8 @@ def get_csr_json(csr_regions=[], constants=[], memory_regions=[]): # CSV Export -------------------------------------------------------------------------------------- -def get_csr_csv(csr_regions=[], constants=[], memory_regions=[]): - d = json.loads(get_csr_json(csr_regions, constants, memory_regions)) +def get_csr_csv(csr_regions={}, constants={}, mem_regions={}): + d = json.loads(get_csr_json(csr_regions, constants, mem_regions)) r = generated_banner("#") for name, value in d["csr_bases"].items(): r += "csr_base,{},0x{:08x},,\n".format(name, value) diff --git a/litex/soc/integration/soc_core.py b/litex/soc/integration/soc_core.py index b3f1ae4003..33be3dab44 100644 --- a/litex/soc/integration/soc_core.py +++ b/litex/soc/integration/soc_core.py @@ -100,18 +100,16 @@ def __init__(self, platform, clk_freq, self.platform = platform self.clk_freq = clk_freq - # config dictionary (store all SoC's parameters to be exported to software) - self.config = dict() - - # SoC's register/interrupt/memory mappings (default or user defined + dynamically allocateds) + # SoC's CSR/Mem/Interrupt mapping (default or user defined + dynamically allocateds) self.soc_csr_map = {} self.soc_interrupt_map = {} self.soc_mem_map = self.mem_map - # Regions / Constants lists - self._memory_regions = [] # (name, origin, length) - self._csr_regions = [] # (name, origin, busword, csr_list/Memory) - self._constants = [] # (name, value) + # SoC's Config/Constants/Regions + self.config = {} + self.constants = {} + self.mem_regions = {} + self.csr_regions = {} # Wishbone masters/slaves lists self._wb_masters = [] @@ -131,6 +129,7 @@ def __init__(self, platform, clk_freq, self.cpu_variant = cpu.check_format_cpu_variant(cpu_variant) self.shadow_base = shadow_base + self.config["SHADOW_BASE"] = shadow_base self.integrated_rom_size = integrated_rom_size self.integrated_rom_initialized = integrated_rom_init != [] @@ -352,12 +351,11 @@ def add_csr_master(self, csrm): def add_memory_region(self, name, origin, length): def in_this_region(addr): return addr >= origin and addr < origin + length - for n, o, l in self._memory_regions: - l = 2**log2_int(l, False) - if n == name or in_this_region(o) or in_this_region(o+l-1): + for n, r in self.mem_regions.items(): + r.length = 2**log2_int(r.length, False) + if n == name or in_this_region(r.origin) or in_this_region(r.origin + r.length - 1): raise ValueError("Memory region conflict between {} and {}".format(n, name)) - - self._memory_regions.append((name, origin, length)) + self.mem_regions[name] = SoCMemRegion(origin, length) def register_mem(self, name, address, interface, size=0x10000000): self.add_wb_slave(address, interface, size) @@ -367,34 +365,23 @@ 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 get_memory_regions(self): - return self._memory_regions - 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, o, l, obj in self._csr_regions: - if n == name or o == 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)) def add_csr_region(self, name, origin, busword, obj): self.check_csr_region(name, origin) - self._csr_regions.append((name, origin, busword, obj)) - - def get_csr_regions(self): - return self._csr_regions + self.csr_regions[name] = SoCCSRRegion(origin, busword, obj) def add_constant(self, name, value=None): - self._constants.append((name, value)) - - def get_constants(self): - r = [] - for _name, _id in sorted(self.soc_interrupt_map.items()): - r.append((_name.upper() + "_INTERRUPT", _id)) - r += self._constants - return r + if name in self.constants.keys(): + raise ValueError("Constant {} already declared.".format(name)) + self.constants[name] = SoCConstant(value) def get_csr_dev_address(self, name, memory): if memory is not None: @@ -418,11 +405,10 @@ def build(self, *args, **kwargs): def do_finalize(self): # Verify CPU has required memories - registered_mems = {regions[0] for regions in self._memory_regions} if self.cpu_type is not None: - for mem in "rom", "sram": - if mem not in registered_mems: - raise FinalizeError("CPU needs \"{}\" to be registered with SoC.register_mem()".format(mem)) + for name in "rom", "sram": + if name not in self.mem_regions.keys(): + raise FinalizeError("CPU needs \"{}\" to be registered with SoC.register_mem()".format(name)) # Add the Wishbone Masters/Slaves interconnect if len(self._wb_masters): @@ -459,11 +445,11 @@ def do_finalize(self): # Add CSRs / Config items to constants for name, constant in self.csrbankarray.constants: - self._constants.append(((name + "_" + constant.name).upper(), constant.value.value)) + self.add_constant(name + "_" + constant.name, constant.value.value) for name, value in sorted(self.config.items(), key=itemgetter(0)): - self._constants.append(("CONFIG_" + name.upper(), value)) + self.add_constant("CONFIG_" + name.upper(), value) if isinstance(value, str): - self._constants.append(("CONFIG_" + name.upper() + "_" + value, 1)) + self.add_constant("CONFIG_" + name.upper() + "_" + value) # Connect interrupts if hasattr(self, "cpu"): @@ -475,7 +461,7 @@ def do_finalize(self): module = getattr(self, _name) assert hasattr(module, 'ev'), "Submodule %s does not have EventManager (xx.ev) module" % _name self.comb += self.cpu.interrupt[_id].eq(module.ev.irq) - + self.constants[_name.upper() + "_INTERRUPT"] = _id # SoCCore arguments --------------------------------------------------------------------------------