From ff31959aea1e3815d95798996c3999f122314ae6 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Fri, 13 Jan 2017 01:33:48 +0100 Subject: [PATCH] merge most of misoc 54e1ef82 and migen e93d0601 changes --- litex/boards/targets/kc705.py | 11 +- litex/boards/targets/nexys_video.py | 1 - litex/build/altera/quartus.py | 3 +- litex/build/tools.py | 35 +++- litex/build/xilinx/common.py | 83 +++----- litex/build/xilinx/ise.py | 25 ++- litex/build/xilinx/platform.py | 8 +- litex/build/xilinx/vivado.py | 51 +++-- litex/gen/__init__.py | 1 + litex/gen/fhdl/bitcontainer.py | 65 +++---- litex/gen/fhdl/decorators.py | 3 +- litex/gen/fhdl/simplify.py | 96 ++++++++- litex/gen/fhdl/specials.py | 33 +--- litex/gen/fhdl/structure.py | 22 ++- litex/gen/fhdl/verilog.py | 50 ++++- litex/gen/fhdl/visit.py | 6 +- litex/gen/genlib/cdc.py | 19 +- litex/gen/genlib/fifo.py | 14 +- litex/gen/genlib/fsm.py | 57 +++++- litex/gen/genlib/io.py | 2 + litex/gen/sim/core.py | 47 +++-- litex/soc/integration/builder.py | 68 +++---- litex/soc/integration/cpu_interface.py | 45 ++--- litex/soc/integration/soc_core.py | 10 +- litex/soc/integration/soc_sdram.py | 2 +- litex/soc/interconnect/csr.py | 195 +++++++++++++++++++ litex/soc/interconnect/csr_bus.py | 30 +++ litex/soc/interconnect/csr_eventmanager.py | 83 +++++++- litex/soc/interconnect/wishbone.py | 15 +- litex/soc/software/bios/boot.c | 34 +++- litex/soc/software/bios/boot.h | 2 +- litex/soc/software/bios/main.c | 67 +------ litex/soc/software/include/base/inttypes.h | 2 + litex/soc/software/include/base/math.h | 14 ++ litex/soc/software/include/base/stddef.h | 2 +- litex/soc/software/include/base/stdlib.h | 1 + litex/soc/software/include/dyld/dyld.h | 3 +- litex/soc/software/include/fdlibm/fdlibm.h | 216 +++++++++++++++++++++ litex/soc/software/libbase/crt0-or1k.S | 2 + litex/soc/software/libbase/exception.c | 200 ++++++++++++++++++- litex/soc/software/libbase/vsnprintf.c | 54 +++--- 41 files changed, 1271 insertions(+), 406 deletions(-) create mode 100644 litex/soc/software/include/base/math.h create mode 100644 litex/soc/software/include/fdlibm/fdlibm.h diff --git a/litex/boards/targets/kc705.py b/litex/boards/targets/kc705.py index 9139be8e..c889d49e 100755 --- a/litex/boards/targets/kc705.py +++ b/litex/boards/targets/kc705.py @@ -131,12 +131,21 @@ class MiniSoC(BaseSoC): def __init__(self, *args, **kwargs): BaseSoC.__init__(self, *args, **kwargs) - self.submodules.ethphy = LiteEthPHY(self.platform.request("eth_clocks"), + eth_clocks = self.platform.request("eth_clocks") + self.submodules.ethphy = LiteEthPHY(eth_clocks, self.platform.request("eth"), clk_freq=self.clk_freq) self.submodules.ethmac = LiteEthMAC(phy=self.ethphy, dw=32, interface="wishbone") self.add_wb_slave(mem_decoder(self.mem_map["ethmac"]), self.ethmac.bus) self.add_memory_region("ethmac", self.mem_map["ethmac"] | self.shadow_base, 0x2000) + self.crg.cd_sys.clk.attr.add("keep") + self.ethphy.crg.cd_eth_tx.clk.attr.add("keep") + # period constraints are required here because of vivado + self.platform.add_period_constraint(self.crg.cd_sys.clk, 8.0) + self.platform.add_period_constraint(self.ethphy.crg.cd_eth_tx.clk, 8.0) + self.platform.add_false_path_constraints( + self.crg.cd_sys.clk, + self.ethphy.crg.cd_eth_tx.clk, eth_clocks.rx) def soc_kc705_args(parser): soc_sdram_args(parser) diff --git a/litex/boards/targets/nexys_video.py b/litex/boards/targets/nexys_video.py index 810f594d..3d52befd 100755 --- a/litex/boards/targets/nexys_video.py +++ b/litex/boards/targets/nexys_video.py @@ -4,7 +4,6 @@ import os from litex.gen import * from litex.gen.genlib.resetsync import AsyncResetSynchronizer -from litex.gen.fhdl.specials import Keep from litex.boards.platforms import nexys_video diff --git a/litex/build/altera/quartus.py b/litex/build/altera/quartus.py index e7a27622..af34b2eb 100644 --- a/litex/build/altera/quartus.py +++ b/litex/build/altera/quartus.py @@ -100,6 +100,7 @@ quartus_map --read_settings_files=on --write_settings_files=off {build_name} -c quartus_fit --read_settings_files=off --write_settings_files=off {build_name} -c {build_name} quartus_asm --read_settings_files=off --write_settings_files=off {build_name} -c {build_name} quartus_sta {build_name} -c {build_name} +quartus_cpf -c {build_name}.sof {build_name}.rbf """.format(build_name=build_name) # noqa build_script_file = "build_" + build_name + ".sh" @@ -115,7 +116,7 @@ class AlteraQuartusToolchain: def build(self, platform, fragment, build_dir="build", build_name="top", toolchain_path="/opt/Altera", run=True, **kwargs): cwd = os.getcwd() - tools.mkdir_noerror(build_dir) + os.makedirs(build_dir, exist_ok=True) os.chdir(build_dir) if not isinstance(fragment, _Fragment): diff --git a/litex/build/tools.py b/litex/build/tools.py index 34c054b1..f585f018 100644 --- a/litex/build/tools.py +++ b/litex/build/tools.py @@ -1,13 +1,9 @@ import os import struct from distutils.version import StrictVersion - - -def mkdir_noerror(d): - try: - os.mkdir(d) - except OSError: - pass +import re +import subprocess +import sys def language_by_filename(name): @@ -23,9 +19,6 @@ def write_to_file(filename, contents, force_unix=False): newline = None if force_unix: newline = "\n" - if os.path.exists(filename): - if open(filename, "r", newline=newline).read() == contents: - return with open(filename, "w", newline=newline) as f: f.write(contents) @@ -43,3 +36,25 @@ def versions(path): yield StrictVersion(n) except ValueError: continue + + +def sub_rules(lines, rules, max_matches=1): + for line in lines: + n = max_matches + for pattern, color in rules: + line, m = re.subn(pattern, color, line, n) + n -= m + if not n: + break + yield line + + +def subprocess_call_filtered(command, rules, *, max_matches=1, **kwargs): + proc = subprocess.Popen(command, stdout=subprocess.PIPE, + universal_newlines=True, bufsize=1, + **kwargs) + with proc: + for line in sub_rules(iter(proc.stdout.readline, ""), + rules, max_matches): + sys.stdout.write(line) + return proc.returncode diff --git a/litex/build/xilinx/common.py b/litex/build/xilinx/common.py index dcd47b25..3728a531 100644 --- a/litex/build/xilinx/common.py +++ b/litex/build/xilinx/common.py @@ -1,11 +1,15 @@ import os import sys -from distutils.version import StrictVersion +try: + import colorama + colorama.init() # install escape sequence translation on Windows + _have_colorama = True +except ImportError: + _have_colorama = False from litex.gen.fhdl.structure import * from litex.gen.fhdl.specials import Instance from litex.gen.fhdl.module import Module -from litex.gen.fhdl.specials import SynthesisDirective from litex.gen.genlib.cdc import * from litex.gen.genlib.resetsync import AsyncResetSynchronizer from litex.gen.genlib.io import * @@ -13,6 +17,20 @@ from litex.gen.genlib.io import * from litex.build import tools +colors = [] +if _have_colorama: + colors += [ + ("^ERROR:.*$", colorama.Fore.RED + colorama.Style.BRIGHT + + r"\g<0>" + colorama.Style.RESET_ALL), + ("^CRITICAL WARNING:.*$", colorama.Fore.RED + + r"\g<0>" + colorama.Style.RESET_ALL), + ("^WARNING:.*$", colorama.Fore.YELLOW + + r"\g<0>" + colorama.Style.RESET_ALL), + ("^INFO:.*$", colorama.Fore.GREEN + + r"\g<0>" + colorama.Style.RESET_ALL), + ] + + def settings(path, ver=None, sub=None): if ver is None: vers = list(tools.versions(path)) @@ -42,52 +60,18 @@ def settings(path, ver=None, sub=None): raise OSError("no Xilinx tools settings file found") -class XilinxNoRetimingVivadoImpl(Module): - def __init__(self, reg): - pass # No equivalent in Vivado - - -class XilinxNoRetimingVivado: - @staticmethod - def lower(dr): - return XilinxNoRetimingVivadoImpl(dr.reg) - - -class XilinxNoRetimingISEImpl(Module): - def __init__(self, reg): - self.specials += SynthesisDirective("attribute register_balancing of {r} is no", r=reg) - - -class XilinxNoRetimingISE: - @staticmethod - def lower(dr): - return XilinxNoRetimingISEImpl(dr.reg) - - -class XilinxMultiRegVivadoImpl(MultiRegImpl): +class XilinxMultiRegImpl(MultiRegImpl): def __init__(self, *args, **kwargs): MultiRegImpl.__init__(self, *args, **kwargs) - for reg in self.regs: - reg.attribute += " SHIFT_EXTRACT=\"NO\", ASYNC_REG=\"TRUE\"," + for r in self.regs: + r.attr.add("async_reg") + r.attr.add("no_shreg_extract") -class XilinxMultiRegVivado: +class XilinxMultiReg: @staticmethod def lower(dr): - return XilinxMultiRegVivadoImpl(dr.i, dr.o, dr.odomain, dr.n) - - -class XilinxMultiRegISEImpl(MultiRegImpl): - def __init__(self, *args, **kwargs): - MultiRegImpl.__init__(self, *args, **kwargs) - self.specials += [SynthesisDirective("attribute shreg_extract of {r} is no", r=r) - for r in self.regs] - - -class XilinxMultiRegISE: - @staticmethod - def lower(dr): - return XilinxMultiRegISEImpl(dr.i, dr.o, dr.odomain, dr.n) + return XilinxMultiRegImpl(dr.i, dr.o, dr.odomain, dr.n) class XilinxAsyncResetSynchronizerImpl(Module): @@ -99,6 +83,8 @@ class XilinxAsyncResetSynchronizerImpl(Module): Instance("FDPE", p_INIT=1, i_D=rst1, i_PRE=async_reset, i_CE=1, i_C=cd.clk, o_Q=cd.rst) ] + rst1.attr.add("async_reg") + cd.rst.attr.add("async_reg") class XilinxAsyncResetSynchronizer: @@ -145,6 +131,7 @@ class XilinxDDROutput: xilinx_special_overrides = { + MultiReg: XilinxMultiReg, AsyncResetSynchronizer: XilinxAsyncResetSynchronizer, DifferentialInput: XilinxDifferentialInput, DifferentialOutput: XilinxDifferentialOutput, @@ -152,18 +139,6 @@ xilinx_special_overrides = { } -xilinx_vivado_special_overrides = { - NoRetiming: XilinxNoRetimingVivado, - MultiReg: XilinxMultiRegVivado -} - - -xilinx_ise_special_overrides = { - NoRetiming: XilinxNoRetimingISE, - MultiReg: XilinxMultiRegISE -} - - class XilinxDDROutputImplS7(Module): def __init__(self, i1, i2, o, clk): self.specials += Instance("ODDR", diff --git a/litex/build/xilinx/ise.py b/litex/build/xilinx/ise.py index 656ed010..19bbe9ef 100644 --- a/litex/build/xilinx/ise.py +++ b/litex/build/xilinx/ise.py @@ -88,39 +88,48 @@ def _run_ise(build_name, ise_path, source, mode, ngdbuild_opt, script_ext = ".bat" shell = ["cmd", "/c"] build_script_contents = "@echo off\nrem Autogenerated by LiteX\n" + fail_stmt = " || exit /b" else: source_cmd = "source " script_ext = ".sh" shell = ["bash"] build_script_contents = "# Autogenerated by LiteX\nset -e\n" + fail_stmt = "" if source: settings = common.settings(ise_path, ver, "ISE_DS") build_script_contents += source_cmd + settings + "\n" ext = "ngc" build_script_contents += """ -xst -ifn {build_name}.xst +xst -ifn {build_name}.xst{fail_stmt} """ build_script_contents += """ -ngdbuild {ngdbuild_opt} -uc {build_name}.ucf {build_name}.{ext} {build_name}.ngd -map {map_opt} -o {build_name}_map.ncd {build_name}.ngd {build_name}.pcf -par {par_opt} {build_name}_map.ncd {build_name}.ncd {build_name}.pcf -bitgen {bitgen_opt} {build_name}.ncd {build_name}.bit +ngdbuild {ngdbuild_opt} -uc {build_name}.ucf {build_name}.{ext} {build_name}.ngd{fail_stmt} +map {map_opt} -o {build_name}_map.ncd {build_name}.ngd {build_name}.pcf{fail_stmt} +par {par_opt} {build_name}_map.ncd {build_name}.ncd {build_name}.pcf{fail_stmt} +bitgen {bitgen_opt} {build_name}.ncd {build_name}.bit{fail_stmt} """ build_script_contents = build_script_contents.format(build_name=build_name, ngdbuild_opt=ngdbuild_opt, bitgen_opt=bitgen_opt, ext=ext, - par_opt=par_opt, map_opt=map_opt) + par_opt=par_opt, map_opt=map_opt, fail_stmt=fail_stmt) build_script_contents += ise_commands.format(build_name=build_name) build_script_file = "build_" + build_name + script_ext tools.write_to_file(build_script_file, build_script_contents, force_unix=False) command = shell + [build_script_file] - r = subprocess.call(command) + r = tools.subprocess_call_filtered(command, common.colors) if r != 0: raise OSError("Subprocess failed") class XilinxISEToolchain: + attr_translate = { + "keep": ("keep", "true"), + "no_retiming": ("register_balancing", "no"), + "async_reg": None, + "no_shreg_extract": ("shreg_extract", "no") + } + def __init__(self): self.xst_opt = """-ifmt MIXED -use_new_parser yes @@ -150,7 +159,7 @@ class XilinxISEToolchain: ngdbuild_opt = self.ngdbuild_opt vns = None - tools.mkdir_noerror(build_dir) + os.makedirs(build_dir, exist_ok=True) cwd = os.getcwd() os.chdir(build_dir) try: diff --git a/litex/build/xilinx/platform.py b/litex/build/xilinx/platform.py index 8a625186..04c8b418 100644 --- a/litex/build/xilinx/platform.py +++ b/litex/build/xilinx/platform.py @@ -18,12 +18,10 @@ class XilinxPlatform(GenericPlatform): so = dict(common.xilinx_special_overrides) if self.device[:3] == "xc7": so.update(common.xilinx_s7_special_overrides) - if self.toolchain == "ise": - so.update(common.xilinx_vivado_special_overrides) - else: - so.update(common.xilinx_ise_special_overrides) so.update(special_overrides) - return GenericPlatform.get_verilog(self, *args, special_overrides=so, **kwargs) + return GenericPlatform.get_verilog(self, *args, + special_overrides=so, attr_translate=self.toolchain.attr_translate, **kwargs) + def build(self, *args, **kwargs): return self.toolchain.build(self, *args, **kwargs) diff --git a/litex/build/xilinx/vivado.py b/litex/build/xilinx/vivado.py index 4d9a8d2b..fb0932bc 100644 --- a/litex/build/xilinx/vivado.py +++ b/litex/build/xilinx/vivado.py @@ -56,28 +56,35 @@ def _run_vivado(build_name, vivado_path, source, ver=None): build_script_contents += "vivado -mode batch -source " + build_name + ".tcl\n" build_script_file = "build_" + build_name + ".bat" tools.write_to_file(build_script_file, build_script_contents) - r = subprocess.call([build_script_file]) + command = build_script_file else: build_script_contents = "# Autogenerated by LiteX\nset -e\n" - if vivado_path is None: - vivado_path = "/opt/Xilinx/Vivado" settings = common.settings(vivado_path, ver) build_script_contents += "source " + settings + "\n" build_script_contents += "vivado -mode batch -source " + build_name + ".tcl\n" build_script_file = "build_" + build_name + ".sh" tools.write_to_file(build_script_file, build_script_contents) - r = subprocess.call(["bash", build_script_file]) - + command = ["bash", build_script_file] + r = tools.subprocess_call_filtered(command, common.colors) if r != 0: raise OSError("Subprocess failed") class XilinxVivadoToolchain: + attr_translate = { + "keep": ("dont_touch", "true"), + "no_retiming": ("dont_touch", "true"), + "async_reg": ("async_reg", "true"), + "no_shreg_extract": None + } + def __init__(self): self.bitstream_commands = [] self.additional_commands = [] self.pre_synthesis_commands = [] self.with_phys_opt = False + self.clocks = dict() + self.false_paths = set() def _build_batch(self, platform, sources, build_name): tcl = [] @@ -103,7 +110,7 @@ class XilinxVivadoToolchain: tcl.append("route_design") tcl.append("report_route_status -file {}_route_status.rpt".format(build_name)) tcl.append("report_drc -file {}_drc.rpt".format(build_name)) - tcl.append("report_timing_summary -max_paths 10 -file {}_timing.rpt".format(build_name)) + tcl.append("report_timing_summary -datasheet -max_paths 10 -file {}_timing.rpt".format(build_name)) tcl.append("report_power -file {}_power.rpt".format(build_name)) for bitstream_command in self.bitstream_commands: tcl.append(bitstream_command.format(build_name=build_name)) @@ -113,15 +120,35 @@ class XilinxVivadoToolchain: tcl.append("quit") tools.write_to_file(build_name + ".tcl", "\n".join(tcl)) + def _convert_clocks(self, platform): + for clk, period in sorted(self.clocks.items(), key=lambda x: x[0].duid): + platform.add_platform_command( + "create_clock -name {clk} -period " + str(period) + + " [get_nets {clk}]", clk=clk) + for from_, to in sorted(self.false_paths, + key=lambda x: (x[0].duid, x[1].duid)): + if (from_ not in self.clocks + or to not in self.clocks): + raise ValueError("Vivado requires period " + "constraints on all clocks used in false paths") + platform.add_platform_command( + "set_false_path -from [get_clocks {from_}] -to [get_clocks {to}]", + from_=from_, to=to) + + # make sure add_*_constraint cannot be used again + del self.clocks + del self.false_paths + def build(self, platform, fragment, build_dir="build", build_name="top", toolchain_path=None, source=True, run=True, **kwargs): - tools.mkdir_noerror(build_dir) + os.makedirs(build_dir, exist_ok=True) cwd = os.getcwd() os.chdir(build_dir) if not isinstance(fragment, _Fragment): fragment = fragment.get_fragment() platform.finalize(fragment) + self._convert_clocks(platform) v_output = platform.get_verilog(fragment, name=build_name, **kwargs) named_sc, named_pc = platform.resolve_signals(v_output.ns) v_file = build_name + ".v" @@ -137,11 +164,9 @@ class XilinxVivadoToolchain: return v_output.ns def add_period_constraint(self, platform, clk, period): - platform.add_platform_command( - "create_clock -name {clk} -period " + str(period) + - " [get_nets {clk}]", clk=clk) + if clk in self.clocks: + raise ValueError("A period constraint already exists") + self.clocks[clk] = period def add_false_path_constraint(self, platform, from_, to): - platform.add_platform_command( - "set_false_path -from [get_clocks {from_}] -to [get_clocks {to}]", - from_=from_, to=to) + self.false_paths.add((from_, to)) diff --git a/litex/gen/__init__.py b/litex/gen/__init__.py index 264a79e2..2cd38253 100644 --- a/litex/gen/__init__.py +++ b/litex/gen/__init__.py @@ -3,6 +3,7 @@ from litex.gen.fhdl.module import * from litex.gen.fhdl.specials import * from litex.gen.fhdl.bitcontainer import * from litex.gen.fhdl.decorators import * +from litex.gen.fhdl.simplify import * from litex.gen.sim import * diff --git a/litex/gen/fhdl/bitcontainer.py b/litex/gen/fhdl/bitcontainer.py index 11a3ede7..e7894bce 100644 --- a/litex/gen/fhdl/bitcontainer.py +++ b/litex/gen/fhdl/bitcontainer.py @@ -5,12 +5,10 @@ __all__ = ["log2_int", "bits_for", "value_bits_sign"] def log2_int(n, need_pow2=True): - l = 1 - r = 0 - while l < n: - l *= 2 - r += 1 - if need_pow2 and l != n: + if n == 0: + return 0 + r = (n - 1).bit_length() + if need_pow2 and (1 << r) != n: raise ValueError("Not a power of 2") return r @@ -26,6 +24,21 @@ def bits_for(n, require_sign_bit=False): return r +def _bitwise_binary_bits_sign(a, b): + if not a[1] and not b[1]: + # both operands unsigned + return max(a[0], b[0]), False + elif a[1] and b[1]: + # both operands signed + return max(a[0], b[0]), True + elif not a[1] and b[1]: + # first operand unsigned (add sign bit), second operand signed + return max(a[0] + 1, b[0]), True + else: + # first signed, second operand unsigned (add sign bit) + return max(a[0], b[0] + 1), True + + def value_bits_sign(v): """Bit length and signedness of a value. @@ -53,18 +66,13 @@ def value_bits_sign(v): elif isinstance(v, f._Operator): obs = list(map(value_bits_sign, v.operands)) if v.op == "+" or v.op == "-": - if not obs[0][1] and not obs[1][1]: - # both operands unsigned - return max(obs[0][0], obs[1][0]) + 1, False - elif obs[0][1] and obs[1][1]: - # both operands signed - return max(obs[0][0], obs[1][0]) + 1, True - elif not obs[0][1] and obs[1][1]: - # first operand unsigned (add sign bit), second operand signed - return max(obs[0][0] + 1, obs[1][0]) + 1, True - else: - # first signed, second operand unsigned (add sign bit) - return max(obs[0][0], obs[1][0] + 1) + 1, True + if len(obs) == 1: + if v.op == "-" and not obs[0][1]: + return obs[0][0] + 1, True + else: + return obs[0] + n, s = _bitwise_binary_bits_sign(*obs) + return n + 1, s elif v.op == "*": if not obs[0][1] and not obs[1][1]: # both operands unsigned @@ -88,23 +96,14 @@ def value_bits_sign(v): extra = 0 return obs[0][0] + extra, obs[0][1] elif v.op == "&" or v.op == "^" or v.op == "|": - if not obs[0][1] and not obs[1][1]: - # both operands unsigned - return max(obs[0][0], obs[1][0]), False - elif obs[0][1] and obs[1][1]: - # both operands signed - return max(obs[0][0], obs[1][0]), True - elif not obs[0][1] and obs[1][1]: - # first operand unsigned (add sign bit), second operand signed - return max(obs[0][0] + 1, obs[1][0]), True - else: - # first signed, second operand unsigned (add sign bit) - return max(obs[0][0], obs[1][0] + 1), True - elif v.op == "<" or v.op == "<=" or v.op == "==" or v.op == "!=" \ - or v.op == ">" or v.op == ">=": - return 1, False + return _bitwise_binary_bits_sign(*obs) + elif (v.op == "<" or v.op == "<=" or v.op == "==" or v.op == "!=" or + v.op == ">" or v.op == ">="): + return 1, False elif v.op == "~": return obs[0] + elif v.op == "m": + return _bitwise_binary_bits_sign(obs[1], obs[2]) else: raise TypeError elif isinstance(v, f._Slice): diff --git a/litex/gen/fhdl/decorators.py b/litex/gen/fhdl/decorators.py index 59444eaf..347eaaa1 100644 --- a/litex/gen/fhdl/decorators.py +++ b/litex/gen/fhdl/decorators.py @@ -28,7 +28,8 @@ class ModuleTransformer: return f Wrapped.__name__ = victim.__name__ - # "{}_{}".format(self.__class__.__name__, victim.__name__) + Wrapped.__doc__ = victim.__doc__ + Wrapped.__module__ = victim.__module__ return Wrapped def wrap_instance(self, victim): diff --git a/litex/gen/fhdl/simplify.py b/litex/gen/fhdl/simplify.py index 5f31a470..864ab37e 100644 --- a/litex/gen/fhdl/simplify.py +++ b/litex/gen/fhdl/simplify.py @@ -2,6 +2,7 @@ from litex.gen.fhdl.structure import * from litex.gen.fhdl.specials import Memory, _MemoryPort, WRITE_FIRST, NO_CHANGE from litex.gen.fhdl.decorators import ModuleTransformer from litex.gen.util.misc import gcd_multiple +from litex.gen.fhdl.bitcontainer import log2_int class FullMemoryWE(ModuleTransformer): @@ -10,13 +11,11 @@ class FullMemoryWE(ModuleTransformer): def transform_fragment(self, i, f): newspecials = set() - replaced_ports = set() for orig in f.specials: if not isinstance(orig, Memory): newspecials.add(orig) continue - global_granularity = gcd_multiple([p.we_granularity if p.we_granularity else orig.width for p in orig.ports]) if global_granularity == orig.width: newspecials.add(orig) # nothing to do @@ -46,13 +45,11 @@ class FullMemoryWE(ModuleTransformer): clock_domain=port.clock.cd) newmem.ports.append(newport) newspecials.add(newport) - - for port in orig.ports: - replaced_ports.add(port) self.replacements[orig] = newmems - newspecials -= replaced_ports f.specials = newspecials + for oldmem in self.replacements.keys(): + f.specials -= set(oldmem.ports) class MemoryToArray(ModuleTransformer): @@ -111,7 +108,7 @@ class MemoryToArray(ModuleTransformer): m = i*port.we_granularity M = (i+1)*port.we_granularity sync.append(If(port.we[i], - storage[port.adr][m:M].eq(port.dat_w))) + storage[port.adr][m:M].eq(port.dat_w[m:M]))) else: sync.append(If(port.we, storage[port.adr].eq(port.dat_w))) @@ -120,3 +117,88 @@ class MemoryToArray(ModuleTransformer): newspecials -= processed_ports f.specials = newspecials + + +class SplitMemory(ModuleTransformer): + """Split memories with depths that are not powers of two into smaller + power-of-two memories. + + This prevents toolchains from rounding up and wasting resources.""" + + def transform_fragment(self, i, f): + old_specials, f.specials = f.specials, set() + old_ports = set() + + for old in old_specials: + if not isinstance(old, Memory): + f.specials.add(old) + continue + try: + log2_int(old.depth, need_pow2=True) + f.specials.add(old) + except ValueError: + new, comb, sync = self._split_mem(old) + old_ports |= set(old.ports) + f.specials.update(new) + f.comb += comb + for cd, sy in sync.items(): + s = f.sync.setdefault(cd, []) + s += sy + f.specials -= old_ports + + def _split_mem(self, mem): + depths = [1 << i for i in range(log2_int(mem.depth, need_pow2=False)) + if mem.depth & (1 << i)] + depths.reverse() + inits = None + if mem.init is not None: + inits = list(mem.init) + mems = [] + for i, depth in enumerate(depths): + init = None + if inits is not None: + init = inits[:depth] + del inits[:depth] + name = "{}_part{}".format(mem.name_override, i) + mems.append(Memory(width=mem.width, depth=depth, + init=init, name=name)) + ports = [] + comb = [] + sync = {} + for port in mem.ports: + p, c, s = self._split_port(port, mems) + ports += p + comb += c + sy = sync.setdefault(port.clock.cd, []) + sy += s + return mems + ports, comb, sync + + def _split_port(self, port, mems): + ports = [mem.get_port(write_capable=port.we is not None, + async_read=port.async_read, + has_re=port.re is not None, + we_granularity=port.we_granularity, + mode=port.mode, + clock_domain=port.clock.cd) + for mem in mems] + + sel = Signal(max=len(ports), reset=len(ports) - 1) + sel_r = Signal.like(sel) + eq = sel_r.eq(sel) + if port.re is not None: + eq = If(port.re, eq) + comb, sync = [], [] + if port.async_read: + comb += [eq] + else: + sync += [eq] + comb += reversed([If(~port.adr[len(p.adr)], sel.eq(i)) + for i, p in enumerate(ports)]) + comb += [p.adr.eq(port.adr) for p in ports] + comb.append(port.dat_r.eq(Array([p.dat_r for p in ports])[sel_r])) + if port.we is not None: + comb.append(Array([p.we for p in ports])[sel].eq(port.we)) + comb += [p.dat_w.eq(port.dat_w) for p in ports] + if port.re is not None: + comb += [p.re.eq(port.re) for p in ports] + return ports, comb, sync diff --git a/litex/gen/fhdl/specials.py b/litex/gen/fhdl/specials.py index d4fc2bf1..5520f893 100644 --- a/litex/gen/fhdl/specials.py +++ b/litex/gen/fhdl/specials.py @@ -111,7 +111,12 @@ class Instance(Special): self.items = list(items) self.synthesis_directive = synthesis_directive for k, v in sorted(kwargs.items(), key=itemgetter(0)): - item_type, item_name = k.split("_", maxsplit=1) + try: + item_type, item_name = k.split("_", maxsplit=1) + except ValueError: + raise TypeError("Wrong format for value '" + str(k) + + "', format should be 'type_name'") + item_class = { "i": Instance.Input, "o": Instance.Output, @@ -277,7 +282,7 @@ class Memory(Special): data_regs = {} for port in memory.ports: if not port.async_read: - if port.mode == WRITE_FIRST and port.we is not None: + if port.mode == WRITE_FIRST: adr_reg = Signal(name_override="memadr") r += "reg [" + str(adrbits-1) + ":0] " \ + gn(adr_reg) + ";\n" @@ -303,11 +308,11 @@ class Memory(Special): r += "\tif (" + gn(port.we) + ")\n" r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "] <= " + gn(port.dat_w) + ";\n" if not port.async_read: - if port.mode == WRITE_FIRST and port.we is not None: + if port.mode == WRITE_FIRST: rd = "\t" + gn(adr_regs[id(port)]) + " <= " + gn(port.adr) + ";\n" else: bassign = gn(data_regs[id(port)]) + " <= " + gn(memory) + "[" + gn(port.adr) + "];\n" - if port.mode == READ_FIRST or port.we is None: + if port.mode == READ_FIRST: rd = "\t" + bassign elif port.mode == NO_CHANGE: rd = "\tif (!" + gn(port.we) + ")\n" \ @@ -323,7 +328,7 @@ class Memory(Special): if port.async_read: r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(port.adr) + "];\n" else: - if port.mode == WRITE_FIRST and port.we is not None: + if port.mode == WRITE_FIRST: r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(adr_regs[id(port)]) + "];\n" else: r += "assign " + gn(port.dat_r) + " = " + gn(data_regs[id(port)]) + ";\n" @@ -340,21 +345,3 @@ class Memory(Special): r += "end\n\n" return r - - -class SynthesisDirective(Special): - def __init__(self, template, **signals): - Special.__init__(self) - self.template = template - self.signals = signals - - @staticmethod - def emit_verilog(directive, ns, add_data_file): - name_dict = dict((k, ns.get_name(sig)) for k, sig in directive.signals.items()) - formatted = directive.template.format(**name_dict) - return "// synthesis " + formatted + "\n" - - -class Keep(SynthesisDirective): - def __init__(self, signal): - SynthesisDirective.__init__(self, "attribute keep of {s} is true", s=signal) diff --git a/litex/gen/fhdl/structure.py b/litex/gen/fhdl/structure.py index 96c2fb27..9dc922dc 100644 --- a/litex/gen/fhdl/structure.py +++ b/litex/gen/fhdl/structure.py @@ -1,5 +1,6 @@ import builtins as _builtins import collections as _collections +import re as _re from litex.gen.fhdl import tracer as _tracer from litex.gen.util.misc import flat_iteration as _flat_iteration @@ -136,7 +137,8 @@ def wrap(value): if isinstance(value, (bool, int)): value = Constant(value) if not isinstance(value, _Value): - raise TypeError("Object is not a Migen value") + raise TypeError("Object '{}' of type {} is not a Migen value" + .format(value, type(value))) return value @@ -310,12 +312,20 @@ class Signal(_Value): determined by the integer range given by `min` (inclusive, defaults to 0) and `max` (exclusive, defaults to 2). related : Signal or None + attr : set of synthesis attributes """ - def __init__(self, bits_sign=None, name=None, variable=False, reset=0, name_override=None, min=None, max=None, related=None, attribute=""): + _name_re = _re.compile(r"^[a-zA-Z_][a-zA-Z0-9_]*$") + + def __init__(self, bits_sign=None, name=None, variable=False, reset=0, name_override=None, min=None, max=None, related=None, attr=None): from litex.gen.fhdl.bitcontainer import bits_for _Value.__init__(self) + for n in [name, name_override]: + if n is not None and not self._name_re.match(n): + raise ValueError("Signal name {} is not a valid Python identifier" + .format(repr(n))) + # determine number of bits and signedness if bits_sign is None: if min is None: @@ -332,15 +342,19 @@ class Signal(_Value): self.nbits, self.signed = bits_sign else: self.nbits, self.signed = bits_sign, False + if isinstance(reset, (bool, int)): + reset = Constant(reset, (self.nbits, self.signed)) if not isinstance(self.nbits, int) or self.nbits <= 0: raise ValueError("Signal width must be a strictly positive integer") + if attr is None: + attr = set() self.variable = variable # deprecated self.reset = reset self.name_override = name_override self.backtrace = _tracer.trace_back(name) self.related = related - self.attribute = attribute + self.attr = attr def __setattr__(self, k, v): if k == "reset": @@ -526,7 +540,7 @@ class Case(_Statement): for k, v in cases.items(): if isinstance(k, (bool, int)): k = Constant(k) - if (not isinstance(k, Constant) + if (not isinstance(k, Constant) and not (isinstance(k, str) and k == "default")): raise TypeError("Case object is not a Migen constant") if not isinstance(v, _collections.Iterable): diff --git a/litex/gen/fhdl/verilog.py b/litex/gen/fhdl/verilog.py index 4a647dd1..1fb6024b 100644 --- a/litex/gen/fhdl/verilog.py +++ b/litex/gen/fhdl/verilog.py @@ -116,9 +116,7 @@ def _printexpr(ns, node): def _printnode(ns, at, level, node): - if node is None: - return "" - elif isinstance(node, Display): + if isinstance(node, Display): s = "\"" + node.s + "\\r\"" for arg in node.args: s += ", " @@ -176,8 +174,30 @@ def _list_comb_wires(f): r |= g[0] return r +def _printattr(sig, attr_translate): + r = "" + firsta = True + for attr in sorted(sig.attr, + key=lambda x: ("", x) if isinstance(x, str) else x): + if isinstance(attr, tuple): + # platform-dependent attribute + attr_name, attr_value = attr + else: + # translated attribute + at = attr_translate[attr] + if at is None: + continue + attr_name, attr_value = at + if not firsta: + r += ", " + firsta = False + r += attr_name + " = \"" + attr_value + "\"" + if r: + r = "(* " + r + " *)" + return r -def _printheader(f, ios, name, ns, + +def _printheader(f, ios, name, ns, attr_translate, reg_initialization): sigs = list_signals(f) | list_special_ios(f, True, True, True) special_outs = list_special_ios(f, False, True, True) @@ -190,6 +210,9 @@ def _printheader(f, ios, name, ns, if not firstp: r += ",\n" firstp = False + attr = _printattr(sig, attr_translate) + if attr: + r += "\t" + attr if sig in inouts: r += "\tinout " + _printsig(ns, sig) elif sig in targets: @@ -201,6 +224,9 @@ def _printheader(f, ios, name, ns, r += "\tinput " + _printsig(ns, sig) r += "\n);\n\n" for sig in sorted(sigs - ios, key=lambda x: x.duid): + attr = _printattr(sig, attr_translate) + if attr: + r += attr + " " if sig in wires: r += "wire " + _printsig(ns, sig) + ";\n" else: @@ -219,15 +245,19 @@ def _printcomb(f, ns, r = "" if f.comb: if dummy_signal: - # Generate a dummy event to get the simulator - # to run the combinatorial process once at the beginning. + explanation = """ +// Adding a dummy event (using a dummy signal 'dummy_s') to get the simulator +// to run the combinatorial process once at the beginning. +""" syn_off = "// synthesis translate_off\n" syn_on = "// synthesis translate_on\n" dummy_s = Signal(name_override="dummy_s") + r += explanation r += syn_off r += "reg " + _printsig(ns, dummy_s) + ";\n" r += "initial " + ns.get_name(dummy_s) + " <= 1'd0;\n" r += syn_on + r += "\n" groups = group_by_targets(f.comb) @@ -280,8 +310,14 @@ def _printspecials(overrides, specials, ns, add_data_file): return r +class DummyAttrTranslate: + def __getitem__(self, k): + return (k, "true") + + def convert(f, ios=None, name="top", special_overrides=dict(), + attr_translate=DummyAttrTranslate(), create_clock_domains=True, display_run=False, reg_initialization=True, @@ -323,7 +359,7 @@ def convert(f, ios=None, name="top", r.ns = ns src = "/* Machine-generated using LiteX gen */\n" - src += _printheader(f, ios, name, ns, + src += _printheader(f, ios, name, ns, attr_translate, reg_initialization=reg_initialization) src += _printcomb(f, ns, display_run=display_run, diff --git a/litex/gen/fhdl/visit.py b/litex/gen/fhdl/visit.py index 84bb8adc..676ceb96 100644 --- a/litex/gen/fhdl/visit.py +++ b/litex/gen/fhdl/visit.py @@ -38,7 +38,7 @@ class NodeVisitor: self.visit_clock_domains(node) elif isinstance(node, _ArrayProxy): self.visit_ArrayProxy(node) - elif node is not None: + else: self.visit_unknown(node) def visit_Constant(self, node): @@ -140,10 +140,8 @@ class NodeTransformer: return self.visit_clock_domains(node) elif isinstance(node, _ArrayProxy): return self.visit_ArrayProxy(node) - elif node is not None: - return self.visit_unknown(node) else: - return None + return self.visit_unknown(node) def visit_Constant(self, node): return node diff --git a/litex/gen/genlib/cdc.py b/litex/gen/genlib/cdc.py index 6bac9e00..72efb03f 100644 --- a/litex/gen/genlib/cdc.py +++ b/litex/gen/genlib/cdc.py @@ -1,3 +1,7 @@ +""" +Clock domain crossing module +""" + from litex.gen.fhdl.structure import * from litex.gen.fhdl.module import Module from litex.gen.fhdl.specials import Special, Memory @@ -6,17 +10,6 @@ from litex.gen.genlib.misc import WaitTimer from litex.gen.genlib.resetsync import AsyncResetSynchronizer -class NoRetiming(Special): - def __init__(self, reg): - Special.__init__(self) - self.reg = reg - - # do nothing - @staticmethod - def lower(dr): - return Module() - - class MultiRegImpl(Module): def __init__(self, i, o, odomain, n): self.i = i @@ -34,7 +27,8 @@ class MultiRegImpl(Module): sd += reg.eq(src) src = reg self.comb += self.o.eq(src) - self.specials += [NoRetiming(reg) for reg in self.regs] + for reg in self.regs: + reg.attr.add("no_retiming") class MultiReg(Special): @@ -114,6 +108,7 @@ class BusSynchronizer(Module): ibuffer = Signal(width) obuffer = Signal(width) sync_i += If(self._pong.o, ibuffer.eq(self.i)) + ibuffer.attr.add("no_retiming") self.specials += MultiReg(ibuffer, obuffer, odomain) sync_o += If(self._ping.o, self.o.eq(obuffer)) diff --git a/litex/gen/genlib/fifo.py b/litex/gen/genlib/fifo.py index 167f9643..f24449ff 100644 --- a/litex/gen/genlib/fifo.py +++ b/litex/gen/genlib/fifo.py @@ -3,7 +3,7 @@ from litex.gen.fhdl.module import Module from litex.gen.fhdl.specials import Memory from litex.gen.fhdl.bitcontainer import log2_int from litex.gen.fhdl.decorators import ClockDomainsRenamer -from litex.gen.genlib.cdc import NoRetiming, MultiReg, GrayCounter +from litex.gen.genlib.cdc import MultiReg, GrayCounter def _inc(signal, modulo): @@ -177,15 +177,11 @@ class AsyncFIFO(Module, _FIFOInterface): ] produce_rdomain = Signal(depth_bits+1) - self.specials += [ - NoRetiming(produce.q), - MultiReg(produce.q, produce_rdomain, "read") - ] + produce.q.attr.add("no_retiming") + self.specials += MultiReg(produce.q, produce_rdomain, "read") consume_wdomain = Signal(depth_bits+1) - self.specials += [ - NoRetiming(consume.q), - MultiReg(consume.q, consume_wdomain, "write") - ] + consume.q.attr.add("no_retiming") + self.specials += MultiReg(consume.q, consume_wdomain, "write") if depth_bits == 1: self.comb += self.writable.eq((produce.q[-1] == consume_wdomain[-1]) | (produce.q[-2] == consume_wdomain[-2])) diff --git a/litex/gen/genlib/fsm.py b/litex/gen/genlib/fsm.py index de20e745..044b95d9 100644 --- a/litex/gen/genlib/fsm.py +++ b/litex/gen/genlib/fsm.py @@ -40,7 +40,7 @@ def _target_eq(a, b): elif ty == _Slice: return (_target_eq(a.value, b.value) and a.start == b.start - and a.end == b.end) + and a.stop == b.stop) elif ty == _ArrayProxy: return (all(_target_eq(x, y) for x, y in zip(a.choices, b.choices)) and _target_eq(a.key, b.key)) @@ -82,8 +82,47 @@ class _LowerNext(NodeTransformer): else: return node - class FSM(Module): + """ + Finite state machine + + Any Python objects can be used as states, e.g. strings. + + Parameters + ---------- + reset_state + Reset state. Defaults to the first added state. + + Examples + -------- + + >>> self.active = Signal() + >>> self.bitno = Signal(3) + >>> + >>> fsm = FSM(reset_state="START") + >>> self.submodules += fsm + >>> + >>> fsm.act("START", + ... self.active.eq(1), + ... If(strobe, + ... NextState("DATA") + ... ) + ... ) + >>> fsm.act("DATA", + ... self.active.eq(1), + ... If(strobe, + ... NextValue(self.bitno, self.bitno + 1) + ... If(self.bitno == 7, + ... NextState("END") + ... ) + ... ) + ... ) + >>> fsm.act("END", + ... self.active.eq(0), + ... NextState("STOP") + ... ) + + """ def __init__(self, reset_state=None): self.actions = OrderedDict() self.state_aliases = dict() @@ -95,6 +134,16 @@ class FSM(Module): self.after_leaving_signals = OrderedDict() def act(self, state, *statements): + """ + Schedules `statements` to be executed in `state`. Statements may include: + + * combinatorial statements of form `a.eq(b)`, equivalent to + `self.comb += a.eq(b)` when the FSM is in the given `state`; + * synchronous statements of form `NextValue(a, b)`, equivalent to + `self.sync += a.eq(b)` when the FSM is in the given `state`; + * a statement of form `NextState(new_state)`, selecting the next state; + * `If`, `Case`, etc. + """ if self.finalized: raise FinalizeError if self.reset_state is None: @@ -119,6 +168,10 @@ class FSM(Module): self.state_aliases[name] = target def ongoing(self, state): + """ + Returns a signal that has the value 1 when the FSM is in the given `state`, + and 0 otherwise. + """ is_ongoing = Signal() self.act(state, is_ongoing.eq(1)) return is_ongoing diff --git a/litex/gen/genlib/io.py b/litex/gen/genlib/io.py index 5c441e66..a30218ca 100644 --- a/litex/gen/genlib/io.py +++ b/litex/gen/genlib/io.py @@ -38,6 +38,8 @@ class DifferentialOutput(Special): class CRG(Module): + """ Clock and Reset Generator """ + def __init__(self, clk, rst=0): self.clock_domains.cd_sys = ClockDomain() self.clock_domains.cd_por = ClockDomain(reset_less=True) diff --git a/litex/gen/sim/core.py b/litex/gen/sim/core.py index 27e8f13f..c23c2a65 100644 --- a/litex/gen/sim/core.py +++ b/litex/gen/sim/core.py @@ -11,7 +11,9 @@ from litex.gen.fhdl.bitcontainer import value_bits_sign from litex.gen.fhdl.tools import (list_targets, list_signals, insert_resets, lower_specials) from litex.gen.fhdl.simplify import MemoryToArray -from litex.gen.fhdl.specials import _MemoryLocation, _MemoryPort +from litex.gen.fhdl.specials import _MemoryLocation +from litex.gen.fhdl.module import Module +from litex.gen.genlib.resetsync import AsyncResetSynchronizer from litex.gen.sim.vcd import VCDWriter, DummyVCDWriter @@ -39,7 +41,7 @@ class TimeManager: else: high = False self.clocks[k] = ClockState(high, half_period, half_period - phase) - + def tick(self): rising = set() falling = set() @@ -62,14 +64,14 @@ str2op = { "+": operator.add, "-": operator.sub, "*": operator.mul, - + ">>>": operator.rshift, "<<<": operator.lshift, - + "&": operator.and_, "^": operator.xor, "|": operator.or_, - + "<": operator.lt, "<=": operator.le, "==": operator.eq, @@ -144,8 +146,8 @@ class Evaluator: v = self.eval(node.v, postcommit) & (2**nbits - 1) return sum(v << i*nbits for i in range(node.n)) elif isinstance(node, _ArrayProxy): - return self.eval(node.choices[self.eval(node.key, postcommit)], - postcommit) + idx = min(len(node.choices) - 1, self.eval(node.key, postcommit)) + return self.eval(node.choices[idx], postcommit) elif isinstance(node, _MemoryLocation): array = self.replaced_memories[node.memory] return self.eval(array[self.eval(node.index, postcommit)], postcommit) @@ -183,7 +185,8 @@ class Evaluator: full_value |= value << node.start self.assign(node.value, full_value) elif isinstance(node, _ArrayProxy): - self.assign(node.choices[self.eval(node.key)], value) + idx = min(len(node.choices) - 1, self.eval(node.key)) + self.assign(node.choices[idx], value) elif isinstance(node, _MemoryLocation): array = self.replaced_memories[node.memory] self.assign(array[self.eval(node.index)], value) @@ -218,9 +221,24 @@ class Evaluator: raise NotImplementedError +class DummyAsyncResetSynchronizerImpl(Module): + def __init__(self, cd, async_reset): + # TODO: asynchronous set + # This naive implementation has a minimum reset pulse + # width requirement of one clock period in cd. + self.comb += cd.rst.eq(async_reset) + + +class DummyAsyncResetSynchronizer: + @staticmethod + def lower(dr): + return DummyAsyncResetSynchronizerImpl(dr.cd, dr.async_reset) + + # TODO: instances via Iverilog/VPI class Simulator: - def __init__(self, fragment_or_module, generators, clocks={"sys": 10}, vcd_name=None): + def __init__(self, fragment_or_module, generators, clocks={"sys": 10}, vcd_name=None, + special_overrides={}): if isinstance(fragment_or_module, _Fragment): self.fragment = fragment_or_module else: @@ -229,16 +247,11 @@ class Simulator: mta = MemoryToArray() mta.transform_fragment(None, self.fragment) - fs, lowered = lower_specials(overrides={}, specials=self.fragment.specials) + overrides = {AsyncResetSynchronizer: DummyAsyncResetSynchronizer} + overrides.update(special_overrides) + fs, lowered = lower_specials(overrides=overrides, specials=self.fragment.specials) self.fragment += fs self.fragment.specials -= lowered - # FIXME: Remaining replaced ports workaround - remaining_memory_ports = set() - for s in self.fragment.specials: - if isinstance(s, _MemoryPort): - remaining_memory_ports.add(s) - self.fragment.specials -= remaining_memory_ports - # FIXME: Remaining replaced ports workaround if self.fragment.specials: raise ValueError("Could not lower all specials", self.fragment.specials) diff --git a/litex/soc/integration/builder.py b/litex/soc/integration/builder.py index dac60305..2f1af0fd 100644 --- a/litex/soc/integration/builder.py +++ b/litex/soc/integration/builder.py @@ -3,7 +3,6 @@ import subprocess import struct import shutil -from litex.build.tools import write_to_file from litex.soc.integration import cpu_interface, soc_sdram, sdram_init @@ -11,10 +10,9 @@ __all__ = ["soc_software_packages", "soc_directory", "Builder", "builder_args", "builder_argdict"] -# in build order (for dependencies) soc_software_packages = [ - "libbase", "libcompiler_rt", + "libbase", "libnet", "bios" ] @@ -47,10 +45,11 @@ class Builder: self.software_packages = [] for name in soc_software_packages: - self.add_software_package( - name, os.path.join(soc_directory, "software", name)) + self.add_software_package(name) - def add_software_package(self, name, src_dir): + def add_software_package(self, name, src_dir=None): + if src_dir is None: + src_dir = os.path.join(soc_directory, "software", name) self.software_packages.append((name, src_dir)) def _generate_includes(self): @@ -67,38 +66,30 @@ class Builder: 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) - - variables_contents = [] - def define(k, v): - variables_contents.append("{}={}\n".format(k, _makefile_escape(v))) - for k, v in cpu_interface.get_cpu_mak(cpu_type): - define(k, v) - define("SOC_DIRECTORY", soc_directory) - define("BUILDINC_DIRECTORY", buildinc_dir) - for name, src_dir in self.software_packages: - define(name.upper() + "_DIRECTORY", src_dir) - write_to_file( - os.path.join(generated_dir, "variables.mak"), - "".join(variables_contents)) - - write_to_file( - os.path.join(generated_dir, "output_format.ld"), - cpu_interface.get_linker_output_format(cpu_type)) - write_to_file( - os.path.join(generated_dir, "regions.ld"), - cpu_interface.get_linker_regions(memory_regions)) - - write_to_file( - os.path.join(generated_dir, "mem.h"), - cpu_interface.get_mem_header(memory_regions, flash_boot_address)) - write_to_file( - os.path.join(generated_dir, "csr.h"), - cpu_interface.get_csr_header(csr_regions, constants)) + with open(os.path.join(generated_dir, "variables.mak"), "w") as f: + def define(k, v): + f.write("{}={}\n".format(k, _makefile_escape(v))) + for k, v in cpu_interface.get_cpu_mak(cpu_type): + define(k, v) + define("SOC_DIRECTORY", soc_directory) + define("BUILDINC_DIRECTORY", buildinc_dir) + f.write("export BUILDINC_DIRECTORY\n") + for name, src_dir in self.software_packages: + define(name.upper() + "_DIRECTORY", src_dir) + + with open(os.path.join(generated_dir, "output_format.ld"), "w") as f: + f.write(cpu_interface.get_linker_output_format(cpu_type)) + with open(os.path.join(generated_dir, "regions.ld"), "w") as f: + f.write(cpu_interface.get_linker_regions(memory_regions)) + + with open(os.path.join(generated_dir, "mem.h"), "w") as f: + f.write(cpu_interface.get_mem_header(memory_regions, flash_boot_address)) + with open(os.path.join(generated_dir, "csr.h"), "w") as f: + f.write(cpu_interface.get_csr_header(csr_regions, constants)) if sdram_phy_settings is not None: - write_to_file( - os.path.join(generated_dir, "sdram_phy.h"), - sdram_init.get_sdram_phy_header(sdram_phy_settings)) + with open(os.path.join(generated_dir, "sdram_phy.h"), "w") as f: + f.write(sdram_init.get_sdram_phy_header(sdram_phy_settings)) def _generate_csr_csv(self): memory_regions = self.soc.get_memory_regions() @@ -107,9 +98,8 @@ class Builder: csr_dir = os.path.dirname(self.csr_csv) os.makedirs(csr_dir, exist_ok=True) - write_to_file( - self.csr_csv, - cpu_interface.get_csr_csv(csr_regions, constants, memory_regions)) + with open(self.csr_csv, "w") as f: + f.write(cpu_interface.get_csr_csv(csr_regions, constants, memory_regions)) def _prepare_software(self): for name, src_dir in self.software_packages: diff --git a/litex/soc/integration/cpu_interface.py b/litex/soc/integration/cpu_interface.py index 2f25e152..650ddbb8 100644 --- a/litex/soc/integration/cpu_interface.py +++ b/litex/soc/integration/cpu_interface.py @@ -11,28 +11,18 @@ cpu_endianness = { } def get_cpu_mak(cpu): - clang = os.getenv("CLANG", "") - if clang != "": - clang = bool(int(clang)) - else: - clang = None if cpu == "lm32": - assert not clang, "lm32 not supported with clang." triple = "lm32-elf" cpuflags = "-mbarrel-shift-enabled -mmultiply-enabled -mdivide-enabled -msign-extend-enabled" + clang = "" elif cpu == "or1k": - # default to CLANG unless told otherwise - if clang is None: - clang = True - triple = "or1k-elf" - cpuflags = "-mhard-mul -mhard-div -mror" - if clang: - triple = "or1k-linux" - cpuflags += "-mffl1 -maddc" + triple = "or1k-linux" + cpuflags = "-mhard-mul -mhard-div -mror -mffl1 -maddc" + clang = "1" elif cpu == "riscv32": - assert not clang, "riscv32 not supported with clang." triple = "riscv32-unknown-elf" cpuflags = "-mno-save-restore" + clang = "0" else: raise ValueError("Unsupported CPU type: "+cpu) return [ @@ -40,7 +30,7 @@ def get_cpu_mak(cpu): ("CPU", cpu), ("CPUFLAGS", cpuflags), ("CPUENDIANNESS", cpu_endianness[cpu]), - ("CLANG", str(0 if clang is None else int(clang))) + ("CLANG", clang) ] @@ -71,7 +61,7 @@ def get_mem_header(regions, flash_boot_address): return r -def _get_rw_functions(reg_name, reg_base, nwords, busword, read_only, with_access_functions): +def _get_rw_functions_c(reg_name, reg_base, nwords, busword, read_only, with_access_functions): r = "" r += "#define CSR_"+reg_name.upper()+"_ADDR "+hex(reg_base)+"\n" @@ -124,18 +114,23 @@ def get_csr_header(regions, constants, with_access_functions=True): r += "#define CSR_"+name.upper()+"_BASE "+hex(origin)+"\n" for csr in obj: nr = (csr.size + busword - 1)//busword - r += _get_rw_functions(name + "_" + csr.name, origin, nr, busword, isinstance(csr, CSRStatus), with_access_functions) + r += _get_rw_functions_c(name + "_" + csr.name, origin, nr, busword, isinstance(csr, CSRStatus), with_access_functions) origin += 4*nr r += "\n/* constants */\n" for name, value in constants: - r += "#define " + name - if value is not None: - if isinstance(value, str): - r += " \"" + value + "\"" - else: - r += " " + str(value) - r += "\n" + if value is None: + r += "#define "+name+"\n" + continue + if isinstance(value, str): + value = "\"" + value + "\"" + ctype = "const char *" + else: + value = str(value) + ctype = "int" + r += "#define "+name+" "+value+"\n" + r += "static inline "+ctype+" "+name.lower()+"_read(void) {\n" + r += "\treturn "+value+";\n}\n" r += "\n#endif\n" return r diff --git a/litex/soc/integration/soc_core.py b/litex/soc/integration/soc_core.py index 8e3f7a22..df54355f 100644 --- a/litex/soc/integration/soc_core.py +++ b/litex/soc/integration/soc_core.py @@ -175,6 +175,14 @@ class SoCCore(Module): r += self._constants return r + def get_csr_dev_address(self, name, memory): + if memory is not None: + name = name + "_" + memory.name_override + try: + return self.csr_map[name] + except ValueError: + return None + def do_finalize(self): registered_mems = {regions[0] for regions in self._memory_regions} if self.cpu_type is not None: @@ -189,7 +197,7 @@ class SoCCore(Module): # CSR self.submodules.csrbankarray = csr_bus.CSRBankArray(self, - lambda name, memory: self.csr_map[name if memory is None else name + "_" + memory.name_override], + self.get_csr_dev_address, data_width=self.csr_data_width, address_width=self.csr_address_width) self.submodules.csrcon = csr_bus.Interconnect( self.wishbone2csr.csr, self.csrbankarray.get_buses()) diff --git a/litex/soc/integration/soc_sdram.py b/litex/soc/integration/soc_sdram.py index efc1c810..4969bd59 100644 --- a/litex/soc/integration/soc_sdram.py +++ b/litex/soc/integration/soc_sdram.py @@ -62,7 +62,7 @@ class SoCSDRAM(SoCCore): main_ram_size = 2**(geom_settings.bankbits + geom_settings.rowbits + geom_settings.colbits)*sdram_width//8 - # XXX: Limit main_ram_size to 256MB, we should modify mem_map to allow larger memories. + # TODO: modify mem_map to allow larger memories. main_ram_size = min(main_ram_size, 256*1024*1024) self.add_constant("L2_SIZE", self.l2_size) diff --git a/litex/soc/interconnect/csr.py b/litex/soc/interconnect/csr.py index c4458cde..b34802fc 100644 --- a/litex/soc/interconnect/csr.py +++ b/litex/soc/interconnect/csr.py @@ -1,3 +1,29 @@ +""" +Configuration and Status Registers +********************************** + +The lowest-level description of a register is provided by the ``CSR`` class, +which maps to the value at a single address on the target bus. Also provided +are helper classes for dealing with values larger than the CSR buses data +width. + + * ``CSRConstant``, for constant values. + * ``CSRStatus``, for providing information to the CPU. + * ``CSRStorage``, for allowing control via the CPU. + +Generating register banks +========================= +A module can provide bus-independent CSRs by implementing a ``get_csrs`` method +that returns a list of instances of the classes described above. + +Similarly, bus-independent memories can be returned as a list by a +``get_memories`` method. + +To avoid listing those manually, a module can inherit from the ``AutoCSR`` +class, which provides ``get_csrs`` and ``get_memories`` methods that scan for +CSR and memory attributes and return their list. +""" + from litex.gen import * from litex.gen.util.misc import xdir from litex.gen.fhdl.tracer import get_obj_var_name @@ -12,13 +38,69 @@ class _CSRBase(DUID): self.size = size +class CSRConstant(DUID): + """Register which contains a constant value. + + Useful for providing information on how a HDL was instantiated to firmware + running on the device. + """ + + def __init__(self, value, bits_sign=None, name=None): + DUID.__init__(self) + self.value = Constant(value, bits_sign) + self.name = get_obj_var_name(name) + if self.name is None: + raise ValueError("Cannot extract CSR name from code, need to specify.") + + def read(self): + """Read method for simulation.""" + return self.value.value + + class CSR(_CSRBase): + """Basic CSR register. + + Parameters + ---------- + size : int + Size of the CSR register in bits. + Must be less than CSR bus width! + + name : string + Provide (or override the name) of the CSR register. + + Attributes + ---------- + r : Signal(size), out + Contains the data written from the bus interface. + ``r`` is only valid when ``re`` is high. + + re : Signal(), out + The strobe signal for ``r``. + It is active for one cycle, after or during a write from the bus. + + w : Signal(size), in + The value to be read from the bus. + Must be provided at all times. + """ + def __init__(self, size=1, name=None): _CSRBase.__init__(self, size, name) self.re = Signal(name=self.name + "_re") self.r = Signal(self.size, name=self.name + "_r") self.w = Signal(self.size, name=self.name + "_w") + def read(self): + """Read method for simulation.""" + return (yield self.w) + + def write(self, value): + """Write method for simulation.""" + yield self.r.eq(value) + yield self.re.eq(1) + yield + yield self.re.eq(0) + class _CompoundCSR(_CSRBase, Module): def __init__(self, size, name): @@ -35,6 +117,39 @@ class _CompoundCSR(_CSRBase, Module): class CSRStatus(_CompoundCSR): + """Status Register. + + The ``CSRStatus`` class is meant to be used as a status register that is + read-only from the CPU. + + The user design is expected to drive its ``status`` signal. + + The advantage of using ``CSRStatus`` instead of using ``CSR`` and driving + ``w`` is that the width of ``CSRStatus`` can be arbitrary. + + Status registers larger than the bus word width are automatically broken + down into several ``CSR`` registers to span several addresses. + + *Be careful, though:* the atomicity of reads is not guaranteed. + + Parameters + ---------- + size : int + Size of the CSR register in bits. + Can be bigger than the CSR bus width. + + reset : string + Value of the register after reset. + + name : string + Provide (or override the name) of the ``CSRStatus`` register. + + Attributes + ---------- + status : Signal(size), in + The value of the CSRStatus register. + """ + def __init__(self, size=1, reset=0, name=None): _CompoundCSR.__init__(self, size, name) self.status = Signal(self.size, reset=reset) @@ -47,8 +162,65 @@ class CSRStatus(_CompoundCSR): self.comb += sc.w.eq(self.status[i*busword:i*busword+nbits]) self.simple_csrs.append(sc) + def read(self): + """Read method for simulation.""" + return (yield self.status) + class CSRStorage(_CompoundCSR): + """Control Register. + + The ``CSRStorage`` class provides a memory location that can be read and + written by the CPU, and read and optionally written by the design. + + It can span several CSR addresses. + + Parameters + ---------- + size : int + Size of the CSR register in bits. + Can be bigger than the CSR bus width. + + reset : string + Value of the register after reset. + + atomic_write : bool + Provide an mechanism for atomic CPU writes is provided. + When enabled, writes to the first CSR addresses go to a back-buffer + whose contents are atomically copied to the main buffer when the last + address is written. + + write_from_dev : bool + Allow the design to update the CSRStorage value. + *Warning*: The atomicity of reads by the CPU is not guaranteed. + + alignment_bits : int + ??? + + name : string + Provide (or override the name) of the ``CSRStatus`` register. + + Attributes + ---------- + storage_full : Signal(size), out + ??? + + storage : Signal(size), out + Signal providing the value of the ``CSRStorage`` object. + + re : Signal(), in + The strobe signal indicating a write to the ``CSRStorage`` register. + It is active for one cycle, after or during a write from the bus. + + we : Signal(), out + Only available when ``write_from_dev == True`` + ??? + + dat_w : Signal(), out + Only available when ``write_from_dev == True`` + ??? + """ + def __init__(self, size=1, reset=0, atomic_write=False, write_from_dev=False, alignment_bits=0, name=None): _CompoundCSR.__init__(self, size, name) self.alignment_bits = alignment_bits @@ -90,6 +262,17 @@ class CSRStorage(_CompoundCSR): self.sync += If(sc.re, self.storage_full[lo:hi].eq(sc.r)) self.sync += self.re.eq(sc.re) + def read(self): + """Read method for simulation.""" + return (yield self.storage) << self.alignment_bits + + def write(self, value): + """Write method for simulation.""" + yield self.storage.eq(value >> self.alignment_bits) + yield self.re.eq(1) + yield + yield self.re.eq(0) + def csrprefix(prefix, csrs, done): for csr in csrs: @@ -129,8 +312,20 @@ def _make_gatherer(method, cls, prefix_cb): class AutoCSR: + """MixIn to provide bus independent access to CSR registers. + + A module can inherit from the ``AutoCSR`` class, which provides + ``get_csrs``, ``get_memories`` and ``get_constants`` methods that scan for + CSR and memory attributes and return their list. + + If the module has child objects that implement ``get_csrs``, + ``get_memories`` or ``get_constants``, they will be called by the + ``AutoCSR`` methods and their CSR and memories added to the lists returned, + with the child objects' names as prefixes. + """ get_memories = _make_gatherer("get_memories", Memory, memprefix) get_csrs = _make_gatherer("get_csrs", _CSRBase, csrprefix) + get_constants = _make_gatherer("get_constants", CSRConstant, csrprefix) class GenericBank(Module): diff --git a/litex/soc/interconnect/csr_bus.py b/litex/soc/interconnect/csr_bus.py index 12819927..86a9dad4 100644 --- a/litex/soc/interconnect/csr_bus.py +++ b/litex/soc/interconnect/csr_bus.py @@ -1,3 +1,11 @@ +""" +CSR-2 bus +========= + +The CSR-2 bus is a low-bandwidth, resource-sensitive bus designed for accessing +the configuration and status registers of cores from software. +""" + from litex.gen import * from litex.gen.genlib.record import * from litex.gen.genlib.misc import chooser @@ -20,6 +28,24 @@ class Interface(Record): Record.__init__(self, set_layout_parameters(_layout, data_width=data_width, address_width=address_width)) + @classmethod + def like(self, other): + return Interface(len(other.dat_w), + len(other.adr)) + + def write(self, adr, dat): + yield self.adr.eq(adr) + yield self.dat_w.eq(dat) + yield self.we.eq(1) + yield + yield self.we.eq(0) + + def read(self, adr): + yield self.adr.eq(adr) + yield + yield + return (yield self.dat_r) + class Interconnect(Module): def __init__(self, master, slaves): @@ -144,6 +170,7 @@ class CSRBankArray(Module): def scan(self, ifargs, ifkwargs): self.banks = [] self.srams = [] + self.constants = [] for name, obj in xdir(self.source, True): if hasattr(obj, "get_csrs"): csrs = obj.get_csrs() @@ -165,6 +192,9 @@ class CSRBankArray(Module): self.submodules += mmap csrs += mmap.get_csrs() self.srams.append((name, memory, mapaddr, mmap)) + if hasattr(obj, "get_constants"): + for constant in obj.get_constants(): + self.constants.append((name, constant)) if csrs: mapaddr = self.address_map(name, None) if mapaddr is None: diff --git a/litex/soc/interconnect/csr_eventmanager.py b/litex/soc/interconnect/csr_eventmanager.py index 12b85a0a..07a32c33 100644 --- a/litex/soc/interconnect/csr_eventmanager.py +++ b/litex/soc/interconnect/csr_eventmanager.py @@ -1,3 +1,8 @@ +""" +The event manager provides a systematic way to generate standard interrupt +controllers. +""" + from functools import reduce from operator import or_ @@ -8,16 +13,44 @@ from litex.soc.interconnect.csr import * class _EventSource(DUID): + """Base class for EventSources. + + Attributes + ---------- + trigger : Signal(), in + Signal which interfaces with the user design. + + status : Signal(), out + Contains the current level of the trigger signal. + This value ends up in the ``status`` register. + + pending : Signal(), out + A trigger event has occurred and not yet cleared. + This value ends up in the ``pending`` register. + + clear : Signal(), in + Clear after a trigger event. + Ignored by some event sources. + """ + def __init__(self): DUID.__init__(self) - self.status = Signal() # value in the status register - self.pending = Signal() # value in the pending register + assert irq if unmasked - self.trigger = Signal() # trigger signal interface to the user design - self.clear = Signal() # clearing attempt by W1C to pending register, ignored by some event sources + self.status = Signal() + self.pending = Signal() + self.trigger = Signal() + self.clear = Signal() -# set on a positive trigger pulse class EventSourcePulse(Module, _EventSource): + """EventSource which triggers on a pulse. + + The event stays asserted after the ``trigger`` signal goes low, and until + software acknowledges it. + + An example use is to pulse ``trigger`` high for 1 cycle after the reception + of a character in a UART. + """ + def __init__(self): _EventSource.__init__(self) self.comb += self.status.eq(0) @@ -27,8 +60,12 @@ class EventSourcePulse(Module, _EventSource): ] -# set on the falling edge of the trigger, status = trigger class EventSourceProcess(Module, _EventSource): + """EventSource which triggers on a falling edge. + + The purpose of this event source is to monitor the status of processes and + generate an interrupt on their completion. + """ def __init__(self): _EventSource.__init__(self) self.comb += self.status.eq(self.trigger) @@ -40,8 +77,13 @@ class EventSourceProcess(Module, _EventSource): ] -# all status set by external trigger class EventSourceLevel(Module, _EventSource): + """EventSource which trigger contains the instantaneous state of the event. + + It must be set and released by the user design. For example, a DMA + controller with several slots can use this event source to signal that one + or more slots require CPU attention. + """ def __init__(self): _EventSource.__init__(self) self.comb += [ @@ -51,6 +93,31 @@ class EventSourceLevel(Module, _EventSource): class EventManager(Module, AutoCSR): + """Provide an IRQ and CSR registers for a set of event sources. + + Each event source is assigned one bit in each of those registers. + + Attributes + ---------- + irq : Signal(), out + A signal which is driven high whenever there is a pending and unmasked + event. + It is typically connected to an interrupt line of a CPU. + + status : CSR(n), read-only + Contains the current level of the trigger line of + ``EventSourceProcess`` and ``EventSourceLevel`` sources. + It is always 0 for ``EventSourcePulse`` + + pending : CSR(n), read-write + Contains the currently asserted events. Writing 1 to the bit assigned + to an event clears it. + + enable : CSR(n), read-write + Defines which asserted events will cause the ``irq`` line to be + asserted. + """ + def __init__(self): self.irq = Signal() @@ -81,6 +148,8 @@ class EventManager(Module, AutoCSR): class SharedIRQ(Module): + """Allow an IRQ signal to be shared between multiple EventManager objects.""" + def __init__(self, *event_managers): self.irq = Signal() self.comb += self.irq.eq(reduce(or_, [ev.irq for ev in event_managers])) diff --git a/litex/soc/interconnect/wishbone.py b/litex/soc/interconnect/wishbone.py index 73ba6e0b..2f2798c7 100644 --- a/litex/soc/interconnect/wishbone.py +++ b/litex/soc/interconnect/wishbone.py @@ -9,14 +9,7 @@ from litex.gen.genlib.fsm import FSM, NextState from litex.soc.interconnect import csr -# TODO: rewrite without FlipFlop and Counter -@ResetInserter() -@CEInserter() -class FlipFlop(Module): - def __init__(self, *args, **kwargs): - self.d = Signal(*args, **kwargs) - self.q = Signal(*args, **kwargs) - self.sync += self.q.eq(self.d) +# TODO: rewrite without FlipFlop _layout = [ @@ -40,6 +33,10 @@ class Interface(Record): data_width=data_width, sel_width=data_width//8)) + @staticmethod + def like(other): + return Interface(len(other.dat_w)) + def _do_transaction(self): yield self.cyc.eq(1) yield self.stb.eq(1) @@ -465,7 +462,7 @@ class Cache(Module): self.master = master self.slave = slave - ### + # # # dw_from = len(master.dat_r) dw_to = len(slave.dat_r) diff --git a/litex/soc/software/bios/boot.c b/litex/soc/software/bios/boot.c index f7c0150b..e0ff8ba9 100644 --- a/litex/soc/software/bios/boot.c +++ b/litex/soc/software/bios/boot.c @@ -27,6 +27,12 @@ static void __attribute__((noreturn)) boot(unsigned int r1, unsigned int r2, uns while(1); } +enum { + ACK_TIMEOUT, + ACK_CANCELLED, + ACK_OK +}; + static int check_ack(void) { int recognized; @@ -42,10 +48,12 @@ static int check_ack(void) if(uart_read_nonblock()) { char c; c = uart_read(); + if((c == 'Q') || (c == '\e')) + return ACK_CANCELLED; if(c == str[recognized]) { recognized++; if(recognized == SFL_MAGIC_LEN) - return 1; + return ACK_OK; } else { if(c == str[0]) recognized = 1; @@ -55,30 +63,39 @@ static int check_ack(void) } timer0_update_value_write(1); } - return 0; + return ACK_TIMEOUT; } #define MAX_FAILED 5 -void serialboot(void) +/* Returns 1 if other boot methods should be tried */ +int serialboot(void) { struct sfl_frame frame; int failed; unsigned int cmdline_adr, initrdstart_adr, initrdend_adr; static const char str[SFL_MAGIC_LEN+1] = SFL_MAGIC_REQ; const char *c; + int ack_status; printf("Booting from serial...\n"); + printf("Press Q or ESC to abort boot completely.\n"); c = str; while(*c) { uart_write(*c); c++; } - if(!check_ack()) { + ack_status = check_ack(); + if(ack_status == ACK_TIMEOUT) { printf("Timeout\n"); - return; + return 1; } + if(ack_status == ACK_CANCELLED) { + printf("Cancelled\n"); + return 0; + } + /* assume ACK_OK */ failed = 0; cmdline_adr = initrdstart_adr = initrdend_adr = 0; @@ -102,7 +119,7 @@ void serialboot(void) failed++; if(failed == MAX_FAILED) { printf("Too many consecutive errors, aborting"); - return; + return 1; } uart_write(SFL_ACK_CRCERROR); continue; @@ -113,7 +130,7 @@ void serialboot(void) case SFL_CMD_ABORT: failed = 0; uart_write(SFL_ACK_SUCCESS); - return; + return 1; case SFL_CMD_LOAD: { char *writepointer; @@ -168,12 +185,13 @@ void serialboot(void) failed++; if(failed == MAX_FAILED) { printf("Too many consecutive errors, aborting"); - return; + return 1; } uart_write(SFL_ACK_UNKNOWN); break; } } + return 1; } #ifdef CSR_ETHMAC_BASE diff --git a/litex/soc/software/bios/boot.h b/litex/soc/software/bios/boot.h index aa9cd88a..65dcf457 100644 --- a/litex/soc/software/bios/boot.h +++ b/litex/soc/software/bios/boot.h @@ -1,7 +1,7 @@ #ifndef __BOOT_H #define __BOOT_H -void serialboot(void); +int serialboot(void); void netboot(void); void flashboot(void); void romboot(void); diff --git a/litex/soc/software/bios/main.c b/litex/soc/software/bios/main.c index 8b93392b..0bdffdf1 100644 --- a/litex/soc/software/bios/main.c +++ b/litex/soc/software/bios/main.c @@ -458,69 +458,9 @@ static void readstr(char *s, int size) } } -static int test_user_abort(void) -{ - char c; - - printf("Automatic boot in 2 seconds...\n"); - printf("Q/ESC: abort boot\n"); -#ifdef FLASH_BOOT_ADDRESS - printf("F: boot from flash\n"); -#endif - printf("S: boot from serial\n"); -#ifdef CSR_ETHMAC_BASE - printf("N: boot from network\n"); -#endif -#ifdef ROM_BOOT_ADDRESS - printf("R: boot from embedded ROM\n"); -#endif - timer0_en_write(0); - timer0_reload_write(0); -#ifndef TEST_USER_ABORT_DELAY - timer0_load_write(SYSTEM_CLOCK_FREQUENCY*2); -#else - timer0_load_write(TEST_USER_ABORT_DELAY); -#endif - timer0_en_write(1); - timer0_update_value_write(1); - while(timer0_value_read()) { - if(readchar_nonblock()) { - c = readchar(); - if((c == 'Q')||(c == 'q')||(c == '\e')) { - puts("Aborted"); - return 0; - } -#ifdef FLASH_BOOT_ADDRESS - if((c == 'F')||(c == 'f')) { - flashboot(); - return 0; - } -#endif - if((c == 'S')||(c == 's')) { - serialboot(); - return 0; - } -#ifdef CSR_ETHMAC_BASE - if((c == 'N')||(c == 'n')) { - netboot(); - return 0; - } -#endif -#ifdef ROM_BOOT_ADDRESS - if((c == 'R')||(c == 'r')) { - romboot(); - return 0; - } -#endif - } - timer0_update_value_write(1); - } - return 1; -} - static void boot_sequence(void) { - if(test_user_abort()) { + if(serialboot()) { #ifdef FLASH_BOOT_ADDRESS flashboot(); #endif @@ -557,10 +497,9 @@ int main(int i, char **c) printf("(unknown)\n"); #endif puts( - "(c) Copyright 2012-2016 Enjoy-Digital\n" - "(c) Copyright 2007-2016 M-Labs Limited\n" + "(c) Copyright 2012-2017 Enjoy-Digital\n" + "(c) Copyright 2007-2017 M-Labs Limited\n" "Built "__DATE__" "__TIME__"\n"); - crcbios(); #ifdef CSR_ETHMAC_BASE eth_init(); diff --git a/litex/soc/software/include/base/inttypes.h b/litex/soc/software/include/base/inttypes.h index 9e4534c2..4afd04b6 100644 --- a/litex/soc/software/include/base/inttypes.h +++ b/litex/soc/software/include/base/inttypes.h @@ -22,6 +22,8 @@ #ifndef __INTTYPES_H #define __INTTYPES_H +#include + # if __WORDSIZE == 64 # define __PRI64_PREFIX "l" # define __PRIPTR_PREFIX "l" diff --git a/litex/soc/software/include/base/math.h b/litex/soc/software/include/base/math.h new file mode 100644 index 00000000..f13bf6c6 --- /dev/null +++ b/litex/soc/software/include/base/math.h @@ -0,0 +1,14 @@ +#ifndef __MATH_H +#define __MATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../fdlibm/fdlibm.h" + +#ifdef __cplusplus +} +#endif + +#endif /* __MATH_H */ diff --git a/litex/soc/software/include/base/stddef.h b/litex/soc/software/include/base/stddef.h index d1afa95e..4f9a211f 100644 --- a/litex/soc/software/include/base/stddef.h +++ b/litex/soc/software/include/base/stddef.h @@ -14,7 +14,7 @@ extern "C" { typedef unsigned long size_t; typedef long ptrdiff_t; -#define offsetof(s,m) (size_t)&(((s *)0)->m) +#define offsetof(type, member) __builtin_offsetof(type, member) #ifdef __cplusplus } diff --git a/litex/soc/software/include/base/stdlib.h b/litex/soc/software/include/base/stdlib.h index 4b9bd049..448b2f67 100644 --- a/litex/soc/software/include/base/stdlib.h +++ b/litex/soc/software/include/base/stdlib.h @@ -74,6 +74,7 @@ void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, con char *getenv(const char *name); void *malloc(size_t size); +void *calloc(size_t nmemb, size_t size); void free(void *ptr); void *realloc(void *ptr, size_t size); diff --git a/litex/soc/software/include/dyld/dyld.h b/litex/soc/software/include/dyld/dyld.h index 6bd55be7..5d3e6f34 100644 --- a/litex/soc/software/include/dyld/dyld.h +++ b/litex/soc/software/include/dyld/dyld.h @@ -5,7 +5,6 @@ struct dyld_info { Elf32_Addr base; - const void *init; const char *strtab; const Elf32_Sym *symtab; struct { @@ -21,7 +20,7 @@ extern "C" { #endif int dyld_load(const void *shlib, Elf32_Addr base, - Elf32_Addr (*resolve_import)(const char *), + Elf32_Addr (*resolve)(void *, const char *), void *resolve_data, struct dyld_info *info, const char **error_out); void *dyld_lookup(const char *symbol, struct dyld_info *info); diff --git a/litex/soc/software/include/fdlibm/fdlibm.h b/litex/soc/software/include/fdlibm/fdlibm.h new file mode 100644 index 00000000..60a8df6c --- /dev/null +++ b/litex/soc/software/include/fdlibm/fdlibm.h @@ -0,0 +1,216 @@ + +/* @(#)fdlibm.h 1.5 04/04/22 */ +/* + * ==================================================== + * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* Sometimes it's necessary to define __LITTLE_ENDIAN explicitly + but these catch some common cases. */ + +#if defined(i386) || defined(i486) || \ + defined(intel) || defined(x86) || defined(i86pc) || \ + defined(__alpha) || defined(__osf__) +#define __LITTLE_ENDIAN +#endif + +#ifdef __LITTLE_ENDIAN +#define __HI(x) *(1+(int*)&x) +#define __LO(x) *(int*)&x +#define __HIp(x) *(1+(int*)x) +#define __LOp(x) *(int*)x +#else +#define __HI(x) *(int*)&x +#define __LO(x) *(1+(int*)&x) +#define __HIp(x) *(int*)x +#define __LOp(x) *(1+(int*)x) +#endif + +#ifdef __STDC__ +#define __P(p) p +#else +#define __P(p) () +#endif + +/* + * ANSI/POSIX + */ + +extern int signgam; + +#define MAXFLOAT ((float)3.40282346638528860e+38) + +enum fdversion {fdlibm_ieee = -1, fdlibm_svid, fdlibm_xopen, fdlibm_posix}; + +#define _LIB_VERSION_TYPE enum fdversion +#define _LIB_VERSION _fdlib_version + +/* if global variable _LIB_VERSION is not desirable, one may + * change the following to be a constant by: + * #define _LIB_VERSION_TYPE const enum version + * In that case, after one initializes the value _LIB_VERSION (see + * s_lib_version.c) during compile time, it cannot be modified + * in the middle of a program + */ +extern _LIB_VERSION_TYPE _LIB_VERSION; + +#define _IEEE_ fdlibm_ieee +#define _SVID_ fdlibm_svid +#define _XOPEN_ fdlibm_xopen +#define _POSIX_ fdlibm_posix + +struct exception { + int type; + char *name; + double arg1; + double arg2; + double retval; +}; + +#define HUGE MAXFLOAT + +/* + * set X_TLOSS = pi*2**52, which is possibly defined in + * (one may replace the following line by "#include ") + */ + +#define X_TLOSS 1.41484755040568800000e+16 + +#define DOMAIN 1 +#define SING 2 +#define OVERFLOW 3 +#define UNDERFLOW 4 +#define TLOSS 5 +#define PLOSS 6 + +/* + * ANSI/POSIX + */ +extern double acos __P((double)); +extern double asin __P((double)); +extern double atan __P((double)); +extern double atan2 __P((double, double)); +extern double cos __P((double)); +extern double sin __P((double)); +extern double tan __P((double)); + +extern double cosh __P((double)); +extern double sinh __P((double)); +extern double tanh __P((double)); + +extern double exp __P((double)); +extern double frexp __P((double, int *)); +extern double ldexp __P((double, int)); +extern double log __P((double)); +extern double log10 __P((double)); +extern double modf __P((double, double *)); + +extern double pow __P((double, double)); +extern double sqrt __P((double)); + +extern double ceil __P((double)); +extern double fabs __P((double)); +extern double floor __P((double)); +extern double fmod __P((double, double)); + +extern double erf __P((double)); +extern double erfc __P((double)); +extern double gamma __P((double)); +extern double hypot __P((double, double)); +extern int isnan __P((double)); +extern int finite __P((double)); +extern double j0 __P((double)); +extern double j1 __P((double)); +extern double jn __P((int, double)); +extern double lgamma __P((double)); +extern double y0 __P((double)); +extern double y1 __P((double)); +extern double yn __P((int, double)); + +extern double acosh __P((double)); +extern double asinh __P((double)); +extern double atanh __P((double)); +extern double cbrt __P((double)); +extern double logb __P((double)); +extern double nextafter __P((double, double)); +extern double remainder __P((double, double)); +#ifdef _SCALB_INT +extern double scalb __P((double, int)); +#else +extern double scalb __P((double, double)); +#endif + +extern int matherr __P((struct exception *)); + +/* + * IEEE Test Vector + */ +extern double significand __P((double)); + +/* + * Functions callable from C, intended to support IEEE arithmetic. + */ +extern double copysign __P((double, double)); +extern int ilogb __P((double)); +extern double rint __P((double)); +extern double scalbn __P((double, int)); + +/* + * BSD math library entry points + */ +extern double expm1 __P((double)); +extern double log1p __P((double)); + +/* + * Reentrant version of gamma & lgamma; passes signgam back by reference + * as the second argument; user must allocate space for signgam. + */ +#ifdef _REENTRANT +extern double gamma_r __P((double, int *)); +extern double lgamma_r __P((double, int *)); +#endif /* _REENTRANT */ + +/* ieee style elementary functions */ +extern double __ieee754_sqrt __P((double)); +extern double __ieee754_acos __P((double)); +extern double __ieee754_acosh __P((double)); +extern double __ieee754_log __P((double)); +extern double __ieee754_atanh __P((double)); +extern double __ieee754_asin __P((double)); +extern double __ieee754_atan2 __P((double,double)); +extern double __ieee754_exp __P((double)); +extern double __ieee754_cosh __P((double)); +extern double __ieee754_fmod __P((double,double)); +extern double __ieee754_pow __P((double,double)); +extern double __ieee754_lgamma_r __P((double,int *)); +extern double __ieee754_gamma_r __P((double,int *)); +extern double __ieee754_lgamma __P((double)); +extern double __ieee754_gamma __P((double)); +extern double __ieee754_log10 __P((double)); +extern double __ieee754_sinh __P((double)); +extern double __ieee754_hypot __P((double,double)); +extern double __ieee754_j0 __P((double)); +extern double __ieee754_j1 __P((double)); +extern double __ieee754_y0 __P((double)); +extern double __ieee754_y1 __P((double)); +extern double __ieee754_jn __P((int,double)); +extern double __ieee754_yn __P((int,double)); +extern double __ieee754_remainder __P((double,double)); +extern int __ieee754_rem_pio2 __P((double,double*)); +#ifdef _SCALB_INT +extern double __ieee754_scalb __P((double,int)); +#else +extern double __ieee754_scalb __P((double,double)); +#endif + +/* fdlibm kernel function */ +extern double __kernel_standard __P((double,double,int)); +extern double __kernel_sin __P((double,double,int)); +extern double __kernel_cos __P((double,double)); +extern double __kernel_tan __P((double,double,int)); +extern int __kernel_rem_pio2 __P((double*,double*,int,int,int,const int*)); diff --git a/litex/soc/software/libbase/crt0-or1k.S b/litex/soc/software/libbase/crt0-or1k.S index 33807f87..5db4142a 100644 --- a/litex/soc/software/libbase/crt0-or1k.S +++ b/litex/soc/software/libbase/crt0-or1k.S @@ -202,6 +202,8 @@ _exception_handler: l.mfspr r5, r0, SPR_EPCR_BASE /* Extract exception effective address */ l.mfspr r6, r0, SPR_EEAR_BASE + /* Extract exception SR */ + l.mfspr r7, r0, SPR_ESR_BASE /* Call exception handler with the link address as argument */ l.jal exception_handler l.nop diff --git a/litex/soc/software/libbase/exception.c b/litex/soc/software/libbase/exception.c index 542001ad..b758e58e 100644 --- a/litex/soc/software/libbase/exception.c +++ b/litex/soc/software/libbase/exception.c @@ -1,19 +1,211 @@ +#include +#include +#include + void isr(void); #ifdef __or1k__ +#include + #define EXTERNAL_IRQ 0x8 +static void emerg_printf(const char *fmt, ...) +{ + char buf[512]; + va_list args; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + char *p = buf; + while(*p) { + while(uart_txfull_read()); + uart_rxtx_write(*p++); + } +} + +static char emerg_getc() +{ + while(uart_rxempty_read()); + char c = uart_rxtx_read(); + uart_ev_pending_write(UART_EV_RX); + return c; +} + +static const char hex[] = "0123456789abcdef"; + +static void gdb_send(const char *txbuf) +{ + unsigned char cksum = 0; + const char *p = txbuf; + while(*p) cksum += *p++; + emerg_printf("+$%s#%c%c", txbuf, hex[cksum >> 4], hex[cksum & 0xf]); +} + +static void gdb_recv(char *rxbuf, size_t size) +{ + size_t pos = (size_t)-1; + for(;;) { + char c = emerg_getc(); + if(c == '$') + pos = 0; + else if(c == '#') + return; + else if(pos < size - 1) { + rxbuf[pos++] = c; + rxbuf[pos] = 0; + } + } +} + +static void gdb_stub(unsigned long pc, unsigned long sr, + unsigned long r1, unsigned long *regs) +{ + gdb_send("S05"); + + char buf[385]; + for(;;) { + gdb_recv(buf, sizeof(buf)); + + switch(buf[0]) { + case '?': { + snprintf(buf, sizeof(buf), "S05"); + break; + } + + case 'g': { + snprintf(buf, sizeof(buf), + "%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x" + "%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x" + "%08x%08x%08x", + 0, r1, regs[2], regs[3], regs[4], regs[5], regs[6], regs[7], + regs[8], regs[9], regs[10], regs[11], regs[12], regs[13], regs[14], regs[15], + regs[16], regs[17], regs[18], regs[19], regs[20], regs[21], regs[22], regs[23], + regs[24], regs[25], regs[26], regs[27], regs[28], regs[29], regs[30], regs[31], + pc-4, pc, sr); + break; + } + + case 'm': { + unsigned long addr, len; + char *endptr = &buf[0]; + addr = strtoul(endptr + 1, &endptr, 16); + len = strtoul(endptr + 1, &endptr, 16); + unsigned char *ptr = (unsigned char *)addr; + if(len > sizeof(buf) / 2) len = sizeof(buf) / 2; + for(size_t i = 0; i < len; i++) { + buf[i*2 ] = hex[ptr[i] >> 4]; + buf[i*2+1] = hex[ptr[i] & 15]; + buf[i*2+2] = 0; + } + break; + } + + case 'p': { + unsigned long reg, value; + char *endptr = &buf[0]; + reg = strtoul(endptr + 1, &endptr, 16); + if(reg == 0) + value = 0; + else if(reg == 1) + value = r1; + else if(reg >= 2 && reg <= 31) + value = regs[reg]; + else if(reg == 33) + value = pc; + else if(reg == 34) + value = sr; + else { + snprintf(buf, sizeof(buf), "E01"); + break; + } + snprintf(buf, sizeof(buf), "%08x", value); + break; + } + + case 'P': { + unsigned long reg, value; + char *endptr = &buf[0]; + reg = strtoul(endptr + 1, &endptr, 16); + value = strtoul(endptr + 1, &endptr, 16); + if(reg == 0) + /* ignore */; + else if(reg == 1) + r1 = value; + else if(reg >= 2 && reg <= 31) + regs[reg] = value; + else if(reg == 33) + pc = value; + else if(reg == 34) + sr = value; + else { + snprintf(buf, sizeof(buf), "E01"); + break; + } + snprintf(buf, sizeof(buf), "OK"); + break; + } + + case 'c': { + if(buf[1] != '\0') { + snprintf(buf, sizeof(buf), "E01"); + break; + } + return; + } + + default: + snprintf(buf, sizeof(buf), ""); + break; + } + + do { + gdb_send(buf); + } while(emerg_getc() == '-'); + } +} + void exception_handler(unsigned long vect, unsigned long *regs, - unsigned long pc, unsigned long ea); + unsigned long pc, unsigned long ea, unsigned long sr); void exception_handler(unsigned long vect, unsigned long *regs, - unsigned long pc, unsigned long ea) + unsigned long pc, unsigned long ea, unsigned long sr) { if(vect == EXTERNAL_IRQ) { isr(); } else { - /* Unhandled exception */ - for(;;); + emerg_printf("\n *** Unhandled exception %d *** \n", vect); + emerg_printf(" pc %08x sr %08x ea %08x\n", + pc, sr, ea); + unsigned long r1 = (unsigned long)regs + 4*32; + regs -= 2; + emerg_printf(" r0 %08x r1 %08x r2 %08x r3 %08x\n", + 0, r1, regs[2], regs[3]); + emerg_printf(" r4 %08x r5 %08x r6 %08x r7 %08x\n", + regs[4], regs[5], regs[6], regs[7]); + emerg_printf(" r8 %08x r9 %08x r10 %08x r11 %08x\n", + regs[8], regs[9], regs[10], regs[11]); + emerg_printf(" r12 %08x r13 %08x r14 %08x r15 %08x\n", + regs[12], regs[13], regs[14], regs[15]); + emerg_printf(" r16 %08x r17 %08x r18 %08x r19 %08x\n", + regs[16], regs[17], regs[18], regs[19]); + emerg_printf(" r20 %08x r21 %08x r22 %08x r23 %08x\n", + regs[20], regs[21], regs[22], regs[23]); + emerg_printf(" r24 %08x r25 %08x r26 %08x r27 %08x\n", + regs[24], regs[25], regs[26], regs[27]); + emerg_printf(" r28 %08x r29 %08x r30 %08x r31 %08x\n", + regs[28], regs[29], regs[30], regs[31]); + emerg_printf(" stack:\n"); + unsigned long *sp = (unsigned long *)r1; + for(unsigned long spoff = 0; spoff < 16; spoff += 4) { + emerg_printf(" %08x:", &sp[spoff]); + for(unsigned long spoff2 = 0; spoff2 < 4; spoff2++) { + emerg_printf(" %08x", sp[spoff + spoff2]); + } + emerg_printf("\n"); + } + emerg_printf(" waiting for gdb... "); + gdb_stub(pc, sr, r1, regs); } } #endif diff --git a/litex/soc/software/libbase/vsnprintf.c b/litex/soc/software/libbase/vsnprintf.c index 2192974d..e056d328 100644 --- a/litex/soc/software/libbase/vsnprintf.c +++ b/litex/soc/software/libbase/vsnprintf.c @@ -21,6 +21,7 @@ #include #include #include +#include /** * vsnprintf - Format a string and place it in a buffer @@ -109,7 +110,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) /* get the precision */ precision = -1; if (*fmt == '.') { - ++fmt; + ++fmt; if (isdigit(*fmt)) precision = skip_atoi(&fmt); else if (*fmt == '*') { @@ -195,51 +196,40 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args) #ifndef NO_FLOAT case 'g': case 'f': { - int m; - double f; - int integer; - + double f, g; + f = va_arg(args, double); if(f < 0.0) { - *str = '-'; + if(str < end) + *str = '-'; str++; f = -f; } - integer = f; - if(integer > 0) { - m = 1; - while(integer > (m*10)) m *= 10; - while((m >= 1) && (str < end)) { - int n; - n = integer/m; - *str = '0' + n; - str++; - f = f - m*n; - integer = integer - m*n; - m /= 10; - } - } else if(str < end) { - *str = '0'; + g = pow(10.0, floor(log10(f))); + if(g < 1.0) { + if(str < end) + *str = '0'; str++; } - - if(str < end) { - *str = '.'; + while(g >= 1.0) { + if(str < end) + *str = '0' + fmod(f/g, 10.0); str++; + g /= 10.0; } - for(i=0;i<6;i++) { - int n; + if(str < end) + *str = '.'; + str++; - f = f*10.0; - n = f; - f = f - n; - if(str >= end) break; - *str = '0' + n; + for(i=0;i<6;i++) { + f = fmod(f*10.0, 10.0); + if(str < end) + *str = '0' + f; str++; } - + continue; } #endif -- 2.30.2