diff --git a/LICENSE b/LICENSE index fc78898b6..4ec4486d5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,14 +1,14 @@ -Unless otherwise noted, LiteX is copyright (C) 2012-2018 Florent Kermarrec. +Unless otherwise noted, LiteX is copyright (C) 2012-2019 Florent Kermarrec. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. + list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. + and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED diff --git a/README b/README index 388858ca6..ee4d46c7e 100644 --- a/README +++ b/README @@ -5,7 +5,7 @@ Migen inside Build your hardware, easily! - Copyright 2012-2018 / EnjoyDigital + Copyright 2012-2019 / EnjoyDigital [> Intro -------- diff --git a/litex/boards/targets/kcu105.py b/litex/boards/targets/kcu105.py index f496e53e0..502af29ee 100755 --- a/litex/boards/targets/kcu105.py +++ b/litex/boards/targets/kcu105.py @@ -14,6 +14,9 @@ from litex.soc.integration.builder import * from litedram.modules import EDY4016A from litedram.phy import usddrphy +from liteeth.phy.ku_1000basex import KU_1000BASEX +from liteeth.core.mac import LiteEthMAC + # CRG ---------------------------------------------------------------------------------------------- class _CRG(Module): @@ -92,15 +95,61 @@ class BaseSoC(SoCSDRAM): sdram_module.geom_settings, sdram_module.timing_settings) + +# EthernetSoC ------------------------------------------------------------------------------------------ + +class EthernetSoC(BaseSoC): + csr_map = { + "ethphy": 18, + "ethmac": 19 + } + csr_map.update(BaseSoC.csr_map) + + interrupt_map = { + "ethmac": 3, + } + interrupt_map.update(BaseSoC.interrupt_map) + + mem_map = { + "ethmac": 0x30000000, # (shadow @0xb0000000) + } + mem_map.update(BaseSoC.mem_map) + + def __init__(self, **kwargs): + BaseSoC.__init__(self, **kwargs) + + self.comb += self.platform.request("sfp_tx_disable_n", 0).eq(1) + self.submodules.ethphy = KU_1000BASEX(self.crg.cd_clk200.clk, + self.platform.request("sfp", 0), sys_clk_freq=self.clk_freq) + self.submodules.ethmac = LiteEthMAC(phy=self.ethphy, dw=32, + interface="wishbone", endianness=self.cpu.endianness) + self.add_wb_slave(mem_decoder(self.mem_map["ethmac"]), self.ethmac.bus) + self.add_memory_region("ethmac", self.mem_map["ethmac"] | self.shadow_base, 0x2000) + + self.crg.cd_sys.clk.attr.add("keep") + self.ethphy.cd_eth_rx.clk.attr.add("keep") + self.ethphy.cd_eth_tx.clk.attr.add("keep") + self.platform.add_period_constraint(self.ethphy.cd_eth_rx.clk, 1e9/125e6) + self.platform.add_period_constraint(self.ethphy.cd_eth_tx.clk, 1e9/125e6) + self.platform.add_false_path_constraints( + self.crg.cd_sys.clk, + self.ethphy.cd_eth_rx.clk, + self.ethphy.cd_eth_tx.clk) + + self.platform.add_platform_command("set_property SEVERITY {{Warning}} [get_drc_checks REQP-1753]") + # Build -------------------------------------------------------------------------------------------- def main(): parser = argparse.ArgumentParser(description="LiteX SoC on KCU105") builder_args(parser) soc_sdram_args(parser) + parser.add_argument("--with-ethernet", action="store_true", + help="enable Ethernet support") args = parser.parse_args() - soc = BaseSoC(**soc_sdram_argdict(args)) + cls = EthernetSoC if args.with_ethernet else BaseSoC + soc = cls(**soc_sdram_argdict(args)) builder = Builder(soc, **builder_argdict(args)) builder.build() diff --git a/litex/boards/targets/versa_ecp5.py b/litex/boards/targets/versa_ecp5.py index e670499eb..f82e42e80 100755 --- a/litex/boards/targets/versa_ecp5.py +++ b/litex/boards/targets/versa_ecp5.py @@ -13,7 +13,10 @@ from litex.soc.integration.soc_sdram import * from litex.soc.integration.builder import * from litedram.modules import MT41K64M16 -from litedram.phy import ECP5DDRPHY, ECP5DDRPHYInit +from litedram.phy import ECP5DDRPHY + +from liteeth.phy.ecp5rgmii import LiteEthPHYRGMII +from liteeth.core.mac import LiteEthMAC # CRG ---------------------------------------------------------------------------------------------- @@ -70,7 +73,7 @@ class BaseSoC(SoCSDRAM): csr_map.update(SoCSDRAM.csr_map) def __init__(self, toolchain="diamond", **kwargs): platform = versa_ecp5.Platform(toolchain=toolchain) - sys_clk_freq = int(50e6) + sys_clk_freq = int(75e6) SoCSDRAM.__init__(self, platform, clk_freq=sys_clk_freq, integrated_rom_size=0x8000, **kwargs) @@ -84,24 +87,61 @@ class BaseSoC(SoCSDRAM): platform.request("ddram"), sys_clk_freq=sys_clk_freq) self.add_constant("ECP5DDRPHY", None) - ddrphy_init = ECP5DDRPHYInit(self.crg, self.ddrphy) - self.submodules += ddrphy_init + self.comb += crg.stop.eq(self.ddrphy.init.stop) sdram_module = MT41K64M16(sys_clk_freq, "1:2") self.register_sdram(self.ddrphy, sdram_module.geom_settings, sdram_module.timing_settings) +# EthernetSoC -------------------------------------------------------------------------------------- + +class EthernetSoC(BaseSoC): + csr_map = { + "ethphy": 18, + "ethmac": 19 + } + csr_map.update(BaseSoC.csr_map) + + interrupt_map = { + "ethmac": 3, + } + interrupt_map.update(BaseSoC.interrupt_map) + + mem_map = { + "ethmac": 0x30000000, # (shadow @0xb0000000) + } + mem_map.update(BaseSoC.mem_map) + + def __init__(self, toolchain="diamond", **kwargs): + BaseSoC.__init__(self, toolchain=toolchain, **kwargs) + + self.submodules.ethphy = LiteEthPHYRGMII( + self.platform.request("eth_clocks"), + self.platform.request("eth")) + self.submodules.ethmac = LiteEthMAC(phy=self.ethphy, dw=32, + interface="wishbone", endianness=self.cpu.endianness) + 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.ethphy.crg.cd_eth_rx.clk.attr.add("keep") + self.ethphy.crg.cd_eth_tx.clk.attr.add("keep") + self.platform.add_period_constraint(self.ethphy.crg.cd_eth_rx.clk, 1e9/125e6) + self.platform.add_period_constraint(self.ethphy.crg.cd_eth_tx.clk, 1e9/125e6) + # Build -------------------------------------------------------------------------------------------- def main(): - parser = argparse.ArgumentParser(description="LiteX SoC on ECP5") + parser = argparse.ArgumentParser(description="LiteX SoC on Versa ECP5") parser.add_argument("--gateware-toolchain", dest="toolchain", default="diamond", help='gateware toolchain to use, diamond (default) or trellis') builder_args(parser) soc_sdram_args(parser) + parser.add_argument("--with-ethernet", action="store_true", + help="enable Ethernet support") args = parser.parse_args() - soc = BaseSoC(toolchain=args.toolchain, **soc_sdram_argdict(args)) + cls = EthernetSoC if args.with_ethernet else BaseSoC + soc = cls(args.toolchain, **soc_sdram_argdict(args)) builder = Builder(soc, **builder_argdict(args)) builder.build() diff --git a/litex/build/altera/common.py b/litex/build/altera/common.py index fafb49837..5d5739100 100644 --- a/litex/build/altera/common.py +++ b/litex/build/altera/common.py @@ -1,6 +1,9 @@ from migen.fhdl.module import Module from migen.fhdl.specials import Instance from migen.genlib.io import DifferentialInput, DifferentialOutput +from migen.genlib.resetsync import AsyncResetSynchronizer + +from migen.fhdl.structure import * class AlteraDifferentialInputImpl(Module): @@ -32,8 +35,29 @@ class AlteraDifferentialOutput: def lower(dr): return AlteraDifferentialOutputImpl(dr.i, dr.o_p, dr.o_n) +class AlteraAsyncResetSynchronizerImpl(Module): + def __init__(self, cd, async_reset): + if not hasattr(async_reset, "attr"): + i, async_reset = async_reset, Signal() + self.comb += async_reset.eq(i) + rst_meta = Signal() + self.specials += [ + Instance("DFF", i_d=0, i_clk=cd.clk, i_clrn=1, + i_prn=async_reset, o_q=rst_meta, + attr={"async_reg", "ars_ff1"}), + Instance("DFF", i_d=rst_meta, i_clk=cd.clk, i_clrn=1, + i_prn=async_reset, o_q=cd.rst, + attr={"async_reg", "ars_ff2"}) + ] + +class AlteraAsyncResetSynchronizer: + @staticmethod + def lower(dr): + return AlteraAsyncResetSynchronizerImpl(dr.cd, dr.async_reset) + altera_special_overrides = { DifferentialInput: AlteraDifferentialInput, - DifferentialOutput: AlteraDifferentialOutput + DifferentialOutput: AlteraDifferentialOutput, + AsyncResetSynchronizer: AlteraAsyncResetSynchronizer } diff --git a/litex/build/altera/platform.py b/litex/build/altera/platform.py index bccab35d2..332f60d73 100644 --- a/litex/build/altera/platform.py +++ b/litex/build/altera/platform.py @@ -25,3 +25,10 @@ class AlteraPlatform(GenericPlatform): if hasattr(clk, "p"): clk = clk.p self.toolchain.add_period_constraint(self, clk, period) + + def add_false_path_constraint(self, from_, to): + if hasattr(from_, "p"): + from_ = from_.p + if hasattr(to, "p"): + to = to.p + self.toolchain.add_false_path_constraint(self, from_, to) diff --git a/litex/build/altera/quartus.py b/litex/build/altera/quartus.py index 1ebc08da6..2aa025567 100644 --- a/litex/build/altera/quartus.py +++ b/litex/build/altera/quartus.py @@ -1,8 +1,9 @@ -# This file is Copyright (c) 2013 Florent Kermarrec +# This file is Copyright (c) 2013-2019 Florent Kermarrec # License: BSD import os import subprocess +import math from migen.fhdl.structure import _Fragment @@ -50,7 +51,7 @@ def _format_qsf(signame, pin, others, resname): return '\n'.join(fmt_c) -def _build_qsf(named_sc, named_pc): +def _build_qsf(named_sc, named_pc, build_name): lines = [] for sig, pins, others, resname in named_sc: if len(pins) > 1: @@ -64,10 +65,27 @@ def _build_qsf(named_sc, named_pc): lines.append("") lines.append("\n\n".join(named_pc)) - lines.append("set_global_assignment -name top_level_entity top") + # Set top level name to "build_name" in .qsf file instead always use "top" name + lines.append("set_global_assignment -name top_level_entity " + build_name) return "\n".join(lines) +def _build_sdc(clocks, false_paths, vns, build_name): + lines = [] + for clk, period in sorted(clocks.items(), key=lambda x: x[0].duid): + lines.append( + "create_clock -name {clk} -period ".format(clk=vns.get_name(clk)) + str(period) + + " [get_ports {{{clk}}}]".format(clk=vns.get_name(clk))) + for from_, to in sorted(false_paths, + key=lambda x: (x[0].duid, x[1].duid)): + lines.append( + "set_false_path " + "-from [get_clocks {{{from_}}}] " + "-to [get_clocks {{{to}}}]".format( + from_=vns.get_name(from_), to=vns.get_name(to))) + tools.write_to_file("{}.sdc".format(build_name), "\n".join(lines)) + + def _build_files(device, sources, vincpaths, named_sc, named_pc, build_name): lines = [] for filename, language, library in sources: @@ -81,12 +99,13 @@ def _build_files(device, sources, vincpaths, named_sc, named_pc, build_name): lang=language.upper(), path=filename.replace("\\", "/"), lib=library)) + lines.append("set_global_assignment -name SDC_FILE {}.sdc".format(build_name)) for path in vincpaths: lines.append("set_global_assignment -name SEARCH_PATH {}".format( path.replace("\\", "/"))) - lines.append(_build_qsf(named_sc, named_pc)) + lines.append(_build_qsf(named_sc, named_pc, build_name)) lines.append("set_global_assignment -name DEVICE {}".format(device)) tools.write_to_file("{}.qsf".format(build_name), "\n".join(lines)) @@ -116,6 +135,10 @@ fi class AlteraQuartusToolchain: + def __init__(self): + self.clocks = dict() + self.false_paths = set() + def build(self, platform, fragment, build_dir="build", build_name="top", toolchain_path=None, run=True, **kwargs): if toolchain_path is None: @@ -139,6 +162,8 @@ class AlteraQuartusToolchain: named_sc, named_pc, build_name) + + _build_sdc(self.clocks, self.false_paths, v_output.ns, build_name) if run: _run_quartus(build_name, toolchain_path) @@ -147,12 +172,11 @@ class AlteraQuartusToolchain: return v_output.ns def add_period_constraint(self, platform, clk, period): - # TODO: handle differential clk - platform.add_platform_command( - "set_global_assignment -name duty_cycle 50 -section_id {clk}", - clk=clk) - platform.add_platform_command( - "set_global_assignment -name fmax_requirement \"{freq} MHz\" " - "-section_id {clk}".format(freq=(1. / period) * 1000, - clk="{clk}"), - clk=clk) + if clk in self.clocks: + raise ValueError("A period constraint already exists") + period = math.floor(period*1e3)/1e3 # round to lowest picosecond + self.clocks[clk] = period + + def add_false_path_constraint(self, platform, from_, to): + if (to, from_) not in self.false_paths: + self.false_paths.add((from_, to)) diff --git a/litex/build/lattice/trellis.py b/litex/build/lattice/trellis.py index f4c7c05cf..63a8d75d4 100644 --- a/litex/build/lattice/trellis.py +++ b/litex/build/lattice/trellis.py @@ -143,7 +143,7 @@ class LatticeTrellisToolchain: self.build_template = [ "yosys -q -l {build_name}.rpt {build_name}.ys", "nextpnr-ecp5 --json {build_name}.json --lpf {build_name}.lpf --textcfg {build_name}.config --{architecture} --package {package} --freq {freq_constraint}", - "ecppack {build_name}.config {build_name}.bit" + "ecppack {build_name}.config --svf {build_name}.svf --bit {build_name}.bit" ] self.freq_constraints = dict() diff --git a/litex/build/microsemi/libero_soc.py b/litex/build/microsemi/libero_soc.py index 4327f6239..a82ac5862 100644 --- a/litex/build/microsemi/libero_soc.py +++ b/litex/build/microsemi/libero_soc.py @@ -23,6 +23,8 @@ def _format_io_constraint(c): elif isinstance(c, IOStandard): return "-io_std {} ".format(c.name) elif isinstance(c, Misc): + return "-RES_PULL {} ".format(c.misc) + else: raise NotImplementedError @@ -183,7 +185,10 @@ def _build_script(build_name, device, toolchain_path, ver=None): copy_stmt = "copy" fail_stmt = " || exit /b" else: - raise NotImplementedError + script_ext = ".sh" + build_script_contents = "# Autogenerated by Migen\n\n" + copy_stmt = "cp" + fail_stmt = " || exit 1" build_script_file = "build_" + build_name + script_ext tools.write_to_file(build_script_file, build_script_contents, diff --git a/litex/build/sim/core/sim.c b/litex/build/sim/core/sim.c index 32124ee19..3f7ad6489 100644 --- a/litex/build/sim/core/sim.c +++ b/litex/build/sim/core/sim.c @@ -203,7 +203,7 @@ static void cb(int sock, short which, void *arg) } } -int main() +int main(int argc, char *argv[]) { void *vdut=NULL; struct timeval tv; @@ -224,6 +224,7 @@ int main() goto out; } + litex_sim_init_cmdargs(argc, argv); if(RC_OK != (ret = litex_sim_initialize_all(&vdut, base))) { goto out; diff --git a/litex/build/sim/core/veril.cpp b/litex/build/sim/core/veril.cpp index ef43fb1df..6821e2078 100644 --- a/litex/build/sim/core/veril.cpp +++ b/litex/build/sim/core/veril.cpp @@ -17,6 +17,11 @@ extern "C" void litex_sim_eval(void *vdut) dut->eval(); } +extern "C" void litex_sim_init_cmdargs(int argc, char *argv[]) +{ + Verilated::commandArgs(argc, argv); +} + extern "C" void litex_sim_init_tracer(void *vdut) { Vdut *dut = (Vdut*)vdut; diff --git a/litex/build/sim/core/veril.h b/litex/build/sim/core/veril.h index 35326ade6..b8c5f1651 100644 --- a/litex/build/sim/core/veril.h +++ b/litex/build/sim/core/veril.h @@ -4,6 +4,7 @@ #define __VERIL_H_ #ifdef __cplusplus +extern "C" void litex_sim_init_cmdargs(int argc, char *argv[]); extern "C" void litex_sim_eval(void *vdut); extern "C" void litex_sim_init_tracer(void *vdut); extern "C" void litex_sim_tracer_dump(); diff --git a/litex/build/xilinx/common.py b/litex/build/xilinx/common.py index da22e6358..36da5b66e 100644 --- a/litex/build/xilinx/common.py +++ b/litex/build/xilinx/common.py @@ -265,7 +265,7 @@ setattr -set keep 1 a:async_reg=true synth_xilinx -top top -write_edif -attrprop {build_name}.edif +write_edif -pvector bra -attrprop {build_name}.edif """.format(build_name=build_name) ys_name = build_name + ".ys" diff --git a/litex/build/xilinx/vivado.py b/litex/build/xilinx/vivado.py index 54bbfe85e..09a19411c 100644 --- a/litex/build/xilinx/vivado.py +++ b/litex/build/xilinx/vivado.py @@ -1,9 +1,10 @@ -# This file is Copyright (c) 2014 Florent Kermarrec +# This file is Copyright (c) 2014-2019 Florent Kermarrec # License: BSD import os import subprocess import sys +import math from migen.fhdl.structure import _Fragment @@ -117,8 +118,9 @@ class XilinxVivadoToolchain: for filename, language, library in sources: filename_tcl = "{" + filename + "}" tcl.append("add_files " + filename_tcl) - tcl.append("set_property library {} [get_files {}]" - .format(library, filename_tcl)) + if language == "vhdl": + tcl.append("set_property library {} [get_files {}]" + .format(library, filename_tcl)) for filename in edifs: filename_tcl = "{" + filename + "}" tcl.append("read_edif " + filename_tcl) @@ -196,23 +198,23 @@ class XilinxVivadoToolchain: # The asynchronous input to a MultiReg is a false path platform.add_platform_command( "set_false_path -quiet " - "-to [get_nets -filter {{mr_ff == TRUE}}]" + "-to [get_nets -quiet -filter {{mr_ff == TRUE}}]" ) # The asychronous reset input to the AsyncResetSynchronizer is a false # path platform.add_platform_command( "set_false_path -quiet " - "-to [get_pins -filter {{REF_PIN_NAME == PRE}} " - "-of [get_cells -filter {{ars_ff1 == TRUE || ars_ff2 == TRUE}}]]" + "-to [get_pins -quiet -filter {{REF_PIN_NAME == PRE}} " + "-of [get_cells -quiet -filter {{ars_ff1 == TRUE || ars_ff2 == TRUE}}]]" ) # clock_period-2ns to resolve metastability on the wire between the # AsyncResetSynchronizer FFs platform.add_platform_command( "set_max_delay 2 -quiet " - "-from [get_pins -filter {{REF_PIN_NAME == Q}} " - "-of [get_cells -filter {{ars_ff1 == TRUE}}]] " - "-to [get_pins -filter {{REF_PIN_NAME == D}} " - "-of [get_cells -filter {{ars_ff2 == TRUE}}]]" + "-from [get_pins -quiet -filter {{REF_PIN_NAME == Q}} " + "-of [get_cells -quiet -filter {{ars_ff1 == TRUE}}]] " + "-to [get_pins -quiet -filter {{REF_PIN_NAME == D}} " + "-of [get_cells -quiet -filter {{ars_ff2 == TRUE}}]]" ) def build(self, platform, fragment, build_dir="build", build_name="top", @@ -250,6 +252,7 @@ class XilinxVivadoToolchain: def add_period_constraint(self, platform, clk, period): if clk in self.clocks: raise ValueError("A period constraint already exists") + period = math.floor(period*1e3)/1e3 # round to lowest picosecond self.clocks[clk] = period def add_false_path_constraint(self, platform, from_, to): diff --git a/litex/soc/cores/clock.py b/litex/soc/cores/clock.py index 785d971cd..962d69cba 100644 --- a/litex/soc/cores/clock.py +++ b/litex/soc/cores/clock.py @@ -1,9 +1,4 @@ -""" -Clock Abstraction Modules - - -Made in Paris-CDG while waiting a delayed Air-France KLM flight... -""" +"""Clock Abstraction Modules""" from migen import * from migen.genlib.io import DifferentialInput @@ -15,13 +10,14 @@ from litex.soc.interconnect.csr import * def period_ns(freq): return 1e9/freq -# Xilinx / 7-Series +# Xilinx / 7-Series -------------------------------------------------------------------------------- class S7Clocking(Module, AutoCSR): clkfbout_mult_frange = (2, 64+1) clkout_divide_range = (1, 128+1) - def __init__(self): + def __init__(self, vco_margin=0): + self.vco_margin = vco_margin self.reset = Signal() self.locked = Signal() self.clkin_freq = None @@ -62,30 +58,32 @@ class S7Clocking(Module, AutoCSR): def compute_config(self): config = {} - config["divclk_divide"] = 1 - for clkfbout_mult in range(*self.clkfbout_mult_frange): - all_valid = True - vco_freq = self.clkin_freq*clkfbout_mult - (vco_freq_min, vco_freq_max) = self.vco_freq_range - if vco_freq >= vco_freq_min and vco_freq <= vco_freq_max: - for n, (clk, f, p, m) in sorted(self.clkouts.items()): - valid = False - for d in range(*self.clkout_divide_range): - clk_freq = vco_freq/d - if abs(clk_freq - f) < f*m: - config["clkout{}_freq".format(n)] = clk_freq - config["clkout{}_divide".format(n)] = d - config["clkout{}_phase".format(n)] = p - valid = True - break - if not valid: - all_valid = False - else: - all_valid = False - if all_valid: - config["vco"] = vco_freq - config["clkfbout_mult"] = clkfbout_mult - return config + for divclk_divide in range(*self.divclk_divide_range): + config["divclk_divide"] = divclk_divide + for clkfbout_mult in range(*self.clkfbout_mult_frange): + all_valid = True + vco_freq = self.clkin_freq*clkfbout_mult/divclk_divide + (vco_freq_min, vco_freq_max) = self.vco_freq_range + if (vco_freq >= vco_freq_min*(1 + self.vco_margin) and + vco_freq <= vco_freq_max*(1 - self.vco_margin)): + for n, (clk, f, p, m) in sorted(self.clkouts.items()): + valid = False + for d in range(*self.clkout_divide_range): + clk_freq = vco_freq/d + if abs(clk_freq - f) < f*m: + config["clkout{}_freq".format(n)] = clk_freq + config["clkout{}_divide".format(n)] = d + config["clkout{}_phase".format(n)] = p + valid = True + break + if not valid: + all_valid = False + else: + all_valid = False + if all_valid: + config["vco"] = vco_freq + config["clkfbout_mult"] = clkfbout_mult + return config raise ValueError("No PLL config found") def expose_drp(self): @@ -127,6 +125,7 @@ class S7PLL(S7Clocking): def __init__(self, speedgrade=-1): S7Clocking.__init__(self) + self.divclk_divide_range = (1, 56+1) self.vco_freq_range = { -1: (800e6, 2133e6), -2: (800e6, 1866e6), @@ -157,6 +156,7 @@ class S7MMCM(S7Clocking): def __init__(self, speedgrade=-1): S7Clocking.__init__(self) + self.divclk_divide_range = (1, 106+1) self.clkin_freq_range = { -1: (10e6, 800e6), -2: (10e6, 933e6), @@ -204,7 +204,7 @@ class S7IDELAYCTRL(Module): ) self.specials += Instance("IDELAYCTRL", i_REFCLK=cd.clk, i_RST=ic_reset) -# Xilinx / Ultrascale +# Xilinx / Ultrascale ------------------------------------------------------------------------------ # TODO: # - use Ultrascale primitives instead of 7-Series' ones. (Vivado recognize and convert them). @@ -213,7 +213,8 @@ class USClocking(Module, AutoCSR): clkfbout_mult_frange = (2, 64+1) clkout_divide_range = (1, 128+1) - def __init__(self): + def __init__(self, vco_margin=0): + self.vco_margin = vco_margin self.reset = Signal() self.locked = Signal() self.clkin_freq = None @@ -254,30 +255,32 @@ class USClocking(Module, AutoCSR): def compute_config(self): config = {} - config["divclk_divide"] = 1 - for clkfbout_mult in range(*self.clkfbout_mult_frange): - all_valid = True - vco_freq = self.clkin_freq*clkfbout_mult - (vco_freq_min, vco_freq_max) = self.vco_freq_range - if vco_freq >= vco_freq_min and vco_freq <= vco_freq_max: - for n, (clk, f, p, m) in sorted(self.clkouts.items()): - valid = False - for d in range(*self.clkout_divide_range): - clk_freq = vco_freq/d - if abs(clk_freq - f) < f*m: - config["clkout{}_freq".format(n)] = clk_freq - config["clkout{}_divide".format(n)] = d - config["clkout{}_phase".format(n)] = p - valid = True - break - if not valid: - all_valid = False - else: - all_valid = False - if all_valid: - config["vco"] = vco_freq - config["clkfbout_mult"] = clkfbout_mult - return config + for divclk_divide in range(*self.divclk_divide_range): + config["divclk_divide"] = divclk_divide + for clkfbout_mult in range(*self.clkfbout_mult_frange): + all_valid = True + vco_freq = self.clkin_freq*clkfbout_mult/divclk_divide + (vco_freq_min, vco_freq_max) = self.vco_freq_range + if (vco_freq >= vco_freq_min*(1 + self.vco_margin) and + vco_freq <= vco_freq_max*(1 - self.vco_margin)): + for n, (clk, f, p, m) in sorted(self.clkouts.items()): + valid = False + for d in range(*self.clkout_divide_range): + clk_freq = vco_freq/d + if abs(clk_freq - f) < f*m: + config["clkout{}_freq".format(n)] = clk_freq + config["clkout{}_divide".format(n)] = d + config["clkout{}_phase".format(n)] = p + valid = True + break + if not valid: + all_valid = False + else: + all_valid = False + if all_valid: + config["vco"] = vco_freq + config["clkfbout_mult"] = clkfbout_mult + return config raise ValueError("No PLL config found") def expose_drp(self): @@ -318,12 +321,12 @@ class USPLL(USClocking): def __init__(self, speedgrade=-1): USClocking.__init__(self) + self.divclk_divide_range = (1, 56+1) self.clkin_freq_range = { -1: (70e6, 800e6), -2: (70e6, 933e6), -3: (70e6, 1066e6), }[speedgrade] - self.vco_freq_range = { -1: (600e6, 1200e6), -2: (600e6, 1335e6), @@ -354,12 +357,12 @@ class USMMCM(USClocking): def __init__(self, speedgrade=-1): USClocking.__init__(self) + self.divclk_divide_range = (1, 106+1) self.clkin_freq_range = { -1: (10e6, 800e6), -2: (10e6, 933e6), -3: (10e6, 1066e6), }[speedgrade] - self.vco_freq_range = { -1: (600e6, 1200e6), -2: (600e6, 1440e6), @@ -404,7 +407,7 @@ class USIDELAYCTRL(Module): i_REFCLK=cd.clk, i_RST=ic_reset) -# Lattice / ECP5 +# Lattice / ECP5 ----------------------------------------------------------------------------------- # TODO: # - add proper phase support. diff --git a/litex/soc/cores/cpu/vexriscv/core.py b/litex/soc/cores/cpu/vexriscv/core.py index db05e1fd4..1c233f317 100644 --- a/litex/soc/cores/cpu/vexriscv/core.py +++ b/litex/soc/cores/cpu/vexriscv/core.py @@ -16,8 +16,11 @@ class VexRiscv(Module, AutoCSR): def __init__(self, platform, cpu_reset_address, variant=None): variant = "std" if variant is None else variant variant = "std_debug" if variant == "debug" else variant - variants = ("std", "std_debug", "lite", "lite_debug", "min", "min_debug") + variants = ("std", "std_debug", "lite", "lite_debug", "min", "min_debug", "full", "full_debug") assert variant in variants, "Unsupported variant %s" % variant + self.platform = platform + self.variant = variant + self.external_variant = None self.reset = Signal() self.ibus = ibus = wishbone.Interface() self.dbus = dbus = wishbone.Interface() @@ -60,9 +63,6 @@ class VexRiscv(Module, AutoCSR): if "debug" in variant: self.add_debug() - # add verilog sources - self.add_sources(platform, variant) - def add_debug(self): debug_reset = Signal() @@ -156,12 +156,20 @@ class VexRiscv(Module, AutoCSR): "std_debug": "VexRiscv_Debug.v", "lite": "VexRiscv_Lite.v", "lite_debug": "VexRiscv_LiteDebug.v", - "min": "VexRiscv_Lite.v", - "min_debug": "VexRiscv_LiteDebug.v", + "min": "VexRiscv_Min.v", + "min_debug": "VexRiscv_MinDebug.v", + "full": "VexRiscv_Full.v", + "full_debug": "VexRiscv_FullDebug.v", } cpu_filename = verilog_variants[variant] vdir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "verilog") platform.add_source(os.path.join(vdir, cpu_filename)) + def use_external_variant(self, variant_filename): + self.external_variant = True + self.platform.add_source(variant_filename) + def do_finalize(self): + if not self.external_variant: + self.add_sources(self.platform, self.variant) self.specials += Instance("VexRiscv", **self.cpu_params) diff --git a/litex/soc/cores/cpu/vexriscv/verilog b/litex/soc/cores/cpu/vexriscv/verilog index d7bbc2c16..ebe406465 160000 --- a/litex/soc/cores/cpu/vexriscv/verilog +++ b/litex/soc/cores/cpu/vexriscv/verilog @@ -1 +1 @@ -Subproject commit d7bbc2c167f1a0886c446d3c305d0ed4388570be +Subproject commit ebe4064653bc143bf92a0ccdd1099173620fcbf5 diff --git a/litex/soc/integration/soc_core.py b/litex/soc/integration/soc_core.py index ed9c02af0..b8897d00b 100644 --- a/litex/soc/integration/soc_core.py +++ b/litex/soc/integration/soc_core.py @@ -1,5 +1,7 @@ +import os import struct import inspect +import json from operator import itemgetter from migen import * @@ -36,24 +38,43 @@ def mem_decoder(address, start=26, end=29): def get_mem_data(filename, endianness="big", mem_size=None): - data = [] - with open(filename, "rb") as mem_file: - while True: - w = mem_file.read(4) - if not w: - break - if endianness == "little": - data.append(struct.unpack("I", w)[0]) - data_size = len(data)*4 + # create memory regions + _, ext = os.path.splitext(filename) + if ext == ".json": + f = open(filename, "r") + regions = json.load(f) + f.close() + else: + regions = {filename: "0x00000000"} + + # determine data_size + data_size = 0 + for filename, base in regions.items(): + data_size = max(int(base, 16) + os.path.getsize(filename), data_size) assert data_size > 0 if mem_size is not None: assert data_size < mem_size, ( "file is too big: {}/{} bytes".format( - data_size, mem_size)) - return data + data_size, mem_size)) + # fill data + data = [0]*(data_size//4) + for filename, base in regions.items(): + with open(filename, "rb") as f: + i = 0 + while True: + w = f.read(4) + if not w: + break + if len(w) != 4: + for i in range(len(w), 4): + w += b'\x00' + if endianness == "little": + data[i] = struct.unpack("I", w)[0] + i += 1 + return data class ReadOnlyDict(dict): def __readonly__(self, *args, **kwargs): diff --git a/litex/soc/integration/soc_zynq.py b/litex/soc/integration/soc_zynq.py index 955402c39..86d2d7b1a 100644 --- a/litex/soc/integration/soc_zynq.py +++ b/litex/soc/integration/soc_zynq.py @@ -8,16 +8,29 @@ from litex.soc.integration.cpu_interface import get_csr_header from litex.soc.interconnect import wishbone from litex.soc.interconnect import axi +# Record layouts ----------------------------------------------------------------------------------- + +def axi_fifo_ctrl_layout(): + return [ + ("racount", 3, DIR_M_TO_S), + ("rcount", 8, DIR_M_TO_S), + ("rdissuecapen", 1, DIR_S_TO_M), + ("wacount", 6, DIR_M_TO_S), + ("wcount", 8, DIR_M_TO_S), + ("wrissuecapen", 1, DIR_S_TO_M), + ] + +# SoC Zync ----------------------------------------------------------------------------------------- class SoCZynq(SoCCore): SoCCore.mem_map["csr"] = 0x00000000 def __init__(self, platform, clk_freq, ps7_name, **kwargs): + self.ps7_name = ps7_name SoCCore.__init__(self, platform, clk_freq, cpu_type=None, shadow_base=0x00000000, **kwargs) - # PS7 -------------------------------------------------------------------------------------- - self.axi_gp0 = axi_gp0 = axi.AXIInterface(data_width=32, address_width=32, id_width=12) + # PS7 (Minimal) ---------------------------------------------------------------------------- ps7_ddram_pads = platform.request("ps7_ddram") - self.specials += Instance(ps7_name, + self.ps7_params = dict( # clk/rst io_PS_CLK=platform.request("ps7_clk"), io_PS_PORB=platform.request("ps7_porb"), @@ -57,10 +70,17 @@ class SoCZynq(SoCCore): # fabric clk o_FCLK_CLK0=ClockSignal("sys"), - # axi clk + # axi gp0 clk i_M_AXI_GP0_ACLK=ClockSignal("sys"), + ) + platform.add_ip(os.path.join("ip", ps7_name + ".xci")) - # axi aw + # GP0 ------------------------------------------------------------------------------------------ + + def add_gp0(self): + self.axi_gp0 = axi_gp0 = axi.AXIInterface(data_width=32, address_width=32, id_width=12) + self.ps7_params.update( + # axi gp0 aw o_M_AXI_GP0_AWVALID=axi_gp0.aw.valid, i_M_AXI_GP0_AWREADY=axi_gp0.aw.ready, o_M_AXI_GP0_AWADDR=axi_gp0.aw.addr, @@ -68,39 +88,39 @@ class SoCZynq(SoCCore): o_M_AXI_GP0_AWLEN=axi_gp0.aw.len, o_M_AXI_GP0_AWSIZE=axi_gp0.aw.size, o_M_AXI_GP0_AWID=axi_gp0.aw.id, - #o_M_AXI_GP0_AWLOCK =, - #o_M_AXI_GP0_AWPROT =, - #o_M_AXI_GP0_AWCACHE =, - #o_M_AXI_GP0_AWQOS =, + o_M_AXI_GP0_AWLOCK=axi_gp0.aw.lock, + o_M_AXI_GP0_AWPROT=axi_gp0.aw.prot, + o_M_AXI_GP0_AWCACHE=axi_gp0.aw.cache, + o_M_AXI_GP0_AWQOS=axi_gp0.aw.qos, - # axi w + # axi gp0 w o_M_AXI_GP0_WVALID=axi_gp0.w.valid, o_M_AXI_GP0_WLAST=axi_gp0.w.last, i_M_AXI_GP0_WREADY=axi_gp0.w.ready, - #o_M_AXI_GP0_WID=, + o_M_AXI_GP0_WID=axi_gp0.w.id, o_M_AXI_GP0_WDATA=axi_gp0.w.data, o_M_AXI_GP0_WSTRB=axi_gp0.w.strb, - # axi b + # axi gp0 b i_M_AXI_GP0_BVALID=axi_gp0.b.valid, o_M_AXI_GP0_BREADY=axi_gp0.b.ready, i_M_AXI_GP0_BID=axi_gp0.b.id, i_M_AXI_GP0_BRESP=axi_gp0.b.resp, - # axi ar + # axi gp0 ar o_M_AXI_GP0_ARVALID=axi_gp0.ar.valid, i_M_AXI_GP0_ARREADY=axi_gp0.ar.ready, o_M_AXI_GP0_ARADDR=axi_gp0.ar.addr, o_M_AXI_GP0_ARBURST=axi_gp0.ar.burst, o_M_AXI_GP0_ARLEN=axi_gp0.ar.len, o_M_AXI_GP0_ARID=axi_gp0.ar.id, - #o_M_AXI_GP0_ARLOCK=, - #o_M_AXI_GP0_ARSIZE=, - #o_M_AXI_GP0_ARPROT=, - #o_M_AXI_GP0_ARCACHE=, - #o_M_AXI_GP0_ARQOS=, + o_M_AXI_GP0_ARLOCK=axi_gp0.ar.lock, + o_M_AXI_GP0_ARSIZE=axi_gp0.ar.size, + o_M_AXI_GP0_ARPROT=axi_gp0.ar.prot, + o_M_AXI_GP0_ARCACHE=axi_gp0.ar.cache, + o_M_AXI_GP0_ARQOS=axi_gp0.ar.qos, - # axi r + # axi gp0 r i_M_AXI_GP0_RVALID=axi_gp0.r.valid, o_M_AXI_GP0_RREADY=axi_gp0.r.ready, i_M_AXI_GP0_RLAST=axi_gp0.r.last, @@ -108,13 +128,79 @@ class SoCZynq(SoCCore): i_M_AXI_GP0_RRESP=axi_gp0.r.resp, i_M_AXI_GP0_RDATA=axi_gp0.r.data, ) - platform.add_ip(os.path.join("ip", ps7_name + ".xci")) - # AXI to Wishbone -------------------------------------------------------------------------- - self.wb_gp0 = wb_gp0 = wishbone.Interface() - axi2wishbone = axi.AXI2Wishbone(axi_gp0, wb_gp0, base_address=0x43c00000) + # HP0 ------------------------------------------------------------------------------------------ + + def add_hp0(self): + self.axi_hp0 = axi_hp0 = axi.AXIInterface(data_width=64, address_width=32, id_width=6) + self.axi_hp0_fifo_ctrl = axi_hp0_fifo_ctrl = Record(axi_fifo_ctrl_layout()) + self.ps7_params.update( + # axi hp0 aw + i_S_AXI_HP0_AWVALID=axi_hp0.aw.valid, + o_S_AXI_HP0_AWREADY=axi_hp0.aw.ready, + i_S_AXI_HP0_AWADDR=axi_hp0.aw.addr, + i_S_AXI_HP0_AWBURST=axi_hp0.aw.burst, + i_S_AXI_HP0_AWLEN=axi_hp0.aw.len, + i_S_AXI_HP0_AWSIZE=axi_hp0.aw.size, + i_S_AXI_HP0_AWID=axi_hp0.aw.id, + i_S_AXI_HP0_AWLOCK=axi_hp0.aw.lock, + i_S_AXI_HP0_AWPROT=axi_hp0.aw.prot, + i_S_AXI_HP0_AWCACHE=axi_hp0.aw.cache, + i_S_AXI_HP0_AWQOS=axi_hp0.aw.qos, + + # axi hp0 w + i_S_AXI_HP0_WVALID=axi_hp0.w.valid, + i_S_AXI_HP0_WLAST=axi_hp0.w.last, + o_S_AXI_HP0_WREADY=axi_hp0.w.ready, + i_S_AXI_HP0_WID=axi_hp0.w.id, + i_S_AXI_HP0_WDATA=axi_hp0.w.data, + i_S_AXI_HP0_WSTRB=axi_hp0.w.strb, + + # axi hp0 b + o_S_AXI_HP0_BVALID=axi_hp0.b.valid, + i_S_AXI_HP0_BREADY=axi_hp0.b.ready, + o_S_AXI_HP0_BID=axi_hp0.b.id, + o_S_AXI_HP0_BRESP=axi_hp0.b.resp, + + # axi hp0 ar + i_S_AXI_HP0_ARVALID=axi_hp0.ar.valid, + o_S_AXI_HP0_ARREADY=axi_hp0.ar.ready, + i_S_AXI_HP0_ARADDR=axi_hp0.ar.addr, + i_S_AXI_HP0_ARBURST=axi_hp0.ar.burst, + i_S_AXI_HP0_ARLEN=axi_hp0.ar.len, + i_S_AXI_HP0_ARID=axi_hp0.ar.id, + i_S_AXI_HP0_ARLOCK=axi_hp0.ar.lock, + i_S_AXI_HP0_ARSIZE=axi_hp0.ar.size, + i_S_AXI_HP0_ARPROT=axi_hp0.ar.prot, + i_S_AXI_HP0_ARCACHE=axi_hp0.ar.cache, + i_S_AXI_HP0_ARQOS=axi_hp0.ar.qos, + + # axi hp0 r + o_S_AXI_HP0_RVALID=axi_hp0.r.valid, + i_S_AXI_HP0_RREADY=axi_hp0.r.ready, + o_S_AXI_HP0_RLAST=axi_hp0.r.last, + o_S_AXI_HP0_RID=axi_hp0.r.id, + o_S_AXI_HP0_RRESP=axi_hp0.r.resp, + o_S_AXI_HP0_RDATA=axi_hp0.r.data, + + # axi hp0 fifo ctrl + o_S_AXI_HP0_RACOUNT=axi_hp0_fifo_ctrl.racount, + o_S_AXI_HP0_RCOUNT=axi_hp0_fifo_ctrl.rcount, + i_S_AXI_HP0_RDISSUECAP1_EN=axi_hp0_fifo_ctrl.rdissuecapen, + o_S_AXI_HP0_WACOUNT=axi_hp0_fifo_ctrl.wacount, + o_S_AXI_HP0_WCOUNT=axi_hp0_fifo_ctrl.wcount, + i_S_AXI_HP0_WRISSUECAP1_EN=axi_hp0_fifo_ctrl.wrissuecapen + ) + + def add_axi_to_wishbone(self, axi_port, base_address=0x43c00000): + wb = wishbone.Interface() + axi2wishbone = axi.AXI2Wishbone(axi_port, wb, base_address) self.submodules += axi2wishbone - self.add_wb_master(wb_gp0) + self.add_wb_master(wb) + + def do_finalize(self): + SoCCore.do_finalize(self) + self.specials += Instance(self.ps7_name, **self.ps7_params) def generate_software_header(self, filename): csr_header = get_csr_header(self.get_csr_regions(), diff --git a/litex/soc/interconnect/avalon.py b/litex/soc/interconnect/avalon.py new file mode 100644 index 000000000..edc2f6f10 --- /dev/null +++ b/litex/soc/interconnect/avalon.py @@ -0,0 +1,38 @@ +from migen import * + +from litex.soc.interconnect import stream + +# AvalonST to/from Native -------------------------------------------------------------------------- + +class Native2AvalonST(Module): + def __init__(self, layout, latency=2): + self.sink = sink = stream.Endpoint(layout) + self.source = source = stream.Endpoint(layout) + + # # # + + _from = sink + for n in range(latency): + _to = stream.Endpoint(layout) + self.sync += _from.connect(_to, omit={"ready"}) + if n == 0: + self.sync += _to.valid.eq(sink.valid & source.ready) + _from = _to + self.comb += _to.connect(source, omit={"ready"}) + self.comb += sink.ready.eq(source.ready) + + +class AvalonST2Native(Module): + def __init__(self, layout, latency=2): + self.sink = sink = stream.Endpoint(layout) + self.source = source = stream.Endpoint(layout) + + # # # + + buf = stream.SyncFIFO(layout, max(latency, 4)) + self.submodules += buf + self.comb += [ + sink.connect(buf.sink, omit={"ready"}), + sink.ready.eq(source.ready), + buf.source.connect(source) + ] diff --git a/litex/soc/interconnect/axi.py b/litex/soc/interconnect/axi.py index 7d0792cee..4692cbfdc 100644 --- a/litex/soc/interconnect/axi.py +++ b/litex/soc/interconnect/axi.py @@ -20,26 +20,31 @@ def ax_description(address_width, id_width): ("burst", 2), # Burst type ("len", 8), # Number of data (-1) transfers (up to 256) ("size", 4), # Number of bytes (-1) of each data transfer (up to 1024 bits) + ("lock", 2), + ("prot", 3), + ("cache", 4), + ("qos", 4), ("id", id_width) ] -def w_description(data_width): +def w_description(data_width, id_width): return [ ("data", data_width), - ("strb", data_width//8) + ("strb", data_width//8), + ("id", id_width) ] def b_description(id_width): return [ ("resp", 2), - ("id", id_width) + ("id", id_width) ] def r_description(data_width, id_width): return [ ("resp", 2), ("data", data_width), - ("id", id_width) + ("id", id_width) ] @@ -51,7 +56,7 @@ class AXIInterface(Record): self.clock_domain = clock_domain self.aw = stream.Endpoint(ax_description(address_width, id_width)) - self.w = stream.Endpoint(w_description(data_width)) + self.w = stream.Endpoint(w_description(data_width, id_width)) self.b = stream.Endpoint(b_description(id_width)) self.ar = stream.Endpoint(ax_description(address_width, id_width)) self.r = stream.Endpoint(r_description(data_width, id_width)) @@ -60,12 +65,12 @@ class AXIInterface(Record): class AXI2Wishbone(Module): def __init__(self, axi, wishbone, base_address): - assert axi.data_width == 32 - assert axi.address_width == 32 + assert axi.data_width == len(wishbone.dat_r) + assert axi.address_width == len(wishbone.adr) + 2 _data = Signal(axi.data_width) - _read_addr = Signal(32) - _write_addr = Signal(32) + _read_addr = Signal(axi.address_width) + _write_addr = Signal(axi.address_width) self.comb += _read_addr.eq(axi.ar.addr - base_address) self.comb += _write_addr.eq(axi.aw.addr - base_address) @@ -78,8 +83,6 @@ class AXI2Wishbone(Module): NextState("DO-WRITE") ) ) - axi_ar_addr = Signal(32) - self.comb += axi_ar_addr.eq(axi.ar.addr - base_address) fsm.act("DO-READ", wishbone.stb.eq(1), wishbone.cyc.eq(1), diff --git a/litex/soc/software/bios/main.c b/litex/soc/software/bios/main.c index 89a3b9cce..26ca39838 100644 --- a/litex/soc/software/bios/main.c +++ b/litex/soc/software/bios/main.c @@ -186,133 +186,18 @@ static void ident(void) printf("Ident: %s\n", buffer); } -#ifdef __lm32__ -enum { - CSR_IE = 1, CSR_IM, CSR_IP, CSR_ICC, CSR_DCC, CSR_CC, CSR_CFG, CSR_EBA, - CSR_DC, CSR_DEBA, CSR_JTX, CSR_JRX, CSR_BP0, CSR_BP1, CSR_BP2, CSR_BP3, - CSR_WP0, CSR_WP1, CSR_WP2, CSR_WP3, -}; - -/* processor registers */ -static int parse_csr(const char *csr) -{ - if(!strcmp(csr, "ie")) return CSR_IE; - if(!strcmp(csr, "im")) return CSR_IM; - if(!strcmp(csr, "ip")) return CSR_IP; - if(!strcmp(csr, "icc")) return CSR_ICC; - if(!strcmp(csr, "dcc")) return CSR_DCC; - if(!strcmp(csr, "cc")) return CSR_CC; - if(!strcmp(csr, "cfg")) return CSR_CFG; - if(!strcmp(csr, "eba")) return CSR_EBA; - if(!strcmp(csr, "dc")) return CSR_DC; - if(!strcmp(csr, "deba")) return CSR_DEBA; - if(!strcmp(csr, "jtx")) return CSR_JTX; - if(!strcmp(csr, "jrx")) return CSR_JRX; - if(!strcmp(csr, "bp0")) return CSR_BP0; - if(!strcmp(csr, "bp1")) return CSR_BP1; - if(!strcmp(csr, "bp2")) return CSR_BP2; - if(!strcmp(csr, "bp3")) return CSR_BP3; - if(!strcmp(csr, "wp0")) return CSR_WP0; - if(!strcmp(csr, "wp1")) return CSR_WP1; - if(!strcmp(csr, "wp2")) return CSR_WP2; - if(!strcmp(csr, "wp3")) return CSR_WP3; - - return 0; -} - -static void rcsr(char *csr) -{ - unsigned int csr2; - register unsigned int value; - - if(*csr == 0) { - printf("rcsr \n"); - return; - } - - csr2 = parse_csr(csr); - if(csr2 == 0) { - printf("incorrect csr\n"); - return; - } - - switch(csr2) { - case CSR_IE: asm volatile ("rcsr %0,ie":"=r"(value)); break; - case CSR_IM: asm volatile ("rcsr %0,im":"=r"(value)); break; - case CSR_IP: asm volatile ("rcsr %0,ip":"=r"(value)); break; - case CSR_CC: asm volatile ("rcsr %0,cc":"=r"(value)); break; - case CSR_CFG: asm volatile ("rcsr %0,cfg":"=r"(value)); break; - case CSR_EBA: asm volatile ("rcsr %0,eba":"=r"(value)); break; - case CSR_DEBA: asm volatile ("rcsr %0,deba":"=r"(value)); break; - case CSR_JTX: asm volatile ("rcsr %0,jtx":"=r"(value)); break; - case CSR_JRX: asm volatile ("rcsr %0,jrx":"=r"(value)); break; - default: printf("csr write only\n"); return; - } - - printf("%08x\n", value); -} - -static void wcsr(char *csr, char *value) -{ - char *c; - unsigned int csr2; - register unsigned int value2; - - if((*csr == 0) || (*value == 0)) { - printf("wcsr
\n"); - return; - } - - csr2 = parse_csr(csr); - if(csr2 == 0) { - printf("incorrect csr\n"); - return; - } - value2 = strtoul(value, &c, 0); - if(*c != 0) { - printf("incorrect value\n"); - return; - } - - switch(csr2) { - case CSR_IE: asm volatile ("wcsr ie,%0"::"r"(value2)); break; - case CSR_IM: asm volatile ("wcsr im,%0"::"r"(value2)); break; - case CSR_ICC: asm volatile ("wcsr icc,%0"::"r"(value2)); break; - case CSR_DCC: asm volatile ("wcsr dcc,%0"::"r"(value2)); break; - case CSR_EBA: asm volatile ("wcsr eba,%0"::"r"(value2)); break; - case CSR_DC: asm volatile ("wcsr dcc,%0"::"r"(value2)); break; - case CSR_DEBA: asm volatile ("wcsr deba,%0"::"r"(value2)); break; - case CSR_JTX: asm volatile ("wcsr jtx,%0"::"r"(value2)); break; - case CSR_JRX: asm volatile ("wcsr jrx,%0"::"r"(value2)); break; - case CSR_BP0: asm volatile ("wcsr bp0,%0"::"r"(value2)); break; - case CSR_BP1: asm volatile ("wcsr bp1,%0"::"r"(value2)); break; - case CSR_BP2: asm volatile ("wcsr bp2,%0"::"r"(value2)); break; - case CSR_BP3: asm volatile ("wcsr bp3,%0"::"r"(value2)); break; - case CSR_WP0: asm volatile ("wcsr wp0,%0"::"r"(value2)); break; - case CSR_WP1: asm volatile ("wcsr wp1,%0"::"r"(value2)); break; - case CSR_WP2: asm volatile ("wcsr wp2,%0"::"r"(value2)); break; - case CSR_WP3: asm volatile ("wcsr wp3,%0"::"r"(value2)); break; - default: printf("csr read only\n"); return; - } -} - -#endif /* __lm32__ */ - /* Init + command line */ static void help(void) { - puts("LiteX SoC BIOS"); - puts("Available commands:"); + puts("LiteX BIOS, available commands:"); puts("mr - read address space"); puts("mw - write address space"); puts("mc - copy address space"); + puts(""); puts("crc - compute CRC32 of a part of the address space"); puts("ident - display identifier"); -#ifdef __lm32__ - puts("rcsr - read processor CSR"); - puts("wcsr - write processor CSR"); -#endif + puts(""); #ifdef CSR_CTRL_BASE puts("reboot - reset processor"); #endif @@ -326,6 +211,7 @@ static void help(void) #ifdef ROM_BOOT_ADDRESS puts("romboot - boot from embedded rom"); #endif + puts(""); #ifdef CSR_SDRAM_BASE puts("memtest - run a memory test"); #endif @@ -385,11 +271,6 @@ static void do_command(char *c) else if(strcmp(token, "help") == 0) help(); -#ifdef __lm32__ - else if(strcmp(token, "rcsr") == 0) rcsr(get_token(&c)); - else if(strcmp(token, "wcsr") == 0) wcsr(get_token(&c), get_token(&c)); -#endif - #ifdef CSR_SDRAM_BASE else if(strcmp(token, "sdrrow") == 0) sdrrow(get_token(&c)); else if(strcmp(token, "sdrsw") == 0) sdrsw(); @@ -399,6 +280,7 @@ static void do_command(char *c) else if(strcmp(token, "sdrrderr") == 0) sdrrderr(get_token(&c)); else if(strcmp(token, "sdrwr") == 0) sdrwr(get_token(&c)); #ifdef CSR_DDRPHY_BASE + else if(strcmp(token, "sdrinit") == 0) sdrinit(); #ifdef CSR_DDRPHY_WLEVEL_EN_ADDR else if(strcmp(token, "sdrwlon") == 0) sdrwlon(); else if(strcmp(token, "sdrwloff") == 0) sdrwloff(); @@ -406,7 +288,6 @@ static void do_command(char *c) else if(strcmp(token, "sdrlevel") == 0) sdrlevel(); #endif else if(strcmp(token, "memtest") == 0) memtest(); - else if(strcmp(token, "sdrinit") == 0) sdrinit(); #endif else if(strcmp(token, "") != 0) @@ -433,10 +314,10 @@ static void crcbios(void) length = (unsigned int)&_edata - offset_bios; actual_crc = crc32((unsigned char *)offset_bios, length); if(expected_crc == actual_crc) - printf("BIOS CRC passed (%08x)\n", actual_crc); + printf(" BIOS CRC passed (%08x)\n", actual_crc); else { - printf("BIOS CRC failed (expected %08x, got %08x)\n", expected_crc, actual_crc); - printf("The system will continue, but expect problems.\n"); + printf(" BIOS CRC failed (expected %08x, got %08x)\n", expected_crc, actual_crc); + printf(" The system will continue, but expect problems.\n"); } } @@ -509,12 +390,22 @@ int main(int i, char **c) irq_setmask(0); irq_setie(1); uart_init(); + printf("\n"); printf("\e[1m __ _ __ _ __\e[0m\n"); printf("\e[1m / / (_) /____ | |/_/\e[0m\n"); printf("\e[1m / /__/ / __/ -_)> <\e[0m\n"); printf("\e[1m /____/_/\\__/\\__/_/|_|\e[0m\n"); - printf("\e[1m SoC BIOS / CPU: "); + printf("\n"); + printf(" (c) Copyright 2012-2019 Enjoy-Digital\n"); + printf(" (c) Copyright 2012-2015 M-Labs Ltd\n"); + printf("\n"); + printf(" BIOS built on "__DATE__" "__TIME__"\n"); + crcbios(); + printf("\n"); + + printf("--============ \e[1mSoC info\e[0m ================--\n"); + printf("\e[1mCPU\e[0m: "); #ifdef __lm32__ printf("LM32"); #elif __or1k__ @@ -528,14 +419,19 @@ int main(int i, char **c) #else printf("Unknown"); #endif - printf(" / %3dMHz\e[0m\n", SYSTEM_CLOCK_FREQUENCY/1000000); + printf(" @ %dMHz\n", SYSTEM_CLOCK_FREQUENCY/1000000); + printf("\e[1mROM\e[0m: %dKB\n", ROM_SIZE/1024); + printf("\e[1mSRAM\e[0m: %dKB\n", SRAM_SIZE/1024); +#ifdef L2_SIZE + printf("\e[1mL2\e[0m: %dKB\n", L2_SIZE/1024); +#endif +#ifdef MAIN_RAM_SIZE + printf("\e[1mMAIN-RAM\e[0m: %dKB\n", MAIN_RAM_SIZE/1024); +#endif + printf("\n"); - puts( - "(c) Copyright 2012-2018 Enjoy-Digital\n" - "(c) Copyright 2007-2018 M-Labs Limited\n" - "Built "__DATE__" "__TIME__"\n"); - crcbios(); -#ifdef CSR_ETHMAC_BASE + printf("--========= \e[1mPeripherals init\e[0m ===========--\n"); +#ifdef CSR_ETHPHY_CRG_RESET_ADDR eth_init(); #endif #ifdef CSR_SDRAM_BASE @@ -547,11 +443,17 @@ int main(int i, char **c) sdr_ok = 1; #endif #endif - if(sdr_ok) - boot_sequence(); - else + if (sdr_ok !=1) printf("Memory initialization failed\n"); + printf("\n"); + if(sdr_ok) { + printf("--========== \e[1mBoot sequence\e[0m =============--\n"); + boot_sequence(); + printf("\n"); + } + + printf("--============= \e[1mConsole\e[0m ================--\n"); while(1) { putsnonl("\e[1mBIOS>\e[0m "); readstr(buffer, 64); diff --git a/litex/soc/software/bios/sdram.c b/litex/soc/software/bios/sdram.c index 401b73ffb..ebd53130e 100644 --- a/litex/soc/software/bios/sdram.c +++ b/litex/soc/software/bios/sdram.c @@ -17,7 +17,7 @@ #define MAIN_RAM_BASE SRAM_BASE #endif -static void cdelay(int i) +__attribute__((unused)) static void cdelay(int i) { while(i > 0) { #if defined (__lm32__) @@ -210,16 +210,18 @@ void sdrwr(char *startaddr) #if defined (USDDRPHY) #define ERR_DDRPHY_DELAY 512 +#define ERR_DDRPHY_BITSLIP 8 +#define NBMODULES DFII_PIX_DATA_SIZE/2 #elif defined (ECP5DDRPHY) #define ERR_DDRPHY_DELAY 8 +#define ERR_DDRPHY_BITSLIP 1 +#define NBMODULES DFII_PIX_DATA_SIZE/4 #else #define ERR_DDRPHY_DELAY 32 +#define ERR_DDRPHY_BITSLIP 8 +#define NBMODULES DFII_PIX_DATA_SIZE/2 #endif -#define ERR_DDRPHY_BITSLIP DFII_NPHASES*2 - -#define NBMODULES DFII_PIX_DATA_SIZE*DFII_NPHASES/8 - #ifdef CSR_DDRPHY_WLEVEL_EN_ADDR void sdrwlon(void) @@ -461,6 +463,9 @@ static int read_level_scan(int module, int bitslip) int show = 1; #ifdef USDDRPHY show = (j%16 == 0); +#endif +#ifdef ECP5DDRPHY + ddrphy_burstdet_clr_write(1); #endif command_prd(DFII_COMMAND_CAS|DFII_COMMAND_CS|DFII_COMMAND_RDDATA); cdelay(15); @@ -471,6 +476,10 @@ static int read_level_scan(int module, int bitslip) if(MMPTR(sdram_dfii_pix_rddata_addr[p]+4*(2*NBMODULES-module-1)) != prs[DFII_PIX_DATA_SIZE*p+2*NBMODULES-module-1]) working = 0; } +#ifdef ECP5DDRPHY + if (((ddrphy_burstdet_seen_read() >> module) & 0x1) != 1) + working = 0; +#endif if (show) printf("%d", working); score += working; @@ -526,6 +535,9 @@ static void read_level(int module) delay = 0; read_delay_rst(module); while(1) { +#ifdef ECP5DDRPHY + ddrphy_burstdet_clr_write(1); +#endif command_prd(DFII_COMMAND_CAS|DFII_COMMAND_CS|DFII_COMMAND_RDDATA); cdelay(15); working = 1; @@ -535,6 +547,10 @@ static void read_level(int module) if(MMPTR(sdram_dfii_pix_rddata_addr[p]+4*(2*NBMODULES-module-1)) != prs[DFII_PIX_DATA_SIZE*p+2*NBMODULES-module-1]) working = 0; } +#ifdef ECP5DDRPHY + if (((ddrphy_burstdet_seen_read() >> module) & 0x1) != 1) + working = 0; +#endif if(working) break; delay++; @@ -557,6 +573,9 @@ static void read_level(int module) /* Find largest working delay */ while(1) { +#ifdef ECP5DDRPHY + ddrphy_burstdet_clr_write(1); +#endif command_prd(DFII_COMMAND_CAS|DFII_COMMAND_CS|DFII_COMMAND_RDDATA); cdelay(15); working = 1; @@ -566,6 +585,10 @@ static void read_level(int module) if(MMPTR(sdram_dfii_pix_rddata_addr[p]+4*(2*NBMODULES-module-1)) != prs[DFII_PIX_DATA_SIZE*p+2*NBMODULES-module-1]) working = 0; } +#ifdef ECP5DDRPHY + if (((ddrphy_burstdet_seen_read() >> module) & 0x1) != 1) + working = 0; +#endif if(!working) break; delay++; diff --git a/litex/soc/software/include/hw/ethmac_mem.h b/litex/soc/software/include/hw/ethmac_mem.h deleted file mode 100644 index 03c7b96d0..000000000 --- a/litex/soc/software/include/hw/ethmac_mem.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef __HW_ETHMAC_MEM_H -#define __HW_ETHMAC_MEM_H - -#include - -#define ETHMAC_RX0_BASE ETHMAC_BASE -#define ETHMAC_RX1_BASE (ETHMAC_BASE+0x0800) -#define ETHMAC_TX0_BASE (ETHMAC_BASE+0x1000) -#define ETHMAC_TX1_BASE (ETHMAC_BASE+0x1800) - -#endif diff --git a/litex/soc/software/libnet/microudp.c b/litex/soc/software/libnet/microudp.c index 38e16588b..69aa901db 100644 --- a/litex/soc/software/libnet/microudp.c +++ b/litex/soc/software/libnet/microudp.c @@ -1,4 +1,6 @@ #include +#include + #ifdef CSR_ETHMAC_BASE #include @@ -6,7 +8,6 @@ #include #include #include -#include #include @@ -110,24 +111,17 @@ struct ethernet_frame { typedef union { struct ethernet_frame frame; - unsigned char raw[1532]; + unsigned char raw[ETHMAC_SLOT_SIZE]; } ethernet_buffer; - -static unsigned int rxslot; static unsigned int rxlen; static ethernet_buffer *rxbuffer; -static ethernet_buffer *rxbuffer0; -static ethernet_buffer *rxbuffer1; -static unsigned int txslot; static unsigned int txlen; static ethernet_buffer *txbuffer; -static ethernet_buffer *txbuffer0; -static ethernet_buffer *txbuffer1; static void send_packet(void) { - + unsigned int txslot; #ifndef HW_PREAMBLE_CRC unsigned int crc; crc = crc32(&txbuffer->raw[8], txlen-8); @@ -147,15 +141,13 @@ static void send_packet(void) printf("\n"); #endif - ethmac_sram_reader_slot_write(txslot); ethmac_sram_reader_length_write(txlen); - while(!(ethmac_sram_reader_ready_read())); ethmac_sram_reader_start_write(1); - txslot = (txslot+1)%2; - if (txslot) - txbuffer = txbuffer1; - else - txbuffer = txbuffer0; + while(!(ethmac_sram_reader_ready_read())); + txslot = ethmac_sram_reader_slot_read(); + txbuffer = (ethernet_buffer *)(ETHMAC_BASE + ETHMAC_SLOT_SIZE * (ETHMAC_RX_SLOTS + txslot)); + txslot = (txslot+1)%ETHMAC_TX_SLOTS; + ethmac_sram_reader_slot_write(txslot); } static unsigned char my_mac[6]; @@ -415,17 +407,6 @@ void microudp_start(const unsigned char *macaddr, unsigned int ip) ethmac_sram_reader_ev_pending_write(ETHMAC_EV_SRAM_READER); ethmac_sram_writer_ev_pending_write(ETHMAC_EV_SRAM_WRITER); - rxbuffer0 = (ethernet_buffer *)ETHMAC_RX0_BASE; - rxbuffer1 = (ethernet_buffer *)ETHMAC_RX1_BASE; - txbuffer0 = (ethernet_buffer *)ETHMAC_TX0_BASE; - txbuffer1 = (ethernet_buffer *)ETHMAC_TX1_BASE; - - rxslot = 0; - txslot = 0; - - rxbuffer = rxbuffer0; - txbuffer = txbuffer0; - for(i=0;i<6;i++) my_mac[i] = macaddr[i]; my_ip = ip; @@ -434,18 +415,21 @@ void microudp_start(const unsigned char *macaddr, unsigned int ip) for(i=0;i<6;i++) cached_mac[i] = 0; + ethmac_sram_reader_slot_write(0); + txbuffer = (ethernet_buffer *)(ETHMAC_BASE + ETHMAC_SLOT_SIZE * ETHMAC_RX_SLOTS); + + + rxbuffer = (ethernet_buffer *)ETHMAC_BASE; rx_callback = (udp_callback)0; } void microudp_service(void) { + unsigned int rxslot; if(ethmac_sram_writer_ev_pending_read() & ETHMAC_EV_SRAM_WRITER) { rxslot = ethmac_sram_writer_slot_read(); + rxbuffer = (ethernet_buffer *)(ETHMAC_BASE + ETHMAC_SLOT_SIZE * rxslot); rxlen = ethmac_sram_writer_length_read(); - if (rxslot) - rxbuffer = rxbuffer1; - else - rxbuffer = rxbuffer0; process_frame(); ethmac_sram_writer_ev_pending_write(ETHMAC_EV_SRAM_WRITER); } @@ -463,13 +447,12 @@ static void busy_wait(unsigned int ds) void eth_init(void) { - ethphy_crg_reset_write(0); - busy_wait(2); - /* that pesky ethernet PHY needs two resets at times... */ +#ifdef CSR_ETHPHY_CRG_RESET_ADDR ethphy_crg_reset_write(1); busy_wait(2); ethphy_crg_reset_write(0); busy_wait(2); +#endif } #ifdef CSR_ETHPHY_MODE_DETECTION_MODE_ADDR diff --git a/litex/utils/__init__.py b/litex/utils/__init__.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/litex/utils/__init__.py @@ -0,0 +1 @@ + diff --git a/litex/utils/litex_server.py b/litex/utils/litex_server.py index 8bb2f86f5..f8c6b116e 100755 --- a/litex/utils/litex_server.py +++ b/litex/utils/litex_server.py @@ -1,5 +1,7 @@ #!/usr/bin/env python3 +import argparse + import sys import socket import time @@ -10,22 +12,19 @@ from litex.soc.tools.remote.etherbone import EtherboneIPC class RemoteServer(EtherboneIPC): - def __init__(self, comm, port=1234): + def __init__(self, comm, bind_ip, bind_port=1234): self.comm = comm - self.port = port + self.bind_ip = bind_ip + self.bind_port = bind_port self.lock = False def open(self): if hasattr(self, "socket"): return self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - for i in range(32): - try: - self.socket.bind(("localhost", self.port + i)) - break - except: - pass - print("tcp port: {:d}".format(self.port + i)) + self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + self.socket.bind((self.bind_ip, self.bind_port)) + print("tcp port: {:d}".format(self.bind_port)) self.socket.listen(1) self.comm.open() @@ -95,46 +94,65 @@ class RemoteServer(EtherboneIPC): def main(): print("LiteX remote server") - if len(sys.argv) < 2 or len(sys.argv) > 4: - print("usages:") - print("litex_server uart [port] [baudrate]") - print("litex_server udp [server] [server_port]") - print("litex_server pcie [bar]") - sys.exit() - comm = sys.argv[1] - if comm == "uart": + parser = argparse.ArgumentParser() + # Common arguments + parser.add_argument("--bind-ip", default="localhost", + help="Host bind address") + parser.add_argument("--bind-port", default=1234, + help="Host bind port") + + # UART arguments + parser.add_argument("--uart", action="store_true", + help="Select UART interface") + parser.add_argument("--uart-port", default=None, + help="Set UART port") + parser.add_argument("--uart-baudrate", default=115200, + help="Set UART baudrate") + + # UDP arguments + parser.add_argument("--udp", action="store_true", + help="Select UDP interface") + parser.add_argument("--udp-ip", default="192.168.1.50", + help="Set UDP remote IP address") + parser.add_argument("--udp-port", default=1234, + help="Set UDP remote port") + + # PCIe arguments + parser.add_argument("--pcie", action="store_true", + help="Select PCIe interface") + parser.add_argument("--pcie-bar", default=None, + help="Set PCIe BAR") + args = parser.parse_args() + + + if args.uart: from litex.soc.tools.remote import CommUART - uart_port = None - uart_baudrate = 115200 - if len(sys.argv) > 2: - uart_port = sys.argv[2] - if len(sys.argv) > 3: - uart_baudrate = int(float(sys.argv[3])) + if args.uart_port is None: + print("Need to specify --uart-port, exiting.") + exit() + uart_port = args.uart_port + uart_baudrate = int(float(args.uart_baudrate)) print("[CommUART] port: {} / baudrate: {} / ".format(uart_port, uart_baudrate), end="") comm = CommUART(uart_port, uart_baudrate) - elif comm == "udp": + elif args.udp: from litex.soc.tools.remote import CommUDP - server = "192.168.1.50" - server_port = 1234 - if len(sys.argv) > 2: - server = sys.argv[2] - if len(sys.argv) > 3: - server_port = int(sys.argv[3]) - print("[CommUDP] server: {} / port: {} / ".format(server, server_port), end="") - comm = CommUDP(server, server_port) - elif comm == "pcie": + udp_ip = args.udp_ip + udp_port = int(args.udp_port) + print("[CommUDP] ip: {} / port: {} / ".format(udp_ip, udp_port), end="") + comm = CommUDP(udp_ip, udp_port) + elif args.pcie: from litex.soc.tools.remote import CommPCIe - bar = "" - if len(sys.argv) > 2: - bar = sys.argv[2] - if len(sys.argv) > 3: - bar_size = int(sys.argv[3]) - print("[CommPCIe] bar: {} / ".format(bar), end="") - comm = CommPCIe(bar) + pcie_bar = args.pcie_bar + if args.pcie_bar is None: + print("Need to speficy --pcie-bar, exiting.") + exit() + print("[CommPCIe] bar: {} / ".format(args.pcie_bar), end="") + comm = CommPCIe(args.pcie_bar) else: - raise NotImplementedError + parser.print_help() + exit() - server = RemoteServer(comm) + server = RemoteServer(comm, args.bind_ip, int(args.bind_port)) server.open() server.start(4) try: diff --git a/litex/utils/litex_sim.py b/litex/utils/litex_sim.py index 4e6eb88dc..808bd8b9b 100755 --- a/litex/utils/litex_sim.py +++ b/litex/utils/litex_sim.py @@ -215,14 +215,20 @@ def main(): sim_config = SimConfig(default_clk="sys_clk") sim_config.add_module("serial2console", "serial") + + cpu_endianness = "big" + if "cpu_type" in soc_kwargs: + if soc_kwargs["cpu_type"] in ["picorv32", "vexriscv"]: + cpu_endianness = "little" + if args.rom_init: - soc_kwargs["integrated_rom_init"] = get_mem_data(args.rom_init) + soc_kwargs["integrated_rom_init"] = get_mem_data(args.rom_init, cpu_endianness) if not args.with_sdram: - soc_kwargs["integrated_main_ram_size"] = 0x10000 + soc_kwargs["integrated_main_ram_size"] = 0x10000000 # 256 MB if args.ram_init is not None: - soc_kwargs["integrated_main_ram_init"] = get_mem_data(args.ram_init) - soc_kwargs["integrated_main_ram_size"] = max(len(soc_kwargs["integrated_main_ram_init"]), 0x10000) + soc_kwargs["integrated_main_ram_init"] = get_mem_data(args.ram_init, cpu_endianness) else: + assert args.ram_init is None soc_kwargs["integrated_main_ram_size"] = 0x0 if args.with_ethernet: sim_config.add_module("ethernet", "eth", args={"interface": "tap0", "ip": "192.168.1.100"}) @@ -235,6 +241,8 @@ def main(): with_etherbone=args.with_etherbone, with_analyzer=args.with_analyzer, **soc_kwargs) + if args.ram_init is not None: + soc.add_constant("ROM_BOOT_ADDRESS", 0x40000000) builder_kwargs["csr_csv"] = "csr.csv" builder = Builder(soc, **builder_kwargs) vns = builder.build(run=False, threads=args.threads, sim_config=sim_config, trace=args.trace) diff --git a/litex_setup.py b/litex_setup.py index 0275a0bd3..614af61af 100755 --- a/litex_setup.py +++ b/litex_setup.py @@ -15,6 +15,7 @@ repos = [ ("liteusb", ("http://github.com/enjoy-digital/", False, True)), ("litedram", ("http://github.com/enjoy-digital/", False, True)), ("litepcie", ("http://github.com/enjoy-digital/", False, True)), + ("litesata", ("http://github.com/enjoy-digital/", False, True)), ("litesdcard", ("http://github.com/enjoy-digital/", False, True)), ("liteiclink", ("http://github.com/enjoy-digital/", False, True)), ("litevideo", ("http://github.com/enjoy-digital/", False, True)),