merge most of misoc 54e1ef82 and migen e93d0601 changes

This commit is contained in:
Florent Kermarrec 2017-01-13 01:33:48 +01:00
parent 2507eff890
commit ff31959aea
41 changed files with 1269 additions and 404 deletions

View file

@ -131,12 +131,21 @@ class MiniSoC(BaseSoC):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
BaseSoC.__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.platform.request("eth"), clk_freq=self.clk_freq)
self.submodules.ethmac = LiteEthMAC(phy=self.ethphy, dw=32, interface="wishbone") 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_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.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): def soc_kc705_args(parser):
soc_sdram_args(parser) soc_sdram_args(parser)

View file

@ -4,7 +4,6 @@ import os
from litex.gen import * from litex.gen import *
from litex.gen.genlib.resetsync import AsyncResetSynchronizer from litex.gen.genlib.resetsync import AsyncResetSynchronizer
from litex.gen.fhdl.specials import Keep
from litex.boards.platforms import nexys_video from litex.boards.platforms import nexys_video

View file

@ -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_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_asm --read_settings_files=off --write_settings_files=off {build_name} -c {build_name}
quartus_sta {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 """.format(build_name=build_name) # noqa
build_script_file = "build_" + build_name + ".sh" build_script_file = "build_" + build_name + ".sh"
@ -115,7 +116,7 @@ class AlteraQuartusToolchain:
def build(self, platform, fragment, build_dir="build", build_name="top", def build(self, platform, fragment, build_dir="build", build_name="top",
toolchain_path="/opt/Altera", run=True, **kwargs): toolchain_path="/opt/Altera", run=True, **kwargs):
cwd = os.getcwd() cwd = os.getcwd()
tools.mkdir_noerror(build_dir) os.makedirs(build_dir, exist_ok=True)
os.chdir(build_dir) os.chdir(build_dir)
if not isinstance(fragment, _Fragment): if not isinstance(fragment, _Fragment):

View file

@ -1,13 +1,9 @@
import os import os
import struct import struct
from distutils.version import StrictVersion from distutils.version import StrictVersion
import re
import subprocess
def mkdir_noerror(d): import sys
try:
os.mkdir(d)
except OSError:
pass
def language_by_filename(name): def language_by_filename(name):
@ -23,9 +19,6 @@ def write_to_file(filename, contents, force_unix=False):
newline = None newline = None
if force_unix: if force_unix:
newline = "\n" newline = "\n"
if os.path.exists(filename):
if open(filename, "r", newline=newline).read() == contents:
return
with open(filename, "w", newline=newline) as f: with open(filename, "w", newline=newline) as f:
f.write(contents) f.write(contents)
@ -43,3 +36,25 @@ def versions(path):
yield StrictVersion(n) yield StrictVersion(n)
except ValueError: except ValueError:
continue 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

View file

@ -1,11 +1,15 @@
import os import os
import sys 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.structure import *
from litex.gen.fhdl.specials import Instance from litex.gen.fhdl.specials import Instance
from litex.gen.fhdl.module import Module from litex.gen.fhdl.module import Module
from litex.gen.fhdl.specials import SynthesisDirective
from litex.gen.genlib.cdc import * from litex.gen.genlib.cdc import *
from litex.gen.genlib.resetsync import AsyncResetSynchronizer from litex.gen.genlib.resetsync import AsyncResetSynchronizer
from litex.gen.genlib.io import * from litex.gen.genlib.io import *
@ -13,6 +17,20 @@ from litex.gen.genlib.io import *
from litex.build import tools 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): def settings(path, ver=None, sub=None):
if ver is None: if ver is None:
vers = list(tools.versions(path)) vers = list(tools.versions(path))
@ -42,52 +60,18 @@ def settings(path, ver=None, sub=None):
raise OSError("no Xilinx tools settings file found") raise OSError("no Xilinx tools settings file found")
class XilinxNoRetimingVivadoImpl(Module): class XilinxMultiRegImpl(MultiRegImpl):
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):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
MultiRegImpl.__init__(self, *args, **kwargs) MultiRegImpl.__init__(self, *args, **kwargs)
for reg in self.regs: for r in self.regs:
reg.attribute += " SHIFT_EXTRACT=\"NO\", ASYNC_REG=\"TRUE\"," r.attr.add("async_reg")
r.attr.add("no_shreg_extract")
class XilinxMultiRegVivado: class XilinxMultiReg:
@staticmethod @staticmethod
def lower(dr): def lower(dr):
return XilinxMultiRegVivadoImpl(dr.i, dr.o, dr.odomain, dr.n) return XilinxMultiRegImpl(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)
class XilinxAsyncResetSynchronizerImpl(Module): class XilinxAsyncResetSynchronizerImpl(Module):
@ -99,6 +83,8 @@ class XilinxAsyncResetSynchronizerImpl(Module):
Instance("FDPE", p_INIT=1, i_D=rst1, i_PRE=async_reset, Instance("FDPE", p_INIT=1, i_D=rst1, i_PRE=async_reset,
i_CE=1, i_C=cd.clk, o_Q=cd.rst) i_CE=1, i_C=cd.clk, o_Q=cd.rst)
] ]
rst1.attr.add("async_reg")
cd.rst.attr.add("async_reg")
class XilinxAsyncResetSynchronizer: class XilinxAsyncResetSynchronizer:
@ -145,6 +131,7 @@ class XilinxDDROutput:
xilinx_special_overrides = { xilinx_special_overrides = {
MultiReg: XilinxMultiReg,
AsyncResetSynchronizer: XilinxAsyncResetSynchronizer, AsyncResetSynchronizer: XilinxAsyncResetSynchronizer,
DifferentialInput: XilinxDifferentialInput, DifferentialInput: XilinxDifferentialInput,
DifferentialOutput: XilinxDifferentialOutput, 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): class XilinxDDROutputImplS7(Module):
def __init__(self, i1, i2, o, clk): def __init__(self, i1, i2, o, clk):
self.specials += Instance("ODDR", self.specials += Instance("ODDR",

View file

@ -88,39 +88,48 @@ def _run_ise(build_name, ise_path, source, mode, ngdbuild_opt,
script_ext = ".bat" script_ext = ".bat"
shell = ["cmd", "/c"] shell = ["cmd", "/c"]
build_script_contents = "@echo off\nrem Autogenerated by LiteX\n" build_script_contents = "@echo off\nrem Autogenerated by LiteX\n"
fail_stmt = " || exit /b"
else: else:
source_cmd = "source " source_cmd = "source "
script_ext = ".sh" script_ext = ".sh"
shell = ["bash"] shell = ["bash"]
build_script_contents = "# Autogenerated by LiteX\nset -e\n" build_script_contents = "# Autogenerated by LiteX\nset -e\n"
fail_stmt = ""
if source: if source:
settings = common.settings(ise_path, ver, "ISE_DS") settings = common.settings(ise_path, ver, "ISE_DS")
build_script_contents += source_cmd + settings + "\n" build_script_contents += source_cmd + settings + "\n"
ext = "ngc" ext = "ngc"
build_script_contents += """ build_script_contents += """
xst -ifn {build_name}.xst xst -ifn {build_name}.xst{fail_stmt}
""" """
build_script_contents += """ build_script_contents += """
ngdbuild {ngdbuild_opt} -uc {build_name}.ucf {build_name}.{ext} {build_name}.ngd 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 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 par {par_opt} {build_name}_map.ncd {build_name}.ncd {build_name}.pcf{fail_stmt}
bitgen {bitgen_opt} {build_name}.ncd {build_name}.bit bitgen {bitgen_opt} {build_name}.ncd {build_name}.bit{fail_stmt}
""" """
build_script_contents = build_script_contents.format(build_name=build_name, build_script_contents = build_script_contents.format(build_name=build_name,
ngdbuild_opt=ngdbuild_opt, bitgen_opt=bitgen_opt, ext=ext, 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_contents += ise_commands.format(build_name=build_name)
build_script_file = "build_" + build_name + script_ext build_script_file = "build_" + build_name + script_ext
tools.write_to_file(build_script_file, build_script_contents, force_unix=False) tools.write_to_file(build_script_file, build_script_contents, force_unix=False)
command = shell + [build_script_file] command = shell + [build_script_file]
r = subprocess.call(command) r = tools.subprocess_call_filtered(command, common.colors)
if r != 0: if r != 0:
raise OSError("Subprocess failed") raise OSError("Subprocess failed")
class XilinxISEToolchain: class XilinxISEToolchain:
attr_translate = {
"keep": ("keep", "true"),
"no_retiming": ("register_balancing", "no"),
"async_reg": None,
"no_shreg_extract": ("shreg_extract", "no")
}
def __init__(self): def __init__(self):
self.xst_opt = """-ifmt MIXED self.xst_opt = """-ifmt MIXED
-use_new_parser yes -use_new_parser yes
@ -150,7 +159,7 @@ class XilinxISEToolchain:
ngdbuild_opt = self.ngdbuild_opt ngdbuild_opt = self.ngdbuild_opt
vns = None vns = None
tools.mkdir_noerror(build_dir) os.makedirs(build_dir, exist_ok=True)
cwd = os.getcwd() cwd = os.getcwd()
os.chdir(build_dir) os.chdir(build_dir)
try: try:

View file

@ -18,12 +18,10 @@ class XilinxPlatform(GenericPlatform):
so = dict(common.xilinx_special_overrides) so = dict(common.xilinx_special_overrides)
if self.device[:3] == "xc7": if self.device[:3] == "xc7":
so.update(common.xilinx_s7_special_overrides) 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) 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): def build(self, *args, **kwargs):
return self.toolchain.build(self, *args, **kwargs) return self.toolchain.build(self, *args, **kwargs)

View file

@ -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_contents += "vivado -mode batch -source " + build_name + ".tcl\n"
build_script_file = "build_" + build_name + ".bat" build_script_file = "build_" + build_name + ".bat"
tools.write_to_file(build_script_file, build_script_contents) tools.write_to_file(build_script_file, build_script_contents)
r = subprocess.call([build_script_file]) command = build_script_file
else: else:
build_script_contents = "# Autogenerated by LiteX\nset -e\n" 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) settings = common.settings(vivado_path, ver)
build_script_contents += "source " + settings + "\n" build_script_contents += "source " + settings + "\n"
build_script_contents += "vivado -mode batch -source " + build_name + ".tcl\n" build_script_contents += "vivado -mode batch -source " + build_name + ".tcl\n"
build_script_file = "build_" + build_name + ".sh" build_script_file = "build_" + build_name + ".sh"
tools.write_to_file(build_script_file, build_script_contents) 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: if r != 0:
raise OSError("Subprocess failed") raise OSError("Subprocess failed")
class XilinxVivadoToolchain: 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): def __init__(self):
self.bitstream_commands = [] self.bitstream_commands = []
self.additional_commands = [] self.additional_commands = []
self.pre_synthesis_commands = [] self.pre_synthesis_commands = []
self.with_phys_opt = False self.with_phys_opt = False
self.clocks = dict()
self.false_paths = set()
def _build_batch(self, platform, sources, build_name): def _build_batch(self, platform, sources, build_name):
tcl = [] tcl = []
@ -103,7 +110,7 @@ class XilinxVivadoToolchain:
tcl.append("route_design") tcl.append("route_design")
tcl.append("report_route_status -file {}_route_status.rpt".format(build_name)) 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_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)) tcl.append("report_power -file {}_power.rpt".format(build_name))
for bitstream_command in self.bitstream_commands: for bitstream_command in self.bitstream_commands:
tcl.append(bitstream_command.format(build_name=build_name)) tcl.append(bitstream_command.format(build_name=build_name))
@ -113,15 +120,35 @@ class XilinxVivadoToolchain:
tcl.append("quit") tcl.append("quit")
tools.write_to_file(build_name + ".tcl", "\n".join(tcl)) 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", def build(self, platform, fragment, build_dir="build", build_name="top",
toolchain_path=None, source=True, run=True, **kwargs): toolchain_path=None, source=True, run=True, **kwargs):
tools.mkdir_noerror(build_dir) os.makedirs(build_dir, exist_ok=True)
cwd = os.getcwd() cwd = os.getcwd()
os.chdir(build_dir) os.chdir(build_dir)
if not isinstance(fragment, _Fragment): if not isinstance(fragment, _Fragment):
fragment = fragment.get_fragment() fragment = fragment.get_fragment()
platform.finalize(fragment) platform.finalize(fragment)
self._convert_clocks(platform)
v_output = platform.get_verilog(fragment, name=build_name, **kwargs) v_output = platform.get_verilog(fragment, name=build_name, **kwargs)
named_sc, named_pc = platform.resolve_signals(v_output.ns) named_sc, named_pc = platform.resolve_signals(v_output.ns)
v_file = build_name + ".v" v_file = build_name + ".v"
@ -137,11 +164,9 @@ class XilinxVivadoToolchain:
return v_output.ns return v_output.ns
def add_period_constraint(self, platform, clk, period): def add_period_constraint(self, platform, clk, period):
platform.add_platform_command( if clk in self.clocks:
"create_clock -name {clk} -period " + str(period) + raise ValueError("A period constraint already exists")
" [get_nets {clk}]", clk=clk) self.clocks[clk] = period
def add_false_path_constraint(self, platform, from_, to): def add_false_path_constraint(self, platform, from_, to):
platform.add_platform_command( self.false_paths.add((from_, to))
"set_false_path -from [get_clocks {from_}] -to [get_clocks {to}]",
from_=from_, to=to)

View file

@ -3,6 +3,7 @@ from litex.gen.fhdl.module import *
from litex.gen.fhdl.specials import * from litex.gen.fhdl.specials import *
from litex.gen.fhdl.bitcontainer import * from litex.gen.fhdl.bitcontainer import *
from litex.gen.fhdl.decorators import * from litex.gen.fhdl.decorators import *
from litex.gen.fhdl.simplify import *
from litex.gen.sim import * from litex.gen.sim import *

View file

@ -5,12 +5,10 @@ __all__ = ["log2_int", "bits_for", "value_bits_sign"]
def log2_int(n, need_pow2=True): def log2_int(n, need_pow2=True):
l = 1 if n == 0:
r = 0 return 0
while l < n: r = (n - 1).bit_length()
l *= 2 if need_pow2 and (1 << r) != n:
r += 1
if need_pow2 and l != n:
raise ValueError("Not a power of 2") raise ValueError("Not a power of 2")
return r return r
@ -26,6 +24,21 @@ def bits_for(n, require_sign_bit=False):
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): def value_bits_sign(v):
"""Bit length and signedness of a value. """Bit length and signedness of a value.
@ -53,18 +66,13 @@ def value_bits_sign(v):
elif isinstance(v, f._Operator): elif isinstance(v, f._Operator):
obs = list(map(value_bits_sign, v.operands)) obs = list(map(value_bits_sign, v.operands))
if v.op == "+" or v.op == "-": if v.op == "+" or v.op == "-":
if not obs[0][1] and not obs[1][1]: if len(obs) == 1:
# both operands unsigned if v.op == "-" and not obs[0][1]:
return max(obs[0][0], obs[1][0]) + 1, False return obs[0][0] + 1, True
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: else:
# first signed, second operand unsigned (add sign bit) return obs[0]
return max(obs[0][0], obs[1][0] + 1) + 1, True n, s = _bitwise_binary_bits_sign(*obs)
return n + 1, s
elif v.op == "*": elif v.op == "*":
if not obs[0][1] and not obs[1][1]: if not obs[0][1] and not obs[1][1]:
# both operands unsigned # both operands unsigned
@ -88,23 +96,14 @@ def value_bits_sign(v):
extra = 0 extra = 0
return obs[0][0] + extra, obs[0][1] return obs[0][0] + extra, obs[0][1]
elif v.op == "&" or v.op == "^" or v.op == "|": elif v.op == "&" or v.op == "^" or v.op == "|":
if not obs[0][1] and not obs[1][1]: return _bitwise_binary_bits_sign(*obs)
# both operands unsigned elif (v.op == "<" or v.op == "<=" or v.op == "==" or v.op == "!=" or
return max(obs[0][0], obs[1][0]), False v.op == ">" or v.op == ">="):
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 1, False
elif v.op == "~": elif v.op == "~":
return obs[0] return obs[0]
elif v.op == "m":
return _bitwise_binary_bits_sign(obs[1], obs[2])
else: else:
raise TypeError raise TypeError
elif isinstance(v, f._Slice): elif isinstance(v, f._Slice):

View file

@ -28,7 +28,8 @@ class ModuleTransformer:
return f return f
Wrapped.__name__ = victim.__name__ Wrapped.__name__ = victim.__name__
# "{}_{}".format(self.__class__.__name__, victim.__name__) Wrapped.__doc__ = victim.__doc__
Wrapped.__module__ = victim.__module__
return Wrapped return Wrapped
def wrap_instance(self, victim): def wrap_instance(self, victim):

View file

@ -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.specials import Memory, _MemoryPort, WRITE_FIRST, NO_CHANGE
from litex.gen.fhdl.decorators import ModuleTransformer from litex.gen.fhdl.decorators import ModuleTransformer
from litex.gen.util.misc import gcd_multiple from litex.gen.util.misc import gcd_multiple
from litex.gen.fhdl.bitcontainer import log2_int
class FullMemoryWE(ModuleTransformer): class FullMemoryWE(ModuleTransformer):
@ -10,13 +11,11 @@ class FullMemoryWE(ModuleTransformer):
def transform_fragment(self, i, f): def transform_fragment(self, i, f):
newspecials = set() newspecials = set()
replaced_ports = set()
for orig in f.specials: for orig in f.specials:
if not isinstance(orig, Memory): if not isinstance(orig, Memory):
newspecials.add(orig) newspecials.add(orig)
continue continue
global_granularity = gcd_multiple([p.we_granularity if p.we_granularity else orig.width for p in orig.ports]) global_granularity = gcd_multiple([p.we_granularity if p.we_granularity else orig.width for p in orig.ports])
if global_granularity == orig.width: if global_granularity == orig.width:
newspecials.add(orig) # nothing to do newspecials.add(orig) # nothing to do
@ -46,13 +45,11 @@ class FullMemoryWE(ModuleTransformer):
clock_domain=port.clock.cd) clock_domain=port.clock.cd)
newmem.ports.append(newport) newmem.ports.append(newport)
newspecials.add(newport) newspecials.add(newport)
for port in orig.ports:
replaced_ports.add(port)
self.replacements[orig] = newmems self.replacements[orig] = newmems
newspecials -= replaced_ports
f.specials = newspecials f.specials = newspecials
for oldmem in self.replacements.keys():
f.specials -= set(oldmem.ports)
class MemoryToArray(ModuleTransformer): class MemoryToArray(ModuleTransformer):
@ -111,7 +108,7 @@ class MemoryToArray(ModuleTransformer):
m = i*port.we_granularity m = i*port.we_granularity
M = (i+1)*port.we_granularity M = (i+1)*port.we_granularity
sync.append(If(port.we[i], 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: else:
sync.append(If(port.we, sync.append(If(port.we,
storage[port.adr].eq(port.dat_w))) storage[port.adr].eq(port.dat_w)))
@ -120,3 +117,88 @@ class MemoryToArray(ModuleTransformer):
newspecials -= processed_ports newspecials -= processed_ports
f.specials = newspecials 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

View file

@ -111,7 +111,12 @@ class Instance(Special):
self.items = list(items) self.items = list(items)
self.synthesis_directive = synthesis_directive self.synthesis_directive = synthesis_directive
for k, v in sorted(kwargs.items(), key=itemgetter(0)): for k, v in sorted(kwargs.items(), key=itemgetter(0)):
try:
item_type, item_name = k.split("_", maxsplit=1) 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 = { item_class = {
"i": Instance.Input, "i": Instance.Input,
"o": Instance.Output, "o": Instance.Output,
@ -277,7 +282,7 @@ class Memory(Special):
data_regs = {} data_regs = {}
for port in memory.ports: for port in memory.ports:
if not port.async_read: 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") adr_reg = Signal(name_override="memadr")
r += "reg [" + str(adrbits-1) + ":0] " \ r += "reg [" + str(adrbits-1) + ":0] " \
+ gn(adr_reg) + ";\n" + gn(adr_reg) + ";\n"
@ -303,11 +308,11 @@ class Memory(Special):
r += "\tif (" + gn(port.we) + ")\n" r += "\tif (" + gn(port.we) + ")\n"
r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "] <= " + gn(port.dat_w) + ";\n" r += "\t\t" + gn(memory) + "[" + gn(port.adr) + "] <= " + gn(port.dat_w) + ";\n"
if not port.async_read: 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" rd = "\t" + gn(adr_regs[id(port)]) + " <= " + gn(port.adr) + ";\n"
else: else:
bassign = gn(data_regs[id(port)]) + " <= " + gn(memory) + "[" + gn(port.adr) + "];\n" 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 rd = "\t" + bassign
elif port.mode == NO_CHANGE: elif port.mode == NO_CHANGE:
rd = "\tif (!" + gn(port.we) + ")\n" \ rd = "\tif (!" + gn(port.we) + ")\n" \
@ -323,7 +328,7 @@ class Memory(Special):
if port.async_read: if port.async_read:
r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(port.adr) + "];\n" r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(port.adr) + "];\n"
else: 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" r += "assign " + gn(port.dat_r) + " = " + gn(memory) + "[" + gn(adr_regs[id(port)]) + "];\n"
else: else:
r += "assign " + gn(port.dat_r) + " = " + gn(data_regs[id(port)]) + ";\n" r += "assign " + gn(port.dat_r) + " = " + gn(data_regs[id(port)]) + ";\n"
@ -340,21 +345,3 @@ class Memory(Special):
r += "end\n\n" r += "end\n\n"
return r 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)

View file

@ -1,5 +1,6 @@
import builtins as _builtins import builtins as _builtins
import collections as _collections import collections as _collections
import re as _re
from litex.gen.fhdl import tracer as _tracer from litex.gen.fhdl import tracer as _tracer
from litex.gen.util.misc import flat_iteration as _flat_iteration from litex.gen.util.misc import flat_iteration as _flat_iteration
@ -136,7 +137,8 @@ def wrap(value):
if isinstance(value, (bool, int)): if isinstance(value, (bool, int)):
value = Constant(value) value = Constant(value)
if not isinstance(value, _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 return value
@ -310,12 +312,20 @@ class Signal(_Value):
determined by the integer range given by `min` (inclusive, determined by the integer range given by `min` (inclusive,
defaults to 0) and `max` (exclusive, defaults to 2). defaults to 0) and `max` (exclusive, defaults to 2).
related : Signal or None 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 from litex.gen.fhdl.bitcontainer import bits_for
_Value.__init__(self) _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 # determine number of bits and signedness
if bits_sign is None: if bits_sign is None:
if min is None: if min is None:
@ -332,15 +342,19 @@ class Signal(_Value):
self.nbits, self.signed = bits_sign self.nbits, self.signed = bits_sign
else: else:
self.nbits, self.signed = bits_sign, False 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: if not isinstance(self.nbits, int) or self.nbits <= 0:
raise ValueError("Signal width must be a strictly positive integer") raise ValueError("Signal width must be a strictly positive integer")
if attr is None:
attr = set()
self.variable = variable # deprecated self.variable = variable # deprecated
self.reset = reset self.reset = reset
self.name_override = name_override self.name_override = name_override
self.backtrace = _tracer.trace_back(name) self.backtrace = _tracer.trace_back(name)
self.related = related self.related = related
self.attribute = attribute self.attr = attr
def __setattr__(self, k, v): def __setattr__(self, k, v):
if k == "reset": if k == "reset":

View file

@ -116,9 +116,7 @@ def _printexpr(ns, node):
def _printnode(ns, at, level, node): def _printnode(ns, at, level, node):
if node is None: if isinstance(node, Display):
return ""
elif isinstance(node, Display):
s = "\"" + node.s + "\\r\"" s = "\"" + node.s + "\\r\""
for arg in node.args: for arg in node.args:
s += ", " s += ", "
@ -176,8 +174,30 @@ def _list_comb_wires(f):
r |= g[0] r |= g[0]
return r 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): reg_initialization):
sigs = list_signals(f) | list_special_ios(f, True, True, True) sigs = list_signals(f) | list_special_ios(f, True, True, True)
special_outs = list_special_ios(f, False, True, True) special_outs = list_special_ios(f, False, True, True)
@ -190,6 +210,9 @@ def _printheader(f, ios, name, ns,
if not firstp: if not firstp:
r += ",\n" r += ",\n"
firstp = False firstp = False
attr = _printattr(sig, attr_translate)
if attr:
r += "\t" + attr
if sig in inouts: if sig in inouts:
r += "\tinout " + _printsig(ns, sig) r += "\tinout " + _printsig(ns, sig)
elif sig in targets: elif sig in targets:
@ -201,6 +224,9 @@ def _printheader(f, ios, name, ns,
r += "\tinput " + _printsig(ns, sig) r += "\tinput " + _printsig(ns, sig)
r += "\n);\n\n" r += "\n);\n\n"
for sig in sorted(sigs - ios, key=lambda x: x.duid): for sig in sorted(sigs - ios, key=lambda x: x.duid):
attr = _printattr(sig, attr_translate)
if attr:
r += attr + " "
if sig in wires: if sig in wires:
r += "wire " + _printsig(ns, sig) + ";\n" r += "wire " + _printsig(ns, sig) + ";\n"
else: else:
@ -219,15 +245,19 @@ def _printcomb(f, ns,
r = "" r = ""
if f.comb: if f.comb:
if dummy_signal: if dummy_signal:
# Generate a dummy event to get the simulator explanation = """
# to run the combinatorial process once at the beginning. // 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_off = "// synthesis translate_off\n"
syn_on = "// synthesis translate_on\n" syn_on = "// synthesis translate_on\n"
dummy_s = Signal(name_override="dummy_s") dummy_s = Signal(name_override="dummy_s")
r += explanation
r += syn_off r += syn_off
r += "reg " + _printsig(ns, dummy_s) + ";\n" r += "reg " + _printsig(ns, dummy_s) + ";\n"
r += "initial " + ns.get_name(dummy_s) + " <= 1'd0;\n" r += "initial " + ns.get_name(dummy_s) + " <= 1'd0;\n"
r += syn_on r += syn_on
r += "\n"
groups = group_by_targets(f.comb) groups = group_by_targets(f.comb)
@ -280,8 +310,14 @@ def _printspecials(overrides, specials, ns, add_data_file):
return r return r
class DummyAttrTranslate:
def __getitem__(self, k):
return (k, "true")
def convert(f, ios=None, name="top", def convert(f, ios=None, name="top",
special_overrides=dict(), special_overrides=dict(),
attr_translate=DummyAttrTranslate(),
create_clock_domains=True, create_clock_domains=True,
display_run=False, display_run=False,
reg_initialization=True, reg_initialization=True,
@ -323,7 +359,7 @@ def convert(f, ios=None, name="top",
r.ns = ns r.ns = ns
src = "/* Machine-generated using LiteX gen */\n" 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) reg_initialization=reg_initialization)
src += _printcomb(f, ns, src += _printcomb(f, ns,
display_run=display_run, display_run=display_run,

View file

@ -38,7 +38,7 @@ class NodeVisitor:
self.visit_clock_domains(node) self.visit_clock_domains(node)
elif isinstance(node, _ArrayProxy): elif isinstance(node, _ArrayProxy):
self.visit_ArrayProxy(node) self.visit_ArrayProxy(node)
elif node is not None: else:
self.visit_unknown(node) self.visit_unknown(node)
def visit_Constant(self, node): def visit_Constant(self, node):
@ -140,10 +140,8 @@ class NodeTransformer:
return self.visit_clock_domains(node) return self.visit_clock_domains(node)
elif isinstance(node, _ArrayProxy): elif isinstance(node, _ArrayProxy):
return self.visit_ArrayProxy(node) return self.visit_ArrayProxy(node)
elif node is not None:
return self.visit_unknown(node)
else: else:
return None return self.visit_unknown(node)
def visit_Constant(self, node): def visit_Constant(self, node):
return node return node

View file

@ -1,3 +1,7 @@
"""
Clock domain crossing module
"""
from litex.gen.fhdl.structure import * from litex.gen.fhdl.structure import *
from litex.gen.fhdl.module import Module from litex.gen.fhdl.module import Module
from litex.gen.fhdl.specials import Special, Memory 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 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): class MultiRegImpl(Module):
def __init__(self, i, o, odomain, n): def __init__(self, i, o, odomain, n):
self.i = i self.i = i
@ -34,7 +27,8 @@ class MultiRegImpl(Module):
sd += reg.eq(src) sd += reg.eq(src)
src = reg src = reg
self.comb += self.o.eq(src) 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): class MultiReg(Special):
@ -114,6 +108,7 @@ class BusSynchronizer(Module):
ibuffer = Signal(width) ibuffer = Signal(width)
obuffer = Signal(width) obuffer = Signal(width)
sync_i += If(self._pong.o, ibuffer.eq(self.i)) sync_i += If(self._pong.o, ibuffer.eq(self.i))
ibuffer.attr.add("no_retiming")
self.specials += MultiReg(ibuffer, obuffer, odomain) self.specials += MultiReg(ibuffer, obuffer, odomain)
sync_o += If(self._ping.o, self.o.eq(obuffer)) sync_o += If(self._ping.o, self.o.eq(obuffer))

View file

@ -3,7 +3,7 @@ from litex.gen.fhdl.module import Module
from litex.gen.fhdl.specials import Memory from litex.gen.fhdl.specials import Memory
from litex.gen.fhdl.bitcontainer import log2_int from litex.gen.fhdl.bitcontainer import log2_int
from litex.gen.fhdl.decorators import ClockDomainsRenamer 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): def _inc(signal, modulo):
@ -177,15 +177,11 @@ class AsyncFIFO(Module, _FIFOInterface):
] ]
produce_rdomain = Signal(depth_bits+1) produce_rdomain = Signal(depth_bits+1)
self.specials += [ produce.q.attr.add("no_retiming")
NoRetiming(produce.q), self.specials += MultiReg(produce.q, produce_rdomain, "read")
MultiReg(produce.q, produce_rdomain, "read")
]
consume_wdomain = Signal(depth_bits+1) consume_wdomain = Signal(depth_bits+1)
self.specials += [ consume.q.attr.add("no_retiming")
NoRetiming(consume.q), self.specials += MultiReg(consume.q, consume_wdomain, "write")
MultiReg(consume.q, consume_wdomain, "write")
]
if depth_bits == 1: if depth_bits == 1:
self.comb += self.writable.eq((produce.q[-1] == consume_wdomain[-1]) self.comb += self.writable.eq((produce.q[-1] == consume_wdomain[-1])
| (produce.q[-2] == consume_wdomain[-2])) | (produce.q[-2] == consume_wdomain[-2]))

View file

@ -40,7 +40,7 @@ def _target_eq(a, b):
elif ty == _Slice: elif ty == _Slice:
return (_target_eq(a.value, b.value) return (_target_eq(a.value, b.value)
and a.start == b.start and a.start == b.start
and a.end == b.end) and a.stop == b.stop)
elif ty == _ArrayProxy: elif ty == _ArrayProxy:
return (all(_target_eq(x, y) for x, y in zip(a.choices, b.choices)) return (all(_target_eq(x, y) for x, y in zip(a.choices, b.choices))
and _target_eq(a.key, b.key)) and _target_eq(a.key, b.key))
@ -82,8 +82,47 @@ class _LowerNext(NodeTransformer):
else: else:
return node return node
class FSM(Module): 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): def __init__(self, reset_state=None):
self.actions = OrderedDict() self.actions = OrderedDict()
self.state_aliases = dict() self.state_aliases = dict()
@ -95,6 +134,16 @@ class FSM(Module):
self.after_leaving_signals = OrderedDict() self.after_leaving_signals = OrderedDict()
def act(self, state, *statements): 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: if self.finalized:
raise FinalizeError raise FinalizeError
if self.reset_state is None: if self.reset_state is None:
@ -119,6 +168,10 @@ class FSM(Module):
self.state_aliases[name] = target self.state_aliases[name] = target
def ongoing(self, state): 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() is_ongoing = Signal()
self.act(state, is_ongoing.eq(1)) self.act(state, is_ongoing.eq(1))
return is_ongoing return is_ongoing

View file

@ -38,6 +38,8 @@ class DifferentialOutput(Special):
class CRG(Module): class CRG(Module):
""" Clock and Reset Generator """
def __init__(self, clk, rst=0): def __init__(self, clk, rst=0):
self.clock_domains.cd_sys = ClockDomain() self.clock_domains.cd_sys = ClockDomain()
self.clock_domains.cd_por = ClockDomain(reset_less=True) self.clock_domains.cd_por = ClockDomain(reset_less=True)

View file

@ -11,7 +11,9 @@ from litex.gen.fhdl.bitcontainer import value_bits_sign
from litex.gen.fhdl.tools import (list_targets, list_signals, from litex.gen.fhdl.tools import (list_targets, list_signals,
insert_resets, lower_specials) insert_resets, lower_specials)
from litex.gen.fhdl.simplify import MemoryToArray 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 from litex.gen.sim.vcd import VCDWriter, DummyVCDWriter
@ -144,8 +146,8 @@ class Evaluator:
v = self.eval(node.v, postcommit) & (2**nbits - 1) v = self.eval(node.v, postcommit) & (2**nbits - 1)
return sum(v << i*nbits for i in range(node.n)) return sum(v << i*nbits for i in range(node.n))
elif isinstance(node, _ArrayProxy): elif isinstance(node, _ArrayProxy):
return self.eval(node.choices[self.eval(node.key, postcommit)], idx = min(len(node.choices) - 1, self.eval(node.key, postcommit))
postcommit) return self.eval(node.choices[idx], postcommit)
elif isinstance(node, _MemoryLocation): elif isinstance(node, _MemoryLocation):
array = self.replaced_memories[node.memory] array = self.replaced_memories[node.memory]
return self.eval(array[self.eval(node.index, postcommit)], postcommit) return self.eval(array[self.eval(node.index, postcommit)], postcommit)
@ -183,7 +185,8 @@ class Evaluator:
full_value |= value << node.start full_value |= value << node.start
self.assign(node.value, full_value) self.assign(node.value, full_value)
elif isinstance(node, _ArrayProxy): 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): elif isinstance(node, _MemoryLocation):
array = self.replaced_memories[node.memory] array = self.replaced_memories[node.memory]
self.assign(array[self.eval(node.index)], value) self.assign(array[self.eval(node.index)], value)
@ -218,9 +221,24 @@ class Evaluator:
raise NotImplementedError 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 # TODO: instances via Iverilog/VPI
class Simulator: 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): if isinstance(fragment_or_module, _Fragment):
self.fragment = fragment_or_module self.fragment = fragment_or_module
else: else:
@ -229,16 +247,11 @@ class Simulator:
mta = MemoryToArray() mta = MemoryToArray()
mta.transform_fragment(None, self.fragment) 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 += fs
self.fragment.specials -= lowered 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: if self.fragment.specials:
raise ValueError("Could not lower all specials", self.fragment.specials) raise ValueError("Could not lower all specials", self.fragment.specials)

View file

@ -3,7 +3,6 @@ import subprocess
import struct import struct
import shutil import shutil
from litex.build.tools import write_to_file
from litex.soc.integration import cpu_interface, soc_sdram, sdram_init 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"] "Builder", "builder_args", "builder_argdict"]
# in build order (for dependencies)
soc_software_packages = [ soc_software_packages = [
"libbase",
"libcompiler_rt", "libcompiler_rt",
"libbase",
"libnet", "libnet",
"bios" "bios"
] ]
@ -47,10 +45,11 @@ class Builder:
self.software_packages = [] self.software_packages = []
for name in soc_software_packages: for name in soc_software_packages:
self.add_software_package( self.add_software_package(name)
name, os.path.join(soc_directory, "software", 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)) self.software_packages.append((name, src_dir))
def _generate_includes(self): def _generate_includes(self):
@ -67,38 +66,30 @@ class Builder:
buildinc_dir = os.path.join(self.output_dir, "software", "include") buildinc_dir = os.path.join(self.output_dir, "software", "include")
generated_dir = os.path.join(buildinc_dir, "generated") generated_dir = os.path.join(buildinc_dir, "generated")
os.makedirs(generated_dir, exist_ok=True) os.makedirs(generated_dir, exist_ok=True)
with open(os.path.join(generated_dir, "variables.mak"), "w") as f:
variables_contents = []
def define(k, v): def define(k, v):
variables_contents.append("{}={}\n".format(k, _makefile_escape(v))) f.write("{}={}\n".format(k, _makefile_escape(v)))
for k, v in cpu_interface.get_cpu_mak(cpu_type): for k, v in cpu_interface.get_cpu_mak(cpu_type):
define(k, v) define(k, v)
define("SOC_DIRECTORY", soc_directory) define("SOC_DIRECTORY", soc_directory)
define("BUILDINC_DIRECTORY", buildinc_dir) define("BUILDINC_DIRECTORY", buildinc_dir)
f.write("export BUILDINC_DIRECTORY\n")
for name, src_dir in self.software_packages: for name, src_dir in self.software_packages:
define(name.upper() + "_DIRECTORY", src_dir) define(name.upper() + "_DIRECTORY", src_dir)
write_to_file(
os.path.join(generated_dir, "variables.mak"),
"".join(variables_contents))
write_to_file( with open(os.path.join(generated_dir, "output_format.ld"), "w") as f:
os.path.join(generated_dir, "output_format.ld"), f.write(cpu_interface.get_linker_output_format(cpu_type))
cpu_interface.get_linker_output_format(cpu_type)) with open(os.path.join(generated_dir, "regions.ld"), "w") as f:
write_to_file( f.write(cpu_interface.get_linker_regions(memory_regions))
os.path.join(generated_dir, "regions.ld"),
cpu_interface.get_linker_regions(memory_regions))
write_to_file( with open(os.path.join(generated_dir, "mem.h"), "w") as f:
os.path.join(generated_dir, "mem.h"), f.write(cpu_interface.get_mem_header(memory_regions, flash_boot_address))
cpu_interface.get_mem_header(memory_regions, flash_boot_address)) with open(os.path.join(generated_dir, "csr.h"), "w") as f:
write_to_file( f.write(cpu_interface.get_csr_header(csr_regions, constants))
os.path.join(generated_dir, "csr.h"),
cpu_interface.get_csr_header(csr_regions, constants))
if sdram_phy_settings is not None: if sdram_phy_settings is not None:
write_to_file( with open(os.path.join(generated_dir, "sdram_phy.h"), "w") as f:
os.path.join(generated_dir, "sdram_phy.h"), f.write(sdram_init.get_sdram_phy_header(sdram_phy_settings))
sdram_init.get_sdram_phy_header(sdram_phy_settings))
def _generate_csr_csv(self): def _generate_csr_csv(self):
memory_regions = self.soc.get_memory_regions() memory_regions = self.soc.get_memory_regions()
@ -107,9 +98,8 @@ class Builder:
csr_dir = os.path.dirname(self.csr_csv) csr_dir = os.path.dirname(self.csr_csv)
os.makedirs(csr_dir, exist_ok=True) os.makedirs(csr_dir, exist_ok=True)
write_to_file( with open(self.csr_csv, "w") as f:
self.csr_csv, f.write(cpu_interface.get_csr_csv(csr_regions, constants, memory_regions))
cpu_interface.get_csr_csv(csr_regions, constants, memory_regions))
def _prepare_software(self): def _prepare_software(self):
for name, src_dir in self.software_packages: for name, src_dir in self.software_packages:

View file

@ -11,28 +11,18 @@ cpu_endianness = {
} }
def get_cpu_mak(cpu): def get_cpu_mak(cpu):
clang = os.getenv("CLANG", "")
if clang != "":
clang = bool(int(clang))
else:
clang = None
if cpu == "lm32": if cpu == "lm32":
assert not clang, "lm32 not supported with clang."
triple = "lm32-elf" triple = "lm32-elf"
cpuflags = "-mbarrel-shift-enabled -mmultiply-enabled -mdivide-enabled -msign-extend-enabled" cpuflags = "-mbarrel-shift-enabled -mmultiply-enabled -mdivide-enabled -msign-extend-enabled"
clang = ""
elif cpu == "or1k": 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" triple = "or1k-linux"
cpuflags += "-mffl1 -maddc" cpuflags = "-mhard-mul -mhard-div -mror -mffl1 -maddc"
clang = "1"
elif cpu == "riscv32": elif cpu == "riscv32":
assert not clang, "riscv32 not supported with clang."
triple = "riscv32-unknown-elf" triple = "riscv32-unknown-elf"
cpuflags = "-mno-save-restore" cpuflags = "-mno-save-restore"
clang = "0"
else: else:
raise ValueError("Unsupported CPU type: "+cpu) raise ValueError("Unsupported CPU type: "+cpu)
return [ return [
@ -40,7 +30,7 @@ def get_cpu_mak(cpu):
("CPU", cpu), ("CPU", cpu),
("CPUFLAGS", cpuflags), ("CPUFLAGS", cpuflags),
("CPUENDIANNESS", cpu_endianness[cpu]), ("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 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 = ""
r += "#define CSR_"+reg_name.upper()+"_ADDR "+hex(reg_base)+"\n" 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" r += "#define CSR_"+name.upper()+"_BASE "+hex(origin)+"\n"
for csr in obj: for csr in obj:
nr = (csr.size + busword - 1)//busword 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 origin += 4*nr
r += "\n/* constants */\n" r += "\n/* constants */\n"
for name, value in constants: for name, value in constants:
r += "#define " + name if value is None:
if value is not None: r += "#define "+name+"\n"
continue
if isinstance(value, str): if isinstance(value, str):
r += " \"" + value + "\"" value = "\"" + value + "\""
ctype = "const char *"
else: else:
r += " " + str(value) value = str(value)
r += "\n" 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" r += "\n#endif\n"
return r return r

View file

@ -175,6 +175,14 @@ class SoCCore(Module):
r += self._constants r += self._constants
return r 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): def do_finalize(self):
registered_mems = {regions[0] for regions in self._memory_regions} registered_mems = {regions[0] for regions in self._memory_regions}
if self.cpu_type is not None: if self.cpu_type is not None:
@ -189,7 +197,7 @@ class SoCCore(Module):
# CSR # CSR
self.submodules.csrbankarray = csr_bus.CSRBankArray(self, 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) data_width=self.csr_data_width, address_width=self.csr_address_width)
self.submodules.csrcon = csr_bus.Interconnect( self.submodules.csrcon = csr_bus.Interconnect(
self.wishbone2csr.csr, self.csrbankarray.get_buses()) self.wishbone2csr.csr, self.csrbankarray.get_buses())

View file

@ -62,7 +62,7 @@ class SoCSDRAM(SoCCore):
main_ram_size = 2**(geom_settings.bankbits + main_ram_size = 2**(geom_settings.bankbits +
geom_settings.rowbits + geom_settings.rowbits +
geom_settings.colbits)*sdram_width//8 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) main_ram_size = min(main_ram_size, 256*1024*1024)
self.add_constant("L2_SIZE", self.l2_size) self.add_constant("L2_SIZE", self.l2_size)

View file

@ -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 import *
from litex.gen.util.misc import xdir from litex.gen.util.misc import xdir
from litex.gen.fhdl.tracer import get_obj_var_name from litex.gen.fhdl.tracer import get_obj_var_name
@ -12,13 +38,69 @@ class _CSRBase(DUID):
self.size = size 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): 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): def __init__(self, size=1, name=None):
_CSRBase.__init__(self, size, name) _CSRBase.__init__(self, size, name)
self.re = Signal(name=self.name + "_re") self.re = Signal(name=self.name + "_re")
self.r = Signal(self.size, name=self.name + "_r") self.r = Signal(self.size, name=self.name + "_r")
self.w = Signal(self.size, name=self.name + "_w") 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): class _CompoundCSR(_CSRBase, Module):
def __init__(self, size, name): def __init__(self, size, name):
@ -35,6 +117,39 @@ class _CompoundCSR(_CSRBase, Module):
class CSRStatus(_CompoundCSR): 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): def __init__(self, size=1, reset=0, name=None):
_CompoundCSR.__init__(self, size, name) _CompoundCSR.__init__(self, size, name)
self.status = Signal(self.size, reset=reset) 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.comb += sc.w.eq(self.status[i*busword:i*busword+nbits])
self.simple_csrs.append(sc) self.simple_csrs.append(sc)
def read(self):
"""Read method for simulation."""
return (yield self.status)
class CSRStorage(_CompoundCSR): 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): def __init__(self, size=1, reset=0, atomic_write=False, write_from_dev=False, alignment_bits=0, name=None):
_CompoundCSR.__init__(self, size, name) _CompoundCSR.__init__(self, size, name)
self.alignment_bits = alignment_bits 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 += If(sc.re, self.storage_full[lo:hi].eq(sc.r))
self.sync += self.re.eq(sc.re) 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): def csrprefix(prefix, csrs, done):
for csr in csrs: for csr in csrs:
@ -129,8 +312,20 @@ def _make_gatherer(method, cls, prefix_cb):
class AutoCSR: 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_memories = _make_gatherer("get_memories", Memory, memprefix)
get_csrs = _make_gatherer("get_csrs", _CSRBase, csrprefix) get_csrs = _make_gatherer("get_csrs", _CSRBase, csrprefix)
get_constants = _make_gatherer("get_constants", CSRConstant, csrprefix)
class GenericBank(Module): class GenericBank(Module):

View file

@ -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 import *
from litex.gen.genlib.record import * from litex.gen.genlib.record import *
from litex.gen.genlib.misc import chooser from litex.gen.genlib.misc import chooser
@ -20,6 +28,24 @@ class Interface(Record):
Record.__init__(self, set_layout_parameters(_layout, Record.__init__(self, set_layout_parameters(_layout,
data_width=data_width, address_width=address_width)) 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): class Interconnect(Module):
def __init__(self, master, slaves): def __init__(self, master, slaves):
@ -144,6 +170,7 @@ class CSRBankArray(Module):
def scan(self, ifargs, ifkwargs): def scan(self, ifargs, ifkwargs):
self.banks = [] self.banks = []
self.srams = [] self.srams = []
self.constants = []
for name, obj in xdir(self.source, True): for name, obj in xdir(self.source, True):
if hasattr(obj, "get_csrs"): if hasattr(obj, "get_csrs"):
csrs = obj.get_csrs() csrs = obj.get_csrs()
@ -165,6 +192,9 @@ class CSRBankArray(Module):
self.submodules += mmap self.submodules += mmap
csrs += mmap.get_csrs() csrs += mmap.get_csrs()
self.srams.append((name, memory, mapaddr, mmap)) 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: if csrs:
mapaddr = self.address_map(name, None) mapaddr = self.address_map(name, None)
if mapaddr is None: if mapaddr is None:

View file

@ -1,3 +1,8 @@
"""
The event manager provides a systematic way to generate standard interrupt
controllers.
"""
from functools import reduce from functools import reduce
from operator import or_ from operator import or_
@ -8,16 +13,44 @@ from litex.soc.interconnect.csr import *
class _EventSource(DUID): 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): def __init__(self):
DUID.__init__(self) DUID.__init__(self)
self.status = Signal() # value in the status register self.status = Signal()
self.pending = Signal() # value in the pending register + assert irq if unmasked self.pending = Signal()
self.trigger = Signal() # trigger signal interface to the user design self.trigger = Signal()
self.clear = Signal() # clearing attempt by W1C to pending register, ignored by some event sources self.clear = Signal()
# set on a positive trigger pulse
class EventSourcePulse(Module, _EventSource): 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): def __init__(self):
_EventSource.__init__(self) _EventSource.__init__(self)
self.comb += self.status.eq(0) 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): 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): def __init__(self):
_EventSource.__init__(self) _EventSource.__init__(self)
self.comb += self.status.eq(self.trigger) 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): 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): def __init__(self):
_EventSource.__init__(self) _EventSource.__init__(self)
self.comb += [ self.comb += [
@ -51,6 +93,31 @@ class EventSourceLevel(Module, _EventSource):
class EventManager(Module, AutoCSR): 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): def __init__(self):
self.irq = Signal() self.irq = Signal()
@ -81,6 +148,8 @@ class EventManager(Module, AutoCSR):
class SharedIRQ(Module): class SharedIRQ(Module):
"""Allow an IRQ signal to be shared between multiple EventManager objects."""
def __init__(self, *event_managers): def __init__(self, *event_managers):
self.irq = Signal() self.irq = Signal()
self.comb += self.irq.eq(reduce(or_, [ev.irq for ev in event_managers])) self.comb += self.irq.eq(reduce(or_, [ev.irq for ev in event_managers]))

View file

@ -9,14 +9,7 @@ from litex.gen.genlib.fsm import FSM, NextState
from litex.soc.interconnect import csr from litex.soc.interconnect import csr
# TODO: rewrite without FlipFlop and Counter # TODO: rewrite without FlipFlop
@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)
_layout = [ _layout = [
@ -40,6 +33,10 @@ class Interface(Record):
data_width=data_width, data_width=data_width,
sel_width=data_width//8)) sel_width=data_width//8))
@staticmethod
def like(other):
return Interface(len(other.dat_w))
def _do_transaction(self): def _do_transaction(self):
yield self.cyc.eq(1) yield self.cyc.eq(1)
yield self.stb.eq(1) yield self.stb.eq(1)

View file

@ -27,6 +27,12 @@ static void __attribute__((noreturn)) boot(unsigned int r1, unsigned int r2, uns
while(1); while(1);
} }
enum {
ACK_TIMEOUT,
ACK_CANCELLED,
ACK_OK
};
static int check_ack(void) static int check_ack(void)
{ {
int recognized; int recognized;
@ -42,10 +48,12 @@ static int check_ack(void)
if(uart_read_nonblock()) { if(uart_read_nonblock()) {
char c; char c;
c = uart_read(); c = uart_read();
if((c == 'Q') || (c == '\e'))
return ACK_CANCELLED;
if(c == str[recognized]) { if(c == str[recognized]) {
recognized++; recognized++;
if(recognized == SFL_MAGIC_LEN) if(recognized == SFL_MAGIC_LEN)
return 1; return ACK_OK;
} else { } else {
if(c == str[0]) if(c == str[0])
recognized = 1; recognized = 1;
@ -55,30 +63,39 @@ static int check_ack(void)
} }
timer0_update_value_write(1); timer0_update_value_write(1);
} }
return 0; return ACK_TIMEOUT;
} }
#define MAX_FAILED 5 #define MAX_FAILED 5
void serialboot(void) /* Returns 1 if other boot methods should be tried */
int serialboot(void)
{ {
struct sfl_frame frame; struct sfl_frame frame;
int failed; int failed;
unsigned int cmdline_adr, initrdstart_adr, initrdend_adr; unsigned int cmdline_adr, initrdstart_adr, initrdend_adr;
static const char str[SFL_MAGIC_LEN+1] = SFL_MAGIC_REQ; static const char str[SFL_MAGIC_LEN+1] = SFL_MAGIC_REQ;
const char *c; const char *c;
int ack_status;
printf("Booting from serial...\n"); printf("Booting from serial...\n");
printf("Press Q or ESC to abort boot completely.\n");
c = str; c = str;
while(*c) { while(*c) {
uart_write(*c); uart_write(*c);
c++; c++;
} }
if(!check_ack()) { ack_status = check_ack();
if(ack_status == ACK_TIMEOUT) {
printf("Timeout\n"); printf("Timeout\n");
return; return 1;
} }
if(ack_status == ACK_CANCELLED) {
printf("Cancelled\n");
return 0;
}
/* assume ACK_OK */
failed = 0; failed = 0;
cmdline_adr = initrdstart_adr = initrdend_adr = 0; cmdline_adr = initrdstart_adr = initrdend_adr = 0;
@ -102,7 +119,7 @@ void serialboot(void)
failed++; failed++;
if(failed == MAX_FAILED) { if(failed == MAX_FAILED) {
printf("Too many consecutive errors, aborting"); printf("Too many consecutive errors, aborting");
return; return 1;
} }
uart_write(SFL_ACK_CRCERROR); uart_write(SFL_ACK_CRCERROR);
continue; continue;
@ -113,7 +130,7 @@ void serialboot(void)
case SFL_CMD_ABORT: case SFL_CMD_ABORT:
failed = 0; failed = 0;
uart_write(SFL_ACK_SUCCESS); uart_write(SFL_ACK_SUCCESS);
return; return 1;
case SFL_CMD_LOAD: { case SFL_CMD_LOAD: {
char *writepointer; char *writepointer;
@ -168,12 +185,13 @@ void serialboot(void)
failed++; failed++;
if(failed == MAX_FAILED) { if(failed == MAX_FAILED) {
printf("Too many consecutive errors, aborting"); printf("Too many consecutive errors, aborting");
return; return 1;
} }
uart_write(SFL_ACK_UNKNOWN); uart_write(SFL_ACK_UNKNOWN);
break; break;
} }
} }
return 1;
} }
#ifdef CSR_ETHMAC_BASE #ifdef CSR_ETHMAC_BASE

View file

@ -1,7 +1,7 @@
#ifndef __BOOT_H #ifndef __BOOT_H
#define __BOOT_H #define __BOOT_H
void serialboot(void); int serialboot(void);
void netboot(void); void netboot(void);
void flashboot(void); void flashboot(void);
void romboot(void); void romboot(void);

View file

@ -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) static void boot_sequence(void)
{ {
if(test_user_abort()) { if(serialboot()) {
#ifdef FLASH_BOOT_ADDRESS #ifdef FLASH_BOOT_ADDRESS
flashboot(); flashboot();
#endif #endif
@ -557,10 +497,9 @@ int main(int i, char **c)
printf("(unknown)\n"); printf("(unknown)\n");
#endif #endif
puts( puts(
"(c) Copyright 2012-2016 Enjoy-Digital\n" "(c) Copyright 2012-2017 Enjoy-Digital\n"
"(c) Copyright 2007-2016 M-Labs Limited\n" "(c) Copyright 2007-2017 M-Labs Limited\n"
"Built "__DATE__" "__TIME__"\n"); "Built "__DATE__" "__TIME__"\n");
crcbios(); crcbios();
#ifdef CSR_ETHMAC_BASE #ifdef CSR_ETHMAC_BASE
eth_init(); eth_init();

View file

@ -22,6 +22,8 @@
#ifndef __INTTYPES_H #ifndef __INTTYPES_H
#define __INTTYPES_H #define __INTTYPES_H
#include <stdint.h>
# if __WORDSIZE == 64 # if __WORDSIZE == 64
# define __PRI64_PREFIX "l" # define __PRI64_PREFIX "l"
# define __PRIPTR_PREFIX "l" # define __PRIPTR_PREFIX "l"

View file

@ -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 */

View file

@ -14,7 +14,7 @@ extern "C" {
typedef unsigned long size_t; typedef unsigned long size_t;
typedef long ptrdiff_t; typedef long ptrdiff_t;
#define offsetof(s,m) (size_t)&(((s *)0)->m) #define offsetof(type, member) __builtin_offsetof(type, member)
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -74,6 +74,7 @@ void qsort(void *base, size_t nmemb, size_t size, int(*compar)(const void *, con
char *getenv(const char *name); char *getenv(const char *name);
void *malloc(size_t size); void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void free(void *ptr); void free(void *ptr);
void *realloc(void *ptr, size_t size); void *realloc(void *ptr, size_t size);

View file

@ -5,7 +5,6 @@
struct dyld_info { struct dyld_info {
Elf32_Addr base; Elf32_Addr base;
const void *init;
const char *strtab; const char *strtab;
const Elf32_Sym *symtab; const Elf32_Sym *symtab;
struct { struct {
@ -21,7 +20,7 @@ extern "C" {
#endif #endif
int dyld_load(const void *shlib, Elf32_Addr base, 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); struct dyld_info *info, const char **error_out);
void *dyld_lookup(const char *symbol, struct dyld_info *info); void *dyld_lookup(const char *symbol, struct dyld_info *info);

View file

@ -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 <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*));

View file

@ -202,6 +202,8 @@ _exception_handler:
l.mfspr r5, r0, SPR_EPCR_BASE l.mfspr r5, r0, SPR_EPCR_BASE
/* Extract exception effective address */ /* Extract exception effective address */
l.mfspr r6, r0, SPR_EEAR_BASE 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 */ /* Call exception handler with the link address as argument */
l.jal exception_handler l.jal exception_handler
l.nop l.nop

View file

@ -1,19 +1,211 @@
#include <generated/csr.h>
#include <stdio.h>
#include <stdarg.h>
void isr(void); void isr(void);
#ifdef __or1k__ #ifdef __or1k__
#include <hw/flags.h>
#define EXTERNAL_IRQ 0x8 #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, 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, 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) { if(vect == EXTERNAL_IRQ) {
isr(); isr();
} else { } else {
/* Unhandled exception */ emerg_printf("\n *** Unhandled exception %d *** \n", vect);
for(;;); 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 #endif

View file

@ -21,6 +21,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <math.h>
/** /**
* vsnprintf - Format a string and place it in a buffer * vsnprintf - Format a string and place it in a buffer
@ -195,48 +196,37 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
#ifndef NO_FLOAT #ifndef NO_FLOAT
case 'g': case 'g':
case 'f': { case 'f': {
int m; double f, g;
double f;
int integer;
f = va_arg(args, double); f = va_arg(args, double);
if(f < 0.0) { if(f < 0.0) {
if(str < end)
*str = '-'; *str = '-';
str++; str++;
f = -f; f = -f;
} }
integer = f; g = pow(10.0, floor(log10(f)));
if(integer > 0) { if(g < 1.0) {
m = 1; if(str < end)
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'; *str = '0';
str++; str++;
} }
while(g >= 1.0) {
if(str < end) { if(str < end)
*str = '.'; *str = '0' + fmod(f/g, 10.0);
str++; str++;
g /= 10.0;
} }
for(i=0;i<6;i++) { if(str < end)
int n; *str = '.';
str++;
f = f*10.0; for(i=0;i<6;i++) {
n = f; f = fmod(f*10.0, 10.0);
f = f - n; if(str < end)
if(str >= end) break; *str = '0' + f;
*str = '0' + n;
str++; str++;
} }