mirror of
https://github.com/enjoy-digital/litex.git
synced 2025-01-04 09:52:26 -05:00
merge most of misoc 54e1ef82 and migen e93d0601 changes
This commit is contained in:
parent
2507eff890
commit
ff31959aea
41 changed files with 1269 additions and 404 deletions
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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 *
|
||||||
|
|
||||||
|
|
|
@ -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]:
|
else:
|
||||||
# both operands signed
|
return obs[0]
|
||||||
return max(obs[0][0], obs[1][0]) + 1, True
|
n, s = _bitwise_binary_bits_sign(*obs)
|
||||||
elif not obs[0][1] and obs[1][1]:
|
return n + 1, s
|
||||||
# first operand unsigned (add sign bit), second operand signed
|
|
||||||
return max(obs[0][0] + 1, obs[1][0]) + 1, True
|
|
||||||
else:
|
|
||||||
# first signed, second operand unsigned (add sign bit)
|
|
||||||
return max(obs[0][0], obs[1][0] + 1) + 1, True
|
|
||||||
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]:
|
return 1, False
|
||||||
# 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
|
|
||||||
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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)):
|
||||||
item_type, item_name = k.split("_", maxsplit=1)
|
try:
|
||||||
|
item_type, item_name = k.split("_", maxsplit=1)
|
||||||
|
except ValueError:
|
||||||
|
raise TypeError("Wrong format for value '" + str(k) +
|
||||||
|
"', format should be 'type_name'")
|
||||||
|
|
||||||
item_class = {
|
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)
|
|
||||||
|
|
|
@ -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":
|
||||||
|
@ -526,7 +540,7 @@ class Case(_Statement):
|
||||||
for k, v in cases.items():
|
for k, v in cases.items():
|
||||||
if isinstance(k, (bool, int)):
|
if isinstance(k, (bool, int)):
|
||||||
k = Constant(k)
|
k = Constant(k)
|
||||||
if (not isinstance(k, Constant)
|
if (not isinstance(k, Constant)
|
||||||
and not (isinstance(k, str) and k == "default")):
|
and not (isinstance(k, str) and k == "default")):
|
||||||
raise TypeError("Case object is not a Migen constant")
|
raise TypeError("Case object is not a Migen constant")
|
||||||
if not isinstance(v, _collections.Iterable):
|
if not isinstance(v, _collections.Iterable):
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
@ -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]))
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,7 +41,7 @@ class TimeManager:
|
||||||
else:
|
else:
|
||||||
high = False
|
high = False
|
||||||
self.clocks[k] = ClockState(high, half_period, half_period - phase)
|
self.clocks[k] = ClockState(high, half_period, half_period - phase)
|
||||||
|
|
||||||
def tick(self):
|
def tick(self):
|
||||||
rising = set()
|
rising = set()
|
||||||
falling = set()
|
falling = set()
|
||||||
|
@ -62,14 +64,14 @@ str2op = {
|
||||||
"+": operator.add,
|
"+": operator.add,
|
||||||
"-": operator.sub,
|
"-": operator.sub,
|
||||||
"*": operator.mul,
|
"*": operator.mul,
|
||||||
|
|
||||||
">>>": operator.rshift,
|
">>>": operator.rshift,
|
||||||
"<<<": operator.lshift,
|
"<<<": operator.lshift,
|
||||||
|
|
||||||
"&": operator.and_,
|
"&": operator.and_,
|
||||||
"^": operator.xor,
|
"^": operator.xor,
|
||||||
"|": operator.or_,
|
"|": operator.or_,
|
||||||
|
|
||||||
"<": operator.lt,
|
"<": operator.lt,
|
||||||
"<=": operator.le,
|
"<=": operator.le,
|
||||||
"==": operator.eq,
|
"==": operator.eq,
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
def define(k, v):
|
||||||
|
f.write("{}={}\n".format(k, _makefile_escape(v)))
|
||||||
|
for k, v in cpu_interface.get_cpu_mak(cpu_type):
|
||||||
|
define(k, v)
|
||||||
|
define("SOC_DIRECTORY", soc_directory)
|
||||||
|
define("BUILDINC_DIRECTORY", buildinc_dir)
|
||||||
|
f.write("export BUILDINC_DIRECTORY\n")
|
||||||
|
for name, src_dir in self.software_packages:
|
||||||
|
define(name.upper() + "_DIRECTORY", src_dir)
|
||||||
|
|
||||||
variables_contents = []
|
with open(os.path.join(generated_dir, "output_format.ld"), "w") as f:
|
||||||
def define(k, v):
|
f.write(cpu_interface.get_linker_output_format(cpu_type))
|
||||||
variables_contents.append("{}={}\n".format(k, _makefile_escape(v)))
|
with open(os.path.join(generated_dir, "regions.ld"), "w") as f:
|
||||||
for k, v in cpu_interface.get_cpu_mak(cpu_type):
|
f.write(cpu_interface.get_linker_regions(memory_regions))
|
||||||
define(k, v)
|
|
||||||
define("SOC_DIRECTORY", soc_directory)
|
|
||||||
define("BUILDINC_DIRECTORY", buildinc_dir)
|
|
||||||
for name, src_dir in self.software_packages:
|
|
||||||
define(name.upper() + "_DIRECTORY", src_dir)
|
|
||||||
write_to_file(
|
|
||||||
os.path.join(generated_dir, "variables.mak"),
|
|
||||||
"".join(variables_contents))
|
|
||||||
|
|
||||||
write_to_file(
|
with open(os.path.join(generated_dir, "mem.h"), "w") as f:
|
||||||
os.path.join(generated_dir, "output_format.ld"),
|
f.write(cpu_interface.get_mem_header(memory_regions, flash_boot_address))
|
||||||
cpu_interface.get_linker_output_format(cpu_type))
|
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, "regions.ld"),
|
|
||||||
cpu_interface.get_linker_regions(memory_regions))
|
|
||||||
|
|
||||||
write_to_file(
|
|
||||||
os.path.join(generated_dir, "mem.h"),
|
|
||||||
cpu_interface.get_mem_header(memory_regions, flash_boot_address))
|
|
||||||
write_to_file(
|
|
||||||
os.path.join(generated_dir, "csr.h"),
|
|
||||||
cpu_interface.get_csr_header(csr_regions, constants))
|
|
||||||
|
|
||||||
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:
|
||||||
|
|
|
@ -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
|
triple = "or1k-linux"
|
||||||
if clang is None:
|
cpuflags = "-mhard-mul -mhard-div -mror -mffl1 -maddc"
|
||||||
clang = True
|
clang = "1"
|
||||||
triple = "or1k-elf"
|
|
||||||
cpuflags = "-mhard-mul -mhard-div -mror"
|
|
||||||
if clang:
|
|
||||||
triple = "or1k-linux"
|
|
||||||
cpuflags += "-mffl1 -maddc"
|
|
||||||
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"
|
||||||
if isinstance(value, str):
|
continue
|
||||||
r += " \"" + value + "\""
|
if isinstance(value, str):
|
||||||
else:
|
value = "\"" + value + "\""
|
||||||
r += " " + str(value)
|
ctype = "const char *"
|
||||||
r += "\n"
|
else:
|
||||||
|
value = str(value)
|
||||||
|
ctype = "int"
|
||||||
|
r += "#define "+name+" "+value+"\n"
|
||||||
|
r += "static inline "+ctype+" "+name.lower()+"_read(void) {\n"
|
||||||
|
r += "\treturn "+value+";\n}\n"
|
||||||
|
|
||||||
r += "\n#endif\n"
|
r += "\n#endif\n"
|
||||||
return r
|
return r
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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]))
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -465,7 +462,7 @@ class Cache(Module):
|
||||||
self.master = master
|
self.master = master
|
||||||
self.slave = slave
|
self.slave = slave
|
||||||
|
|
||||||
###
|
# # #
|
||||||
|
|
||||||
dw_from = len(master.dat_r)
|
dw_from = len(master.dat_r)
|
||||||
dw_to = len(slave.dat_r)
|
dw_to = len(slave.dat_r)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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"
|
||||||
|
|
14
litex/soc/software/include/base/math.h
Normal file
14
litex/soc/software/include/base/math.h
Normal 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 */
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
216
litex/soc/software/include/fdlibm/fdlibm.h
Normal file
216
litex/soc/software/include/fdlibm/fdlibm.h
Normal 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*));
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
@ -109,7 +110,7 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
|
||||||
/* get the precision */
|
/* get the precision */
|
||||||
precision = -1;
|
precision = -1;
|
||||||
if (*fmt == '.') {
|
if (*fmt == '.') {
|
||||||
++fmt;
|
++fmt;
|
||||||
if (isdigit(*fmt))
|
if (isdigit(*fmt))
|
||||||
precision = skip_atoi(&fmt);
|
precision = skip_atoi(&fmt);
|
||||||
else if (*fmt == '*') {
|
else if (*fmt == '*') {
|
||||||
|
@ -195,51 +196,40 @@ 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) {
|
||||||
*str = '-';
|
if(str < end)
|
||||||
|
*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;
|
*str = '0';
|
||||||
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++;
|
str++;
|
||||||
}
|
}
|
||||||
|
while(g >= 1.0) {
|
||||||
|
if(str < end)
|
||||||
|
*str = '0' + fmod(f/g, 10.0);
|
||||||
|
str++;
|
||||||
|
g /= 10.0;
|
||||||
|
}
|
||||||
|
|
||||||
if(str < end) {
|
if(str < end)
|
||||||
*str = '.';
|
*str = '.';
|
||||||
str++;
|
str++;
|
||||||
}
|
|
||||||
|
|
||||||
for(i=0;i<6;i++) {
|
for(i=0;i<6;i++) {
|
||||||
int n;
|
f = fmod(f*10.0, 10.0);
|
||||||
|
if(str < end)
|
||||||
f = f*10.0;
|
*str = '0' + f;
|
||||||
n = f;
|
|
||||||
f = f - n;
|
|
||||||
if(str >= end) break;
|
|
||||||
*str = '0' + n;
|
|
||||||
str++;
|
str++;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue