From 20f4da0b15b43cdfa6f1af5693bf5e1cf9f4e94c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jean-Fran=C3=A7ois=20Nguyen?= Date: Thu, 26 Mar 2020 12:15:24 +0100 Subject: [PATCH] soc.base: add SoC and ConfigBuilder --- lambdasoc/soc/__init__.py | 0 lambdasoc/soc/base.py | 119 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 lambdasoc/soc/__init__.py create mode 100644 lambdasoc/soc/base.py diff --git a/lambdasoc/soc/__init__.py b/lambdasoc/soc/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lambdasoc/soc/base.py b/lambdasoc/soc/base.py new file mode 100644 index 0000000..df5dac5 --- /dev/null +++ b/lambdasoc/soc/base.py @@ -0,0 +1,119 @@ +import os +import re +import textwrap +import jinja2 + +from nmigen import tracer +from nmigen_soc.memory import MemoryMap +from nmigen.build.run import * + +from .. import __version__, software +from ..periph import Peripheral + + +__all__ = ["socproperty", "SoC", "ConfigBuilder"] + + +def socproperty(cls, src_loc_at=0): + name = tracer.get_var_name(depth=2 + src_loc_at) + __name = "__{}".format(name) + + def getter(self): + assert isinstance(self, SoC) + attr = getattr(self, __name, None) + if attr is None: + raise NotImplementedError("SoC {!r} does not have a {}" + .format(self, name)) + return attr + + def setter(self, value): + assert isinstance(self, SoC) + if not isinstance(value, cls): + raise TypeError("{} must be an instance of {}, not {!r}" + .format(name, cls.__name__, value)) + setattr(self, __name, value) + + return property(getter, setter) + + +class SoC: + memory_map = socproperty(MemoryMap) + + def build(self, build_dir="build/soc", do_build=True): + plan = ConfigBuilder.prepare(self) + if not do_build: + return plan + + products = plan.execute_local(build_dir) + return products + + +class ConfigBuilder: + file_templates = { + "build_{{name}}.sh": r""" + # {{autogenerated}} + set -e + {{emit_commands()}} + """, + "{{name}}_resources.csv": r""" + # {{autogenerated}} + # , , , + {% for resource, (start, end, width) in soc.memory_map.all_resources() -%} + {{resource.name}}, {{hex(start)}}, {{hex(end)}}, {{width}} + {% endfor %} + """, + } + command_templates = [] + + def prepare(self, soc, build_dir, name): + name = name or type(soc).__name__.lower() + + autogenerated = "Automatically generated by LambdaSoC {}. Do not edit.".format(__version__) + + def periph_addr(periph): + assert isinstance(periph, Peripheral) + periph_map = periph.bus.memory_map + for window, (start, end, ratio) in soc.memory_map.windows(): + if periph_map is window: + return start + raise KeyError(periph) + + def periph_csrs(periph): + assert isinstance(periph, Peripheral) + periph_map = periph.bus.memory_map + for resource, (start, end, width) in periph_map.all_resources(): + if isinstance(resource, csr.Element): + yield resource, (start, end, width) + + def emit_commands(): + commands = [] + for index, command_tpl in enumerate(self.command_templates): + command = render(command_tpl, origin="".format(index + 1)) + command = re.sub(r"\s+", " ", command) + commands.append(command) + return "\n".join(commands) + + def render(source, origin): + try: + source = textwrap.dedent(source).strip() + compiled = jinja2.Template(source, trim_blocks=True, lstrip_blocks=True) + except jinja2.TemplateSyntaxError as e: + e.args = ("{} (at {}:{})".format(e.message, origin, e.lineno),) + raise + return compiled.render({ + "autogenerated": autogenerated, + "build_dir": os.path.abspath(build_dir), + "emit_commands": emit_commands, + "hex": hex, + "name": name, + "periph_addr": periph_addr, + "periph_csrs": periph_csrs, + "soc": soc, + "software_dir": os.path.dirname(software.__file__), + }) + + plan = BuildPlan(script="build_{}".format(name)) + for filename_tpl, content_tpl in self.file_templates.items(): + plan.add_file(render(filename_tpl, origin=filename_tpl), + render(content_tpl, origin=content_tpl)) + return plan -- 2.30.2