Bring into line with master

This commit is contained in:
Dave Marples 2020-05-12 12:28:09 +01:00
commit 33e202edd4
10 changed files with 208 additions and 60 deletions

View File

@ -4,7 +4,9 @@
# License: BSD
import os
import re
import sys
import math
import subprocess
import shutil
@ -40,7 +42,7 @@ def _format_lpf(signame, pin, others, resname):
return "\n".join(lpf)
def _build_lpf(named_sc, named_pc, build_name):
def _build_lpf(named_sc, named_pc, clocks, vns, build_name):
lpf = []
lpf.append("BLOCK RESETPATHS;")
lpf.append("BLOCK ASYNCPATHS;")
@ -51,7 +53,16 @@ def _build_lpf(named_sc, named_pc, build_name):
else:
lpf.append(_format_lpf(sig, pins[0], others, resname))
if named_pc:
lpf.append("\n\n".join(named_pc))
lpf.append("\n".join(named_pc))
# Note: .lpf is only used post-synthesis, Synplify constraints clocks by default to 200MHz.
for clk, period in clocks.items():
clk_name = vns.get_name(clk)
lpf.append("FREQUENCY {} \"{}\" {} MHz;".format(
"PORT" if clk_name in [name for name, _, _, _ in named_sc] else "NET",
clk_name,
str(1e3/period)))
tools.write_to_file(build_name + ".lpf", "\n".join(lpf))
# Project (.tcl) -----------------------------------------------------------------------------------
@ -67,13 +78,15 @@ def _build_tcl(device, sources, vincpaths, build_name):
"-synthesis \"synplify\""
]))
def tcl_path(path): return path.replace("\\", "/")
# Add include paths
vincpath = ';'.join(map(lambda x: x.replace('\\', '/'), vincpaths))
vincpath = ";".join(map(lambda x: tcl_path(x), vincpaths))
tcl.append("prj_impl option {include path} {\"" + vincpath + "\"}")
# Add sources
for filename, language, library in sources:
tcl.append("prj_src add \"" + filename.replace("\\", "/") + "\" -work " + library)
tcl.append("prj_src add \"{}\" -work {}".format(tcl_path(filename), library))
# Set top level
tcl.append("prj_impl option top \"{}\"".format(build_name))
@ -89,6 +102,10 @@ def _build_tcl(device, sources, vincpaths, build_name):
tcl.append("prj_run Export -impl impl -task Bitgen")
if _produces_jedec(device):
tcl.append("prj_run Export -impl impl -task Jedecgen")
# Close project
tcl.append("prj_project close")
tools.write_to_file(build_name + ".tcl", "\n".join(tcl))
# Script -------------------------------------------------------------------------------------------
@ -130,6 +147,34 @@ def _run_script(script):
if subprocess.call(shell + [script]) != 0:
raise OSError("Subprocess failed")
def _check_timing(build_name):
lines = open("impl/{}_impl.par".format(build_name), "r").readlines()
runs = [None, None]
for i in range(len(lines)-1):
if lines[i].startswith("Level/") and lines[i+1].startswith("Cost "):
runs[0] = i + 2
if lines[i].startswith("* : Design saved.") and runs[0] is not None:
runs[1] = i
break
assert all(map(lambda x: x is not None, runs))
p = re.compile(r"(^\s*\S+\s+\*?\s+[0-9]+\s+)(\S+)(\s+\S+\s+)(\S+)(\s+.*)")
for l in lines[runs[0]:runs[1]]:
m = p.match(l)
if m is None: continue
limit = 1e-8
setup = m.group(2)
hold = m.group(4)
# If there were no freq constraints in lpf, ratings will be dashed.
# results will likely be terribly unreliable, so bail
assert not setup == hold == "-", "No timing constraints were provided"
setup, hold = map(float, (setup, hold))
if setup > limit and hold > limit:
# At least one run met timing
# XXX is this necessarily the run from which outputs will be used?
return
raise Exception("Failed to meet timing")
# LatticeDiamondToolchain --------------------------------------------------------------------------
class LatticeDiamondToolchain:
@ -149,12 +194,14 @@ class LatticeDiamondToolchain:
special_overrides = common.lattice_ecp5_special_overrides
def __init__(self):
self.clocks = {}
self.false_paths = set() # FIXME: use it
def build(self, platform, fragment,
build_dir = "build",
build_name = "top",
run = True,
timingstrict = True,
**kwargs):
# Create build directory
@ -175,7 +222,7 @@ class LatticeDiamondToolchain:
platform.add_source(v_file)
# Generate design constraints file (.lpf)
_build_lpf(named_sc, named_pc, build_name)
_build_lpf(named_sc, named_pc, self.clocks, v_output.ns, build_name)
# Generate design script file (.tcl)
_build_tcl(platform.device, platform.sources, platform.verilog_include_paths, build_name)
@ -186,6 +233,8 @@ class LatticeDiamondToolchain:
# Run
if run:
_run_script(script)
if timingstrict:
_check_timing(build_name)
os.chdir(cwd)
@ -193,9 +242,12 @@ class LatticeDiamondToolchain:
def add_period_constraint(self, platform, clk, period):
clk.attr.add("keep")
# TODO: handle differential clk
platform.add_platform_command("""FREQUENCY PORT "{clk}" {freq} MHz;""".format(
freq=str(float(1/period)*1000), clk="{clk}"), clk=clk)
period = math.floor(period*1e3)/1e3 # round to lowest picosecond
if clk in self.clocks:
if period != self.clocks[clk]:
raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns"
.format(self.clocks[clk], period))
self.clocks[clk] = period
def add_false_path_constraint(self, platform, from_, to):
from_.attr.add("keep")

View File

@ -93,32 +93,35 @@ def parse_device(device):
_build_template = [
"yosys -l {build_name}.rpt {build_name}.ys",
"nextpnr-ice40 --json {build_name}.json --pcf {build_name}.pcf --asc {build_name}.txt \
--pre-pack {build_name}_pre_pack.py --{architecture} --package {package} {timefailarg}",
--pre-pack {build_name}_pre_pack.py --{architecture} --package {package} {timefailarg} {ignoreloops} --seed {seed}",
"icepack -s {build_name}.txt {build_name}.bin"
]
def _build_script(build_template, build_name, architecture, package, timingstrict):
def _build_script(build_template, build_name, architecture, package, timingstrict, ignoreloops, seed):
if sys.platform in ("win32", "cygwin"):
script_ext = ".bat"
build_script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n"
script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n"
fail_stmt = " || exit /b"
else:
script_ext = ".sh"
build_script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n"
script_contents = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\nset -e\n"
fail_stmt = ""
for s in build_template:
s_fail = s + "{fail_stmt}\n" # Required so Windows scripts fail early.
build_script_contents += s_fail.format(
script_contents += s_fail.format(
build_name = build_name,
architecture = architecture,
package = package,
timefailarg = "--timing-allow-fail" if not timingstrict else "",
fail_stmt = fail_stmt)
ignoreloops = "--ignore-loops" if ignoreloops else "",
fail_stmt = fail_stmt,
seed = seed)
build_script_file = "build_" + build_name + script_ext
tools.write_to_file(build_script_file, build_script_contents,force_unix=False)
return build_script_file
script_file = "build_" + build_name + script_ext
tools.write_to_file(script_file, script_contents, force_unix=False)
return script_file
def _run_script(script):
if sys.platform in ("win32", "cygwin"):
@ -158,6 +161,8 @@ class LatticeIceStormToolchain:
synth_opts = "",
run = True,
timingstrict = False,
ignoreloops = False,
seed = 1,
**kwargs):
# Create build directory
@ -190,7 +195,7 @@ class LatticeIceStormToolchain:
(family, architecture, package) = parse_device(platform.device)
# Generate build script
script = _build_script(self.build_template, build_name, architecture, package, timingstrict)
script = _build_script(self.build_template, build_name, architecture, package, timingstrict, ignoreloops, seed)
# Run
if run:
@ -207,3 +212,18 @@ class LatticeIceStormToolchain:
raise ValueError("Clock already constrained to {:.2f}ns, new constraint to {:.2f}ns"
.format(self.clocks[clk], period))
self.clocks[clk] = period
def icestorm_args(parser):
parser.add_argument("--nextpnr-timingstrict", action="store_true",
help="fail if timing not met, i.e., do NOT pass '--timing-allow-fail' to nextpnr")
parser.add_argument("--nextpnr-ignoreloops", action="store_true",
help="ignore combinational loops in timing analysis, i.e. pass '--ignore-loops' to nextpnr")
parser.add_argument("--nextpnr-seed", default=1, type=int,
help="seed to pass to nextpnr")
def icestorm_argdict(args):
return {
"timingstrict": args.nextpnr_timingstrict,
"ignoreloops": args.nextpnr_ignoreloops,
"seed": args.nextpnr_seed,
}

View File

@ -117,11 +117,11 @@ nextpnr_ecp5_architectures = {
_build_template = [
"yosys -l {build_name}.rpt {build_name}.ys",
"nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config \
--{architecture} --package {package} --speed {speed_grade} {timefailarg} {ignoreloops}",
"ecppack {build_name}.config --svf {build_name}.svf --bit {build_name}.bit --bootaddr={bootaddr}"
--{architecture} --package {package} --speed {speed_grade} {timefailarg} {ignoreloops} --seed {seed}",
"ecppack {build_name}.config --svf {build_name}.svf --bit {build_name}.bit"
]
def _build_script(source, build_template, build_name, architecture, package, speed_grade, timingstrict, ignoreloops, bootaddr):
def _build_script(source, build_template, build_name, architecture, package, speed_grade, timingstrict, ignoreloops, bootaddr, seed):
if sys.platform in ("win32", "cygwin"):
script_ext = ".bat"
script_contents = "@echo off\nrem Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n\n"
@ -141,7 +141,8 @@ def _build_script(source, build_template, build_name, architecture, package, spe
timefailarg = "--timing-allow-fail" if not timingstrict else "",
ignoreloops = "--ignore-loops" if ignoreloops else "",
bootaddr = bootaddr,
fail_stmt = fail_stmt)
fail_stmt = fail_stmt,
seed = seed)
script_file = "build_" + build_name + script_ext
tools.write_to_file(script_file, script_contents, force_unix=False)
@ -188,6 +189,7 @@ class LatticeTrellisToolchain:
timingstrict = False,
ignoreloops = False,
bootaddr = 0,
seed = 1,
**kwargs):
# Create build directory
@ -219,8 +221,7 @@ class LatticeTrellisToolchain:
# Generate build script
script = _build_script(False, self.build_template, build_name, architecture, package,
speed_grade, timingstrict, ignoreloops, bootaddr)
speed_grade, timingstrict, ignoreloops, bootaddr, seed)
# Run
if run:
_run_script(script)
@ -248,11 +249,14 @@ def trellis_args(parser):
help="ignore combinational loops in timing analysis, i.e. pass '--ignore-loops' to nextpnr")
parser.add_argument("--ecppack-bootaddr", default=0,
help="Set boot address for next image, i.e. pass '--bootaddr xxx' to ecppack")
parser.add_argument("--nextpnr-seed", default=1, type=int,
help="seed to pass to nextpnr")
def trellis_argdict(args):
return {
"nowidelut": args.yosys_nowidelut,
"timingstrict": args.nextpnr_timingstrict,
"ignoreloops": args.nextpnr_ignoreloops,
"bootaddr": args.ecppack_bootaddr
"bootaddr": args.ecppack_bootaddr,
"seed": args.nextpnr_seed,
}

View File

@ -88,6 +88,7 @@ CPU_VARIANTS = {
"minimal" : ["min",],
"lite" : ["light", "zephyr", "nuttx"],
"standard": [None, "std"],
"imac": [],
"full": [],
"linux" : [],
"linuxd" : [],

View File

@ -25,6 +25,8 @@ CPU_VARIANTS = {
"lite+debug": "VexRiscv_LiteDebug",
"standard": "VexRiscv",
"standard+debug": "VexRiscv_Debug",
"imac": "VexRiscv_IMAC",
"imac+debug": "VexRiscv_IMACDebug",
"full": "VexRiscv_Full",
"full+debug": "VexRiscv_FullDebug",
"linux": "VexRiscv_Linux",
@ -47,6 +49,8 @@ GCC_FLAGS = {
"lite+debug": "-march=rv32i -mabi=ilp32",
"standard": "-march=rv32im -mabi=ilp32",
"standard+debug": "-march=rv32im -mabi=ilp32",
"imac": "-march=rv32imac -mabi=ilp32",
"imac+debug": "-march=rv32imac -mabi=ilp32",
"full": "-march=rv32im -mabi=ilp32",
"full+debug": "-march=rv32im -mabi=ilp32",
"linux": "-march=rv32ima -mabi=ilp32",

34
litex/soc/cores/led.py Normal file
View File

@ -0,0 +1,34 @@
# This file is Copyright (c) 2020 Florent Kermarrec <florent@enjoy-digital.fr>
# License: BSD
from migen import *
from migen.genlib.misc import WaitTimer
from litex.soc.interconnect.csr import *
# Led Chaser ---------------------------------------------------------------------------------------
_CHASER_MODE = 0
_CONTROL_MODE = 1
class LedChaser(Module, AutoCSR):
def __init__(self, pads, sys_clk_freq, period=1e0):
self._out = CSRStorage(len(pads), description="Led Output(s) Control.")
# # #
n = len(pads)
chaser = Signal(n)
mode = Signal(reset=_CHASER_MODE)
timer = WaitTimer(int(period*sys_clk_freq/(2*n)))
self.submodules += timer
self.comb += timer.wait.eq(~timer.done)
self.sync += If(timer.done, chaser.eq(Cat(~chaser[-1], chaser)))
self.sync += If(self._out.re, mode.eq(_CONTROL_MODE))
self.comb += [
If(mode == _CONTROL_MODE,
pads.eq(self._out.storage)
).Else(
pads.eq(chaser)
)
]

View File

@ -239,11 +239,13 @@ class UART(Module, AutoCSR, UARTInterface):
self.ev.rx.trigger.eq(~rx_fifo.source.valid)
]
class UARTWishboneBridge(WishboneStreamingBridge):
class UARTBone(WishboneStreamingBridge):
def __init__(self, pads, clk_freq, baudrate=115200):
self.submodules.phy = RS232PHY(pads, clk_freq, baudrate)
WishboneStreamingBridge.__init__(self, self.phy, clk_freq)
class UARTWishboneBridge(UARTBone): pass
# UART Multiplexer ---------------------------------------------------------------------------------
class UARTMultiplexer(Module):

View File

@ -282,7 +282,8 @@ class SoCBusHandler(Module):
return is_io
# Add Master/Slave -----------------------------------------------------------------------------
def add_adapter(self, name, interface):
def add_adapter(self, name, interface, direction="m2s"):
assert direction in ["m2s", "s2m"]
if interface.data_width != self.data_width:
self.logger.info("{} Bus {} from {}-bit to {}-bit.".format(
colorer(name),
@ -290,7 +291,11 @@ class SoCBusHandler(Module):
colorer(interface.data_width),
colorer(self.data_width)))
new_interface = wishbone.Interface(data_width=self.data_width)
self.submodules += wishbone.Converter(interface, new_interface)
if direction == "m2s":
converter = wishbone.Converter(master=interface, slave=new_interface)
if direction == "s2m":
converter = wishbone.Converter(master=new_interface, slave=interface)
self.submodules += converter
return new_interface
else:
return interface
@ -304,7 +309,7 @@ class SoCBusHandler(Module):
colorer("already declared", color="red")))
self.logger.error(self)
raise
master = self.add_adapter(name, master)
master = self.add_adapter(name, master, "m2s")
self.masters[name] = master
self.logger.info("{} {} as Bus Master.".format(
colorer(name, color="underline"),
@ -336,7 +341,7 @@ class SoCBusHandler(Module):
colorer("already declared", color="red")))
self.logger.error(self)
raise
slave = self.add_adapter(name, slave)
slave = self.add_adapter(name, slave, "s2m")
self.slaves[name] = slave
self.logger.info("{} {} as Bus Slave.".format(
colorer(name, color="underline"),
@ -753,8 +758,8 @@ class SoC(Module):
def add_csr_bridge(self, origin):
self.submodules.csr_bridge = wishbone2csr.WB2CSR(
bus_csr = csr_bus.Interface(
address_width = self.csr.address_width,
data_width = self.csr.data_width))
address_width = self.csr.address_width,
data_width = self.csr.data_width))
csr_size = 2**(self.csr.address_width + 2)
csr_region = SoCRegion(origin=origin, size=csr_size, cached=False)
self.bus.add_slave("csr", self.csr_bridge.wishbone, csr_region)
@ -926,13 +931,9 @@ class LiteXSoC(SoC):
if name == "stub":
self.comb += self.uart.sink.ready.eq(1)
# Bridge
elif name in ["bridge"]:
self.submodules.uart = uart.UARTWishboneBridge(
pads = self.platform.request("serial"),
clk_freq = self.sys_clk_freq,
baudrate = baudrate)
self.bus.add_master(name="uart_bridge", master=self.uart.wishbone)
# UARTBone / Bridge
elif name in ["uartbone", "bridge"]:
self.add_uartbone(baudrate=baudrate)
# Crossover
elif name in ["crossover"]:
@ -986,6 +987,15 @@ class LiteXSoC(SoC):
else:
self.add_constant("UART_POLLING")
# Add UARTbone ---------------------------------------------------------------------------------
def add_uartbone(self, name="serial", baudrate=115200):
from litex.soc.cores import uart
self.submodules.uartbone = uart.UARTBone(
pads = self.platform.request(name),
clk_freq = self.sys_clk_freq,
baudrate = baudrate)
self.bus.add_master(name="uartbone", master=self.uartbone.wishbone)
# Add SDRAM ------------------------------------------------------------------------------------
def add_sdram(self, name, phy, module, origin, size=None,
l2_cache_size = 8192,
@ -1133,7 +1143,7 @@ class LiteXSoC(SoC):
eth_tx_clk)
# Add Etherbone --------------------------------------------------------------------------------
def add_etherbone(self, name="etherbone", phy=None,
def add_etherbone(self, name="etherbone", phy=None, clock_domain=None,
mac_address = 0x10e2d5000000,
ip_address = "192.168.1.50",
udp_port = 1234):
@ -1146,9 +1156,21 @@ class LiteXSoC(SoC):
mac_address = mac_address,
ip_address = ip_address,
clk_freq = self.clk_freq)
if clock_domain is not None: # FIXME: Could probably be avoided.
ethcore = ClockDomainsRenamer("eth_tx")(ethcore)
self.submodules += ethcore
# Clock domain renaming
if clock_domain is not None: # FIXME: Could probably be avoided.
self.clock_domains.cd_etherbone = ClockDomain("etherbone")
self.comb += self.cd_etherbone.clk.eq(ClockSignal(clock_domain))
self.comb += self.cd_etherbone.rst.eq(ResetSignal(clock_domain))
clock_domain = "etherbone"
else:
clock_domain = "sys"
# Etherbone
etherbone = LiteEthEtherbone(ethcore.udp, udp_port)
etherbone = LiteEthEtherbone(ethcore.udp, udp_port, cd=clock_domain)
setattr(self.submodules, name, etherbone)
self.add_wb_master(etherbone.wishbone.bus)
# Timing constraints
@ -1168,10 +1190,10 @@ class LiteXSoC(SoC):
# Add SPI Flash --------------------------------------------------------------------------------
def add_spi_flash(self, name="spiflash", mode="4x", dummy_cycles=None, clk_freq=None):
assert dummy_cycles is not None # FIXME: Get dummy_cycles from SPI Flash
assert mode in ["4x"] # FIXME: Add 1x support.
assert mode in ["1x", "4x"]
if clk_freq is None: clk_freq = self.clk_freq/2 # FIXME: Get max clk_freq from SPI Flash
spiflash = SpiFlash(
pads = self.platform.request(name + mode),
pads = self.platform.request(name if mode == "1x" else name + mode),
dummy = dummy_cycles,
div = ceil(self.clk_freq/clk_freq),
with_bitbang = True,

View File

@ -61,6 +61,11 @@ class SoCCore(LiteXSoC):
}
def __init__(self, platform, clk_freq,
# Bus parameters
bus_standard = "wishbone",
bus_data_width = 32,
bus_address_width = 32,
bus_timeout = 1e6,
# CPU parameters
cpu_type = "vexriscv",
cpu_reset_address = None,
@ -92,18 +97,15 @@ class SoCCore(LiteXSoC):
with_timer = True,
# Controller parameters
with_ctrl = True,
# Wishbone parameters
with_wishbone = True,
wishbone_timeout_cycles = 1e6,
# Others
**kwargs):
# New LiteXSoC class ----------------------------------------------------------------------------
LiteXSoC.__init__(self, platform, clk_freq,
bus_standard = "wishbone",
bus_data_width = 32,
bus_address_width = 32,
bus_timeout = wishbone_timeout_cycles,
bus_standard = bus_standard,
bus_data_width = bus_data_width,
bus_address_width = bus_address_width,
bus_timeout = bus_timeout,
bus_reserved_regions = {},
csr_data_width = csr_data_width,
@ -127,9 +129,6 @@ class SoCCore(LiteXSoC):
cpu_reset_address = None if cpu_reset_address == "None" else cpu_reset_address
cpu_variant = cpu.check_format_cpu_variant(cpu_variant)
if not with_wishbone:
self.mem_map["csr"] = 0x00000000
self.cpu_type = cpu_type
self.cpu_variant = cpu_variant
self.cpu_cls = cpu_cls
@ -141,9 +140,6 @@ class SoCCore(LiteXSoC):
self.csr_data_width = csr_data_width
self.with_wishbone = with_wishbone
self.wishbone_timeout_cycles = wishbone_timeout_cycles
self.wb_slaves = {}
# Modules instances ------------------------------------------------------------------------
@ -187,9 +183,8 @@ class SoCCore(LiteXSoC):
if with_timer:
self.add_timer(name="timer0")
# Add Wishbone to CSR bridge
if with_wishbone:
self.add_csr_bridge(self.mem_map["csr"])
# Add CSR bridge
self.add_csr_bridge(self.mem_map["csr"])
# Methods --------------------------------------------------------------------------------------

View File

@ -191,6 +191,13 @@ void sdrwlon(void)
sdram_dfii_pi0_address_write(DDRX_MR1 | (1 << 7));
sdram_dfii_pi0_baddress_write(1);
command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS);
#ifdef SDRAM_PHY_DDR4_RDIMM
sdram_dfii_pi0_address_write((DDRX_MR1 | (1 << 7)) ^ 0x2BF8) ;
sdram_dfii_pi0_baddress_write(1 ^ 0xF);
command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS);
#endif
ddrphy_wlevel_en_write(1);
}
@ -199,6 +206,13 @@ void sdrwloff(void)
sdram_dfii_pi0_address_write(DDRX_MR1);
sdram_dfii_pi0_baddress_write(1);
command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS);
#ifdef SDRAM_PHY_DDR4_RDIMM
sdram_dfii_pi0_address_write(DDRX_MR1 ^ 0x2BF8);
sdram_dfii_pi0_baddress_write(1 ^ 0xF);
command_p0(DFII_COMMAND_RAS|DFII_COMMAND_CAS|DFII_COMMAND_WE|DFII_COMMAND_CS);
#endif
ddrphy_wlevel_en_write(0);
}
@ -903,7 +917,7 @@ static void memspeed(void)
end = timer0_value_read();
read_speed = (8*MEMTEST_DATA_SIZE*(CONFIG_CLOCK_FREQUENCY/1000000))/(start - end);
printf("Memspeed Writes: %dMbps Reads: %dMbps\n", write_speed, read_speed);
printf("Memspeed Writes: %ldMbps Reads: %ldMbps\n", write_speed, read_speed);
}
int memtest(void)