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)
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
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"
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):
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):
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)
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
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 *
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))
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):
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:
xilinx_special_overrides = {
+ MultiReg: XilinxMultiReg,
AsyncResetSynchronizer: XilinxAsyncResetSynchronizer,
DifferentialInput: XilinxDifferentialInput,
DifferentialOutput: XilinxDifferentialOutput,
}
-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",
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
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:
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)
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 = []
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))
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"
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))
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 *
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
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.
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
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):
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):
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):
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
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):
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)))
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
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,
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"
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" \
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"
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)
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
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
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:
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":
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):
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 += ", "
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)
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:
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:
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)
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,
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,
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):
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
+"""
+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
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
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):
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))
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):
]
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]))
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))
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()
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:
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
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)
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
else:
high = False
self.clocks[k] = ClockState(high, half_period, half_period - phase)
-
+
def tick(self):
rising = set()
falling = set()
"+": operator.add,
"-": operator.sub,
"*": operator.mul,
-
+
">>>": operator.rshift,
"<<<": operator.lshift,
-
+
"&": operator.and_,
"^": operator.xor,
"|": operator.or_,
-
+
"<": operator.lt,
"<=": operator.le,
"==": operator.eq,
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)
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)
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:
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)
import struct
import shutil
-from litex.build.tools import write_to_file
from litex.soc.integration import cpu_interface, soc_sdram, sdram_init
"Builder", "builder_args", "builder_argdict"]
-# in build order (for dependencies)
soc_software_packages = [
- "libbase",
"libcompiler_rt",
+ "libbase",
"libnet",
"bios"
]
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):
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()
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:
}
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 [
("CPU", cpu),
("CPUFLAGS", cpuflags),
("CPUENDIANNESS", cpu_endianness[cpu]),
- ("CLANG", str(0 if clang is None else int(clang)))
+ ("CLANG", clang)
]
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"
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
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:
# 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())
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)
+"""
+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
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):
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)
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
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:
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):
+"""
+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
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):
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()
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:
+"""
+The event manager provides a systematic way to generate standard interrupt
+controllers.
+"""
+
from functools import reduce
from operator import or_
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)
]
-# 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)
]
-# 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 += [
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()
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]))
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 = [
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)
self.master = master
self.slave = slave
- ###
+ # # #
dw_from = len(master.dat_r)
dw_to = len(slave.dat_r)
while(1);
}
+enum {
+ ACK_TIMEOUT,
+ ACK_CANCELLED,
+ ACK_OK
+};
+
static int check_ack(void)
{
int recognized;
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;
}
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;
failed++;
if(failed == MAX_FAILED) {
printf("Too many consecutive errors, aborting");
- return;
+ return 1;
}
uart_write(SFL_ACK_CRCERROR);
continue;
case SFL_CMD_ABORT:
failed = 0;
uart_write(SFL_ACK_SUCCESS);
- return;
+ return 1;
case SFL_CMD_LOAD: {
char *writepointer;
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
#ifndef __BOOT_H
#define __BOOT_H
-void serialboot(void);
+int serialboot(void);
void netboot(void);
void flashboot(void);
void romboot(void);
}
}
-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
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();
#ifndef __INTTYPES_H
#define __INTTYPES_H
+#include <stdint.h>
+
# if __WORDSIZE == 64
# define __PRI64_PREFIX "l"
# define __PRIPTR_PREFIX "l"
--- /dev/null
+#ifndef __MATH_H
+#define __MATH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "../fdlibm/fdlibm.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MATH_H */
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
}
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);
struct dyld_info {
Elf32_Addr base;
- const void *init;
const char *strtab;
const Elf32_Sym *symtab;
struct {
#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);
--- /dev/null
+
+/* @(#)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 <values.h>
+ * (one may replace the following line by "#include <values.h>")
+ */
+
+#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*));
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
+#include <generated/csr.h>
+#include <stdio.h>
+#include <stdarg.h>
+
void isr(void);
#ifdef __or1k__
+#include <hw/flags.h>
+
#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
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
+#include <math.h>
/**
* vsnprintf - Format a string and place it in a buffer
/* get the precision */
precision = -1;
if (*fmt == '.') {
- ++fmt;
+ ++fmt;
if (isdigit(*fmt))
precision = skip_atoi(&fmt);
else if (*fmt == '*') {
#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