Merge remote-tracking branch 'origin/master' into feature/mode_based_bus_crossbar

This commit is contained in:
Justin Berger 2024-08-23 16:42:23 -06:00
commit ac871c690c
109 changed files with 5387 additions and 1237 deletions

View File

@ -13,10 +13,6 @@ jobs:
- name: Setup CCache
uses: hendrikmuhs/ccache-action@v1.2
- uses: ghdl/setup-ghdl-ci@master
with:
backend: llvm
# Install Tools
- name: Install Tools
run: |
@ -47,6 +43,16 @@ jobs:
sudo python3 litex_setup.py --gcc=openrisc
sudo python3 litex_setup.py --gcc=powerpc
# Build / Install GHDL
- name: Build GHDL
run: |
sudo apt-get install gnat llvm
git clone https://github.com/ghdl/ghdl.git
cd ghdl
./configure --with-llvm-config
make
sudo make install
# Build / Install Verilator
- name: Build Verilator
run: |

View File

@ -1,5 +1,41 @@
[> Changes since 2023.12
[> Changes since 2024.04
------------------------
[> Fixed
--------
- cpu/zynq7000 : Fixed AXI version to AXI3.
- build/vhd2v_converter : Fixed instance replace robustness.
- tools/litex_json2renode : Corrected VexRiscv variants (#1984).
- software/liblitespi : Fixed xor-used-pow bug (#2001).
- soc : Fixed AHB2Wishbone bridge creation (#1998).
- soc : Fixed parameters propagation for AXI data-width conversion (#1997).
[> Added
--------
- cpu/vexiiriscv : Added initial support (#1923).
- builder : Added default generation of exports with default names to output_dir (#1978).
- litex.gen : Added byte size definitions and use them in targets/json2dts.
- litepcie : Added external QPLL support/sharing for Xilinx Artix7.
- cores/zynq7000/mp : Improved integration, added peripherals supports (#1994).
- software/bios : Generalized IRQ handling approach between CPUs.
- cores/video : Added fifo_depth parameter to add_video_framebuffer (#1931).
- gen/common : Added byte size definitions (KILOBYTE, MEGABYTE, GIGABYTE).
- tools/litex_json2dts_linux : Simplified CPU architecture/RISC-V ISA.
- soc : Added add_spi_master method (#1985).
- tools/litex_json2dts_zephyr : Added spimaster/spiflash handlers (#1985).
- tools/litex_json2renode : Added .elf bios option (#1984).
- cores : Added Watchdog core and Zephyr support (#1996).
- soc : Added add_spi_ram method (#2028).
- build : Added initial Apicula (Gowin) Platform support (#2036).
- build : Added initial Agilex5 support.
- liteeth/mac : Improved broadcast filtering logic in Hybrid Mode (https://github.com/enjoy-digital/liteeth/pull/165).
[> Changed
----------
- integration/builder : Changed export behavior to now generate csr.csv and csr.json by default to output_dir.
- csr_bus : Added .re signal (#1999).
[> 2024.04, released on June 5th 2024
-------------------------------------
[> Fixed
--------
- integration/soc : Fixed typo in cpu mem_bus axi-via-wb downconvert
@ -9,6 +45,10 @@
- litespi/software: : Fixed SPI Flash Clk Divider computation when with L2 Cache.
- litepcie/us(p)pciephy : Fixed x8 / 256-bit wide case.
- litex_sim/serial2console : Fixed RX backpressure handling.
- litedram/frontend/avalon : Fixed and cleaned-up.
- litex_sim/video : Fixed pixel format to RGBA.
- build/xilinx/common : Fixed missing clk parameter on XilinxSDRTristateImpl.
- soc/interconnect : Fixed CSR/LiteXModule issue on WishboneSRAM/AXILiteSRAM.
[> Added
--------
@ -27,6 +67,22 @@
- litex_sim : Added jtagremote support.
- soc/add_master : Added region support to allow/limit access to a specific region.
- litex_json2dts_linux : Added ip= bootarg when local/remote ips are defined.
- cores/jtag : Added JTAGBone support for Zynq.
- cores/ram/lattice_nx : Improved timings.
- liteeth_gen : Added QPLL/BUFH/BUFG parameters for A7 1000BaseX PHY.
- litex_sim : Added Video Color Bar support.
- cpu/neorv32 : Updated to v1.9.7.
- cores/hyperbus : Added latency configuration and variable latency support.
- cpu/cv32e41p : Added ISR support.
- litesdcard : Improved SDPHYClocker (Timings).
- cpu/vexriscv_smp : Added baremetal IRQ support.
- cpu/naxriscv : Added baremetal IRQ support.
- cpu/zynqmp : Added Ethernet, UART, I2C support and improved AXI Master.
- build/efinix : Added reconfiguration interface support.
- build/efinix : Added tx_output_load configuration support.
- cpu/eos_s3 : Updated qlal4s3b_cell_macro clock and reset signals.
- build/quicklogic : Updated f4pga Makefile.
- build/microsemi : Updated libero_soc toolchain.
[> Changed
----------

View File

@ -54,9 +54,9 @@ The framework is also far from perfect and we'll be happy to have your [feedback
Have fun! :wink:
**Moral precisions**: The project is shared with a permissive BSD 2-Clause License and we are encouraged to continue sharing it this way thanks to the awesome community and clients willing to support the project!
If the projet is useful for your research, hobby or commercial projects, we are just asking you to be coherent and behave with integrity: Don't expect free support or that the community will be welcoming if your spent your time complaining about the project (and then direspect developers) or don't pay the custom developments you asked for... (While it's probably natural for 99% of users/clients, it does seems useful to add this for the 1% remaining that are eating lots of our energy/time).
We share this project under a permissive BSD 2-Clause License, inspired by our fantastic community and supportive clients. If LiteX benefits your research, hobby, or commercial projects, we kindly ask for your positive collaboration and respect for the effort involved.
Thank you for helping us improve LiteX and being part of our community!
# Typical LiteX design flow:
```

View File

@ -19,4 +19,4 @@ pythondata-{dt}-{dn} module not installed! Unable to use {dn} {dt}.
You can install this by running;
pip3 install git+https://github.com/litex-hub/pythondata-{dt}-{dn}.git
""".format(dt=data_type, dn=data_name, e=e))
""".format(dt=data_type, dn=data_name, e=e)) from None

View File

@ -99,7 +99,6 @@ class AlteraDDROutputImpl(Module):
o_dataout = o,
)
class AlteraDDROutput:
@staticmethod
def lower(dr):
@ -147,3 +146,103 @@ altera_special_overrides = {
SDROutput: AlteraSDROutput,
SDRInput: AlteraSDRInput,
}
# Agilex5 DDROutput --------------------------------------------------------------------------------
class Agilex5DDROutputImpl(Module):
def __init__(self, i1, i2, o, clk):
self.specials += Instance("tennm_ph2_ddio_out",
p_mode = "MODE_DDR",
p_asclr_ena = "ASCLR_ENA_NONE",
p_sclr_ena = "SCLR_ENA_NONE",
o_dataout = o,
i_datainlo = i2,
i_datainhi = i1,
i_clk = clk,
i_ena = Constant(1, 1),
i_areset = Constant(1, 1),
i_sreset = Constant(1, 1),
)
class Agilex5DDROutput:
@staticmethod
def lower(dr):
return Agilex5DDROutputImpl(dr.i1, dr.i2, dr.o, dr.clk)
# Agilex5 DDRInput ---------------------------------------------------------------------------------
class Agilex5DDRInputImpl(Module):
def __init__(self, i, o1, o2, clk):
self.specials += Instance("tennm_ph2_ddio_in",
p_mode = "MODE_DDR",
p_asclr_ena = "ASCLR_ENA_NONE",
p_sclr_ena = "SCLR_ENA_NONE",
i_clk = clk,
i_datain = i,
o_regouthi = o1,
o_regoutlo = o2,
i_ena = Constant(1, 1),
i_areset = Constant(1, 1),
i_sreset = Constant(1, 1),
)
class Agilex5DDRInput:
@staticmethod
def lower(dr):
return Agilex5DDRInputImpl(dr.i, dr.o1, dr.o2, dr.clk)
# Agilex5 SDROutput --------------------------------------------------------------------------------
class Agilex5SDROutput:
@staticmethod
def lower(dr):
return Agilex5DDROutputImpl(dr.i, dr.i, dr.o, dr.clk)
# Agilex5 SDRInput ---------------------------------------------------------------------------------
class Agilex5SDRInput:
@staticmethod
def lower(dr):
return Agilex5DDRInputImpl(dr.i, dr.o, Signal(), dr.clk)
# Agilex5 SDRTristate ------------------------------------------------------------------------------
class Agilex5SDRTristateImpl(Module):
def __init__(self, io, o, oe, i, clk):
_i = Signal()
_o = Signal()
_oe = Signal()
self.specials += [
SDRIO(o, _o, clk),
SDRIO(oe, _oe, clk),
SDRIO(_i, i, clk),
Instance("tennm_ph2_io_ibuf",
p_bus_hold = "BUS_HOLD_OFF",
io_i = io, # FIXME: its an input but io is needed to have correct dir at top module
o_o = _i,
),
Instance("tennm_ph2_io_obuf",
p_open_drain = "OPEN_DRAIN_OFF",
i_i = _o,
i_oe = _oe,
io_o = io, # FIXME: its an output but io is needed to have correct dir at top module
),
]
class Agilex5SDRTristate(Module):
@staticmethod
def lower(dr):
return Agilex5SDRTristateImpl(dr.io, dr.o, dr.oe, dr.i, dr.clk)
# Agilex5 Special Overrides ------------------------------------------------------------------------
agilex5_special_overrides = {
AsyncResetSynchronizer: AlteraAsyncResetSynchronizer,
DifferentialInput: AlteraDifferentialInput,
DifferentialOutput: AlteraDifferentialOutput,
DDROutput: Agilex5DDROutput,
DDRInput: Agilex5DDRInput,
SDROutput: Agilex5SDROutput,
SDRInput: Agilex5SDRInput,
SDRTristate: Agilex5SDRTristate,
}

View File

@ -34,6 +34,8 @@ class AlteraPlatform(GenericPlatform):
def get_verilog(self, *args, special_overrides=dict(), **kwargs):
so = dict(common.altera_special_overrides)
if self.device[:3] == "A5E":
so.update(common.agilex5_special_overrides)
so.update(special_overrides)
return GenericPlatform.get_verilog(self, *args,
special_overrides = so,
@ -62,3 +64,11 @@ class AlteraPlatform(GenericPlatform):
for pad in common.altera_reserved_jtag_pads:
r[pad] = self.request(pad)
return r
@classmethod
def fill_args(cls, toolchain, parser):
quartus.fill_args(parser)
@classmethod
def get_argdict(cls, toolchain, args):
return quartus.get_argdict(args)

View File

@ -28,10 +28,19 @@ class AlteraQuartusToolchain(GenericToolchain):
def __init__(self):
super().__init__()
self._synth_tool = "quartus_map"
self.additional_sdc_commands = []
self.additional_qsf_commands = []
self.cst = []
def build(self, platform, fragment,
synth_tool = "quartus_map",
**kwargs):
self._synth_tool = synth_tool
return GenericToolchain.build(self, platform, fragment, **kwargs)
# IO/Placement Constraints (.qsf) --------------------------------------------------------------
def _format_constraint(self, c, signame, fmt_r):
@ -143,7 +152,8 @@ class AlteraQuartusToolchain(GenericToolchain):
# Add IPs
for filename in self.platform.ips:
qsf.append("set_global_assignment -name QSYS_FILE " + filename.replace("\\", "/"))
file_ext = os.path.splitext(filename)[1][1:].upper()
qsf.append(f"set_global_assignment -name {file_ext}_FILE " + filename.replace("\\", "/"))
# Add include paths
for path in self.platform.verilog_include_paths:
@ -178,7 +188,7 @@ class AlteraQuartusToolchain(GenericToolchain):
script_contents += "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() + "\n"
script_contents += "set -e -u -x -o pipefail\n"
script_contents += """
quartus_map --read_settings_files=on --write_settings_files=off {build_name} -c {build_name}
{synth_tool} --read_settings_files=on --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_sta {build_name} -c {build_name}"""
@ -196,7 +206,7 @@ then
quartus_cpf -c {build_name}.sof {build_name}.rbf
fi
"""
script_contents = script_contents.format(build_name=build_name)
script_contents = script_contents.format(build_name=build_name, synth_tool=self._synth_tool)
tools.write_to_file(script_file, script_contents, force_unix=True)
return script_file
@ -207,10 +217,19 @@ fi
else:
shell = ["bash"]
if which("quartus_map") is None:
if which(self._synth_tool) is None:
msg = "Unable to find Quartus toolchain, please:\n"
msg += "- Add Quartus toolchain to your $PATH."
raise OSError(msg)
if subprocess.call(shell + [script]) != 0:
raise OSError("Error occured during Quartus's script execution.")
def fill_args(parser):
toolchain_group = parser.add_argument_group(title="Quartus toolchain options")
toolchain_group.add_argument("--synth-tool", default="quartus_map", help="Synthesis mode (quartus_map or quartus_syn).")
def get_argdict(args):
return {
"synth_tool" : args.synth_tool,
}

View File

@ -162,21 +162,21 @@ class EfinixSDRTristate(Module):
class EfinixDifferentialOutputImpl(Module):
def __init__(self, platform, i, o_p, o_n):
# only keep _p
# only keep _p
io_name = platform.get_pin_name(o_p)
io_pad = platform.get_pad_name(o_p) # need real pad name
io_prop = platform.get_pin_properties(o_p)
if platform.family == "Titanium":
# _p has _P_ and _n has _N_ followed by an optional function
# lvds block needs _PN_
# lvds block needs _PN_
pad_split = io_pad.split('_')
assert pad_split[1] == 'P'
io_pad = f"{pad_split[0]}_PN_{pad_split[2]}"
else:
assert "TXP" in io_pad
# diff output pins are TXPYY and TXNYY
# lvds block needs TXYY
# lvds block needs TXYY
io_pad = io_pad.replace("TXP", "TX")
platform.add_extension([(io_name, 0, Pins(1))])
@ -206,21 +206,21 @@ class EfinixDifferentialOutput:
class EfinixDifferentialInputImpl(Module):
def __init__(self, platform, i_p, i_n, o):
# only keep _p
# only keep _p
io_name = platform.get_pin_name(i_p)
io_pad = platform.get_pad_name(i_p) # need real pad name
io_prop = platform.get_pin_properties(i_p)
if platform.family == "Titanium":
# _p has _P_ and _n has _N_ followed by an optional function
# lvds block needs _PN_
# lvds block needs _PN_
pad_split = io_pad.split('_')
assert pad_split[1] == 'P'
io_pad = f"{pad_split[0]}_PN_{pad_split[2]}"
else:
assert "RXP" in io_pad
# diff input pins are RXPYY and RXNYY
# lvds block needs RXYY
# lvds block needs RXYY
io_pad = io_pad.replace("RXP", "RX")
platform.add_extension([

View File

@ -107,9 +107,9 @@ class EfinixDbParser:
peri = root.findall('efxpt:periphery_instance', namespaces)
for p in peri:
# T20/T120 have instance attribute in single_conn
# not true for T4/T8 -> search in dependency subnode
# not true for T4/T8 (except for TQFP144 package) -> search in dependency subnode
if p.get('block') == 'pll':
if self.device[0:2] not in ['T4', 'T8']:
if self.device[0:2] not in ['T4', 'T8'] or self.device[0:6] == "T8Q144":
conn = p.findall('efxpt:single_conn', namespaces)
for c in conn:
i = c.get('instance')

View File

@ -50,7 +50,23 @@ class EfinityToolchain(GenericToolchain):
if self.platform.verilog_include_paths:
self.options["includ_path"] = "{" + ";".join(self.platform.verilog_include_paths) + "}"
def build(self, platform, fragment, **kwargs):
def build(self, platform, fragment,
synth_mode = "speed",
infer_clk_enable = "3",
bram_output_regs_packing = "1",
retiming = "1",
seq_opt = "1",
mult_input_regs_packing = "1",
mult_output_regs_packing = "1",
**kwargs):
self._synth_mode = synth_mode
self._infer_clk_enable = infer_clk_enable
self._bram_output_regs_packing = bram_output_regs_packing
self._retiming = retiming
self._seq_opt = seq_opt
self._mult_input_regs_packing = mult_input_regs_packing
self._mult_output_regs_packing = mult_output_regs_packing
# Apply FullMemoryWE on Design (Efiniy does not infer memories correctly otherwise).
FullMemoryWE()(fragment)
@ -270,11 +286,11 @@ class EfinityToolchain(GenericToolchain):
# Some IO blocks don't have Python API so we need to configure them
# directly in the peri.xml file
# We also need to configure the bank voltage here
# We also need to configure the bank voltage here
if self.ifacewriter.xml_blocks or self.platform.iobank_info:
self.ifacewriter.generate_xml_blocks()
# Because the Python API is sometimes bugged, we need to tweak the generated xml
# Because the Python API is sometimes bugged, we need to tweak the generated xml
if self.ifacewriter.fix_xml:
self.ifacewriter.fix_xml_values()
@ -295,18 +311,18 @@ class EfinityToolchain(GenericToolchain):
"--binary-db", f"{self._build_name}.vdb",
"--family", self.platform.family,
"--device", self.platform.device,
"--mode", "speed",
"--mode", self._synth_mode,
"--max_ram", "-1",
"--max_mult", "-1",
"--infer-clk-enable", "3",
"--infer-clk-enable", self._infer_clk_enable,
"--infer-sync-set-reset", "1",
"--fanout-limit", "0",
"--bram_output_regs_packing", "1",
"--retiming", "1",
"--seq_opt", "1",
"--bram_output_regs_packing", self._bram_output_regs_packing,
"--retiming", self._retiming,
"--seq_opt", self._seq_opt,
"--blast_const_operand_adders", "1",
"--mult_input_regs_packing", "1",
"--mult_output_regs_packing", "1",
"--mult_input_regs_packing", self._mult_input_regs_packing,
"--mult_output_regs_packing", self._mult_output_regs_packing,
"--veri_option", "verilog_mode=verilog_2k,vhdl_mode=vhdl_2008",
"--work-dir", "work_syn",
"--output-dir", "outflow",
@ -377,3 +393,33 @@ class EfinityToolchain(GenericToolchain):
], common.colors)
if r != 0:
raise OSError("Error occurred during export_bitstream execution.")
def build_args(parser):
toolchain = parser.add_argument_group(title="Efinity toolchain options")
toolchain.add_argument("--synth-mode", default="speed", help="Synthesis optimization mode.",
choices=["speed", "area", "area2"])
toolchain.add_argument("--infer-clk-enable", default="3", help="infers the flip-flop clock enable signal.",
choices=["0", "1", "2", "3", "4",])
toolchain.add_argument("--infer-sync-set-reset", default="1", help="Infer synchronous set/reset signals.",
choices=["0", "1"])
toolchain.add_argument("--bram-output-regs-packing", default="1", help="Pack registers into the output of BRAM.",
choices=["0", "1"])
toolchain.add_argument("--retiming", default="1", help="Perform retiming optimization.",
choices=["0", "1", "2"])
toolchain.add_argument("--seq-opt", default="1", help="Turn on sequential optimization.",
choices=["0", "1"])
toolchain.add_argument("--mult-input-regs-packing", default="1", help="Allow packing of multiplier input registers.",
choices=["0", "1"]),
toolchain.add_argument("--mult-output-regs-packing", default="1", help="Allow packing of multiplier output registers.",
choices=["0", "1"])
def build_argdict(args):
return {
"synth_mode" : args.synth_mode,
"infer_clk_enable" : args.infer_clk_enable,
"bram_output_regs_packing" : args.bram_output_regs_packing,
"retiming" : args.retiming,
"seq_opt" : args.seq_opt,
"mult_input_regs_packing" : args.mult_input_regs_packing,
"mult_output_regs_packing" : args.mult_output_regs_packing,
}

View File

@ -289,7 +289,7 @@ design.create("{2}", "{3}", "./../gateware", overwrite=True)
elif block["input_clock"] == "EXTERNAL":
# PLL V1 has a different configuration
if partnumber[0:2] in ["T4", "T8"]:
if partnumber[0:2] in ["T4", "T8"] and partnumber != "T8Q144":
cmd += 'design.gen_pll_ref_clock("{}", pll_res="{}", refclk_res="{}", refclk_name="{}", ext_refclk_no="{}")\n\n' \
.format(name, block["resource"], block["input_clock_pad"], block["input_clock_name"], block["clock_no"])
else:
@ -329,7 +329,7 @@ design.create("{2}", "{3}", "./../gateware", overwrite=True)
cmd += 'design.set_property("{}", "FEEDBACK_MODE", "{}", "PLL")\n'.format(name, "LOCAL" if feedback_clk < 1 else "CORE")
cmd += 'design.set_property("{}", "FEEDBACK_CLK", "CLK{}", "PLL")\n'.format(name, 0 if feedback_clk < 1 else feedback_clk)
# auto_calc_pll_clock is always working with Titanium and only working when feedback is unused for Trion
# auto_calc_pll_clock is always working with Titanium and only working when feedback is unused for Trion
if block["feedback"] == -1 or block["version"] == "V3":
cmd += "target_freq = {\n"
for i, clock in enumerate(block["clk_out"]):
@ -425,6 +425,7 @@ design.create("{2}", "{3}", "./../gateware", overwrite=True)
fast_clk = block.get("fast_clk", "")
slow_clk = block.get("slow_clk", "")
half_rate= block.get("half_rate", "0")
tx_output_load=block.get("output_load", "3")
if mode == "OUTPUT":
block_type = "LVDS_TX"
@ -443,7 +444,7 @@ design.create("{2}", "{3}", "./../gateware", overwrite=True)
cmd.append('design.set_property("{}", "TX_PRE_EMP", "MEDIUM_LOW", "{}")'.format(name, block_type))
cmd.append('design.set_property("{}", "TX_VOD", "TYPICAL", "{}")'.format(name, block_type))
else:
cmd.append('design.set_property("{}", "TX_OUTPUT_LOAD", "3", "{}")'.format(name, block_type))
cmd.append('design.set_property("{}", "TX_OUTPUT_LOAD", "{}", "{}")'.format(name, tx_output_load, block_type))
cmd.append('design.set_property("{}", "TX_REDUCED_SWING", "0", "{}")'.format(name, block_type))
cmd.append('design.set_property("{}", "TX_SLOWCLK_DIV", "1", "{}")'.format(name, block_type))
cmd.append('design.set_property("{}", "TX_SER", "{}", "{}")'.format(name, size, block_type))
@ -602,6 +603,31 @@ design.create("{2}", "{3}", "./../gateware", overwrite=True)
return '\n'.join(cmd) + '\n'
def generate_remote_update(self, block, verbose=True):
name = block["name"]
pins = block["pins"]
clock = block["clock"]
invert_clk = block["invert_clock"]
enable = block["enable"]
def get_pin_name(pin):
return pin.backtrace[-1][0]
cmds = []
cmds.append(f"# ---------- REMOTE UPDATE ---------")
cmds.append(f'design.set_device_property("ru", "RECONFIG_EN", "{enable}", "RU")')
if enable:
cmds.append(f'design.set_device_property("ru", "CBSEL_PIN", "{get_pin_name(pins.CBSEL)}", "RU")')
cmds.append(f'design.set_device_property("ru", "CLK_PIN", "{clock}", "RU")')
cmds.append(f'design.set_device_property("ru", "CONFIG_PIN", "{get_pin_name(pins.CONFIG)}", "RU")')
cmds.append(f'design.set_device_property("ru", "ENA_PIN", "{get_pin_name(pins.ENA)}", "RU")')
cmds.append(f'design.set_device_property("ru", "ERROR_PIN", "{get_pin_name(pins.ERROR)}", "RU")')
if hasattr(pins, 'IN_USER'):
cmds.append(f'design.set_device_property("ru", "IN_USER_PIN", "{get_pin_name(pins.IN_USER)}", "RU")')
cmds.append(f'design.set_device_property("ru", "INVERT_CLK_EN", "{invert_clk}", "RU")')
cmds.append(f"# ---------- END REMOTE UPDATE ---------\n")
return "\n".join(cmds)
def generate(self, partnumber):
output = ""
for block in self.blocks:
@ -624,6 +650,8 @@ design.create("{2}", "{3}", "./../gateware", overwrite=True)
output += self.generate_jtag(block)
if block["type"] == "SPI_FLASH":
output += self.generate_spiflash(block)
if block["type"] == "REMOTE_UPDATE":
output += self.generate_remote_update(block)
return output
def footer(self):

View File

@ -125,26 +125,24 @@ class EfinixPlatform(GenericPlatform):
sig = sig.value
return sig
def get_pin_name(self, sig, without_index=False):
def get_pin_name(self, sig):
if sig is None:
return None
assert len(sig) == 1
idx = 0
slc = False
while isinstance(sig, _Slice) and hasattr(sig, "value"):
slc = True
idx = sig.start
sig = sig.value
slc = hasattr(sig, "nbits") and sig.nbits > 1
sc = self.constraint_manager.get_sig_constraints()
for s, pins, others, resource in sc:
if s == sig:
name = resource[0] + (f"{resource[1]}" if resource[1] is not None else "")
if resource[2]:
name = resource[0] + "_" + resource[2]
if without_index is False:
name = name + (f"{idx}" if slc else "")
return name
else:
return resource[0] + (f"{idx}" if slc else "")
name = name + "_" + resource[2]
name = name + (f"{idx}" if slc else "")
return name
return None
def get_pad_name(self, sig):
@ -187,3 +185,34 @@ class EfinixPlatform(GenericPlatform):
pll = self.pll_available[0]
self.get_pll_resource(pll)
return pll
@classmethod
def fill_args(cls, toolchain, parser):
"""
pass parser to the specific toolchain to
fill this with toolchain args
Parameters
==========
toolchain: str
toolchain name
parser: argparse.ArgumentParser
parser to be filled
"""
efinity.build_args(parser)
@classmethod
def get_argdict(cls, toolchain, args):
"""
return a dict of args
Parameters
==========
toolchain: str
toolchain name
Return
======
a dict of key/value for each args or an empty dict
"""
return efinity.build_argdict(args)

View File

@ -0,0 +1,5 @@
# Platforms.
from litex.build.gowin.platform import GowinPlatform
# Programmers.
from litex.build.gowin.programmer import GowinProgrammer

View File

@ -0,0 +1,54 @@
#
# This file is part of LiteX.
#
# Copyright (c) 2024 Mai Lapyst
# SPDX-License-Identifier: BSD-2-Clause
from litex.build.generic_platform import *
from litex.build import tools
from litex.build.gowin.gowin import _build_cst
from litex.build.yosys_nextpnr_toolchain import YosysNextPNRToolchain
class GowinApiculaToolchain(YosysNextPNRToolchain):
family = "gowin"
synth_fmt = "json"
pnr_fmt = "report"
packer_cmd = "gowin_pack"
def __init__(self):
super().__init__()
self.options = {}
self.additional_cst_commands = []
def build_io_constraints(self):
_build_cst(self.named_sc, self.named_pc, self.additional_cst_commands, self._build_name)
return (self._build_name + ".cst", "CST")
def finalize(self):
pnr_opts = "--write {top}_routed.json --top {top} --device {device}" + \
" --vopt family={devicename} --vopt cst={top}.cst"
self._pnr_opts += pnr_opts.format(
top = self._build_name,
device = self.platform.device,
devicename = self.platform.devicename
)
self._packer_opts += "-d {devicename} -o {top}.fs {top}_routed.json".format(
devicename = self.platform.devicename,
top = self._build_name
)
# use_mspi_as_gpio and friends
for option, value in self.options.items():
if option.startswith("use_") and value:
self._packer_opts += " --" + option[4:]
YosysNextPNRToolchain.finalize(self)
# family is gowin but NextPNRWrapper needs to call 'nextpnr-himbaechel' not 'nextpnr-gowin'
self._nextpnr.name = "nextpnr-himbaechel"
def build(self, platform, fragment, **kwargs):
self.platform = platform
return YosysNextPNRToolchain.build(self, platform, fragment, **kwargs)

View File

@ -100,9 +100,19 @@ class GowinDifferentialOutput:
def lower(dr):
return GowinDifferentialOutputImpl(dr.i, dr.o_p, dr.o_n)
# Gowin Tristate -----------------------------------------------------------------------------------
# Gowin Special Overrides --------------------------------------------------------------------------
class GowinTristateImpl(Module):
gowin_special_overrides = {
AsyncResetSynchronizer: GowinAsyncResetSynchronizer,
DDRInput: GowinDDRInput,
DDROutput: GowinDDROutput,
DifferentialInput: GowinDifferentialInput,
DifferentialOutput: GowinDifferentialOutput,
}
# Gw5A Tristate ------------------------------------------------------------------------------------
class Gw5ATristateImpl(Module):
def __init__(self, io, o, oe, i):
nbits, _ = value_bits_sign(io)
for bit in range(nbits):
@ -113,18 +123,74 @@ class GowinTristateImpl(Module):
i_OEN = ~oe,
)
class GowinTristate:
class Gw5ATristate:
@staticmethod
def lower(dr):
return GowinTristateImpl(dr.target, dr.o, dr.oe, dr.i)
return Gw5ATristateImpl(dr.target, dr.o, dr.oe, dr.i)
# Gowin Special Overrides --------------------------------------------------------------------------
# Gw5A SDROutput -----------------------------------------------------------------------------------
gowin_special_overrides = {
AsyncResetSynchronizer: GowinAsyncResetSynchronizer,
DDRInput: GowinDDRInput,
DDROutput: GowinDDROutput,
DifferentialInput: GowinDifferentialInput,
DifferentialOutput: GowinDifferentialOutput,
#Tristate: GowinTristate, # FIXME: issue with tangNano9k hyperram
class Gw5ASDROutputImpl(Module):
def __init__(self, i, o, clk):
self.specials += Instance("DFFSE",
i_D = i,
o_Q = o,
i_CLK = clk,
i_SET = Constant(0,1),
i_CE = Constant(1,1),
)
class Gw5ASDROutput:
@staticmethod
def lower(dr):
return Gw5ASDROutputImpl(dr.i, dr.o, dr.clk)
# Gw5A SDRInput ------------------------------------------------------------------------------------
class Gw5ASDRInputImpl(Module):
def __init__(self, i, o, clk):
self.specials += Instance("DFFSE",
i_D = i,
o_Q = o,
i_CLK = clk,
i_SET = Constant(0,1),
i_CE = Constant(1,1),
)
class Gw5ASDRInput:
@staticmethod
def lower(dr):
return Gw5ASDRInputImpl(dr.i, dr.o, dr.clk)
# Gw5A SDRTristate ---------------------------------------------------------------------------------
class Gw5ASDRTristateImpl(Module):
def __init__(self, io, o, oe, i, clk):
_o = Signal()
_oe_n = Signal()
_i = Signal()
self.specials += [
SDROutput(o, _o, clk),
SDROutput(~oe, _oe_n, clk),
SDRInput(_i, i, clk),
Instance("IOBUF",
io_IO = io,
o_O = _i,
i_I = _o,
i_OEN = _oe_n,
),
]
class Gw5ASDRTristate:
@staticmethod
def lower(dr):
return Gw5ASDRTristateImpl(dr.io, dr.o, dr.oe, dr.i, dr.clk)
# Gw5A Special Overrides ---------------------------------------------------------------------------
gw5a_special_overrides = {
SDRTristate: Gw5ASDRTristate,
SDROutput: Gw5ASDROutput,
SDRInput: Gw5ASDRInput,
Tristate: Gw5ATristate,
}

View File

@ -17,6 +17,61 @@ from litex.build.generic_toolchain import GenericToolchain
from litex.build.generic_platform import *
from litex.build import tools
# Constraints (.cst) -------------------------------------------------------------------------------
def _build_cst(named_sc, named_pc, additional_cst_commands, build_name):
cst = []
flat_sc = []
for name, pins, other, resource in named_sc:
if len(pins) > 1:
for i, p in enumerate(pins):
flat_sc.append((f"{name}[{i}]", p, other))
else:
flat_sc.append((name, pins[0], other))
def _search_pin_entry(pin_lst, pin_name):
for name, pin, other in pin_lst:
if pin_name == name:
return (name, pin, other)
return (None, None, None)
for name, pin, other in flat_sc:
if pin != "X":
t_name = name.split('[') # avoid index pins
tmp_name = t_name[0]
if tmp_name[-2:] == "_p":
pn = tmp_name[:-2] + "_n"
if len(t_name) > 1:
pn += '[' + t_name[1]
(_, n_pin, _) = _search_pin_entry(flat_sc, pn)
if n_pin is not None:
pin = f"{pin},{n_pin}"
elif tmp_name[-2:] == "_n":
pp = tmp_name[:-2] + "_p"
if len(t_name) > 1:
pp += '[' + t_name[1]
(p_name, _, _) = _search_pin_entry(flat_sc, pp)
if p_name is not None:
continue
cst.append(f"IO_LOC \"{name}\" {pin};")
other_cst = []
for c in other:
if isinstance(c, IOStandard):
other_cst.append(f"IO_TYPE={c.name}")
elif isinstance(c, Misc):
other_cst.append(f"{c.misc}")
if len(other_cst):
t = " ".join(other_cst)
cst.append(f"IO_PORT \"{name}\" {t};")
if named_pc:
cst.extend(named_pc)
cst.extend(additional_cst_commands)
tools.write_to_file(build_name + ".cst", "\n".join(cst))
# GowinToolchain -----------------------------------------------------------------------------------
@ -60,58 +115,7 @@ class GowinToolchain(GenericToolchain):
# Constraints (.cst ) --------------------------------------------------------------------------
def build_io_constraints(self):
cst = []
flat_sc = []
for name, pins, other, resource in self.named_sc:
if len(pins) > 1:
for i, p in enumerate(pins):
flat_sc.append((f"{name}[{i}]", p, other))
else:
flat_sc.append((name, pins[0], other))
def _search_pin_entry(pin_lst, pin_name):
for name, pin, other in pin_lst:
if pin_name == name:
return (name, pin, other)
return (None, None, None)
for name, pin, other in flat_sc:
if pin != "X":
t_name = name.split('[') # avoid index pins
tmp_name = t_name[0]
if tmp_name[-2:] == "_p":
pn = tmp_name[:-2] + "_n"
if len(t_name) > 1:
pn += '[' + t_name[1]
(_, n_pin, _) = _search_pin_entry(flat_sc, pn)
if n_pin is not None:
pin = f"{pin},{n_pin}"
elif tmp_name[-2:] == "_n":
pp = tmp_name[:-2] + "_p"
if len(t_name) > 1:
pp += '[' + t_name[1]
(p_name, _, _) = _search_pin_entry(flat_sc, pp)
if p_name is not None:
continue
cst.append(f"IO_LOC \"{name}\" {pin};")
other_cst = []
for c in other:
if isinstance(c, IOStandard):
other_cst.append(f"IO_TYPE={c.name}")
elif isinstance(c, Misc):
other_cst.append(f"{c.misc}")
if len(other_cst):
t = " ".join(other_cst)
cst.append(f"IO_PORT \"{name}\" {t};")
if self.named_pc:
cst.extend(self.named_pc)
cst.extend(self.additional_cst_commands)
tools.write_to_file(f"{self._build_name}.cst", "\n".join(cst))
_build_cst(self.named_sc, self.named_pc, self.additional_cst_commands, self._build_name)
return (f"{self._build_name}.cst", "CST")
# Timing Constraints (.sdc ) -------------------------------------------------------------------

View File

@ -8,7 +8,7 @@
import os
from litex.build.generic_platform import GenericPlatform
from litex.build.gowin import common, gowin
from litex.build.gowin import common, gowin, apicula
# GowinPlatform ------------------------------------------------------------------------------------
@ -28,12 +28,14 @@ class GowinPlatform(GenericPlatform):
if toolchain == "gowin":
self.toolchain = gowin.GowinToolchain()
elif toolchain == "apicula":
raise ValueError("Apicula toolchain needs more work")
self.toolchain = apicula.GowinApiculaToolchain()
else:
raise ValueError(f"Unknown toolchain {toolchain}")
def get_verilog(self, *args, special_overrides=dict(), **kwargs):
so = dict(common.gowin_special_overrides)
if self.device[:4] == "GW5A":
so.update(common.gw5a_special_overrides)
so.update(special_overrides)
return GenericPlatform.get_verilog(self, *args,
special_overrides = so,

View File

@ -83,9 +83,10 @@ class LatticeRadiantToolchain(GenericToolchain):
def __init__(self):
super().__init__()
self._timingstrict = False
self._synth_mode = "radiant"
self._yosys = None
self._timingstrict = False
self._synth_mode = "radiant"
self._yosys = None
self._prj_strategy_opts = {}
def build(self, platform, fragment,
timingstrict = False,
@ -176,6 +177,10 @@ class LatticeRadiantToolchain(GenericToolchain):
# Set top level
tcl.append("prj_set_impl_opt top \"{}\"".format(self._build_name))
# Set user project extra configurations
for k,v in self._prj_strategy_opts.items():
tcl.append(f"prj_set_strategy_value {k}={v}")
# Save project
tcl.append("prj_save")
@ -283,6 +288,16 @@ class LatticeRadiantToolchain(GenericToolchain):
return
raise Exception("Failed to meet timing")
"""
Set optional configuration for syn, par, bit.
Attributes
==========
strategy_opts: dict
keys/values to inject at script creation time
"""
def set_prj_strategy_opts(self, strategy_opts={}):
self._prj_strategy_opts.update(strategy_opts)
def radiant_build_args(parser):
toolchain_group = parser.add_argument_group(title="Radiant toolchain options")

View File

@ -8,6 +8,7 @@ import os
import sys
import subprocess
import shutil
from shutil import which
from migen.fhdl.structure import _Fragment
@ -83,6 +84,7 @@ class MicrosemiLiberoSoCPolarfireToolchain(GenericToolchain):
def build_project(self):
tcl = []
die, package, speed = self.platform.device.split("-")
# Create project
tcl.append(" ".join([
"new_project",
@ -96,49 +98,33 @@ class MicrosemiLiberoSoCPolarfireToolchain(GenericToolchain):
"-use_enhanced_constraint_flow 1",
"-hdl {VERILOG}",
"-family {PolarFire}",
"-die {}",
"-package {}",
"-speed {}",
"-die_voltage {}",
"-part_range {}",
"-adv_options {}"
]))
die, package, speed = self.platform.device.split("-")
tcl.append(" ".join([
"set_device",
"-family {PolarFire}",
"-die {}".format(self.tcl_name(die)),
"-package {}".format(self.tcl_name(package)),
"-speed {}".format(self.tcl_name("-" + speed)),
# FIXME: common to all PolarFire devices?
"-die_voltage {1.0}",
"-part_range {EXT}",
"-adv_options {IO_DEFT_STD:LVCMOS 1.8V}",
"-adv_options {RESTRICTPROBEPINS:1}",
"-adv_options {RESTRICTSPIPINS:0}",
"-adv_options {TEMPR:EXT}",
"-adv_options {UNUSED_MSS_IO_RESISTOR_PULL:None}",
"-adv_options {VCCI_1.2_VOLTR:EXT}",
"-adv_options {VCCI_1.5_VOLTR:EXT}",
"-adv_options {VCCI_1.8_VOLTR:EXT}",
"-adv_options {VCCI_2.5_VOLTR:EXT}",
"-adv_options {VCCI_3.3_VOLTR:EXT}",
"-adv_options {VOLTR:EXT} "
]))
"-part_range {IND}",
"-adv_options {VCCI_1.2_VOLTR:IND}",
"-adv_options {VCCI_1.5_VOLTR:IND}",
"-adv_options {VCCI_1.8_VOLTR:IND}",
"-adv_options {VCCI_2.5_VOLTR:IND}",
"-adv_options {VCCI_3.3_VOLTR:IND}"
]))
# Add sources
for filename, language, library, *copy in self.platform.sources:
filename_tcl = "{" + filename + "}"
tcl.append("import_files -hdl_source " + filename_tcl)
# Building the design Hierarchy
tcl.append("build_design_hierarchy")
# Set top level
tcl.append("set_root -module {}".format(self.tcl_name(self._build_name)))
tcl.append("set_root -module {}".format(self.tcl_name(self._build_name + "::work")))
# Copy init files FIXME: support for include path on LiberoSoC?
for file in os.listdir(self._build_dir):
if file.endswith(".init"):
tcl.append("file copy -- {} impl/synthesis".format(file))
# Commenting out copy init file as this breaks the LiberoSoC flow.
#for file in os.listdir(self._build_dir):
# if file.endswith(".init"):
# tcl.append("file copy -- {} impl/synthesis".format(file))
# Import io constraints
tcl.append("import_files -io_pdc {}".format(self.tcl_name(self._build_name + "_io.pdc")))
@ -177,6 +163,23 @@ class MicrosemiLiberoSoCPolarfireToolchain(GenericToolchain):
tcl.append("run_tool -name {PLACEROUTE}")
tcl.append("run_tool -name {GENERATEPROGRAMMINGDATA}")
tcl.append("run_tool -name {GENERATEPROGRAMMINGFILE}")
# Export the FPExpress programming file to Libero SoC default location
tcl.append(" export_prog_job \
-job_file_name {top} \
-export_dir {./impl/designer/top/export} \
-bitstream_file_type {TRUSTED_FACILITY} \
-bitstream_file_components {FABRIC SNVM} \
-zeroization_likenew_action 0 \
-zeroization_unrecoverable_action 0 \
-program_design 1 \
-program_spi_flash 0 \
-include_plaintext_passkey 0 \
-design_bitstream_format {PPD} \
-prog_optional_procedures {} \
-skip_recommended_procedures {} \
-sanitize_snvm 0 ")
# Generate tcl
tools.write_to_file(self._build_name + ".tcl", "\n".join(tcl))
@ -221,6 +224,7 @@ class MicrosemiLiberoSoCPolarfireToolchain(GenericToolchain):
copy_stmt = "cp"
fail_stmt = " || exit 1"
script_contents += "libero script:" + self._build_name + ".tcl\n"
script_file = "build_" + self._build_name + script_ext
tools.write_to_file(script_file, script_contents,
force_unix=False)
@ -236,8 +240,12 @@ class MicrosemiLiberoSoCPolarfireToolchain(GenericToolchain):
else:
shell = ["bash"]
if which("libero") is None:
msg = "Unable to find or source Libero SoC toolchain, please make sure libero has been installed corectly.\n"
raise OSError(msg)
if subprocess.call(shell + [script]) != 0:
raise OSError("Subprocess failed")
raise OSError("Subprocess failed")
def add_false_path_constraint(self, platform, from_, to):
if (to, from_) not in self.false_paths:

View File

@ -83,9 +83,10 @@ class NextPNRWrapper():
=======
str containing instruction and/or rule
"""
cmd = "{pnr_name} --{in_fmt} {build_name}.{in_fmt} --{constr_fmt}" + \
" {build_name}.{constr_fmt}" + \
" --{out_fmt} {build_name}.{out_ext} {pnr_opts}"
cmd = "{pnr_name} --{in_fmt} {build_name}.{in_fmt}"
if self._constr_format != "":
cmd += " --{constr_fmt} {build_name}.{constr_fmt}"
cmd += " --{out_fmt} {build_name}.{out_ext} {pnr_opts}"
base_cmd = cmd.format(
pnr_name = self.name,
build_name = self._build_name,

View File

@ -41,6 +41,7 @@ class OpenFPGALoader(GenericProgrammer):
cmd = self.cmd + ["--bitstream", bitstream_file]
# Execute command.
print(" ".join(cmd))
self.call(cmd)
def flash(self, address, data_file, external=False, unprotect_flash=False, verify=False, **kwargs):
@ -72,6 +73,7 @@ class OpenFPGALoader(GenericProgrammer):
# Execute Command.
try:
print(" ".join(cmd))
self.call(cmd)
except OSError as e:
print(' '.join(cmd))

View File

@ -27,12 +27,13 @@ class OpenOCD(GenericProgrammer):
])
self.call(["openocd", "-f", config, "-c", script])
def flash(self, address, data, set_qe=False):
def flash(self, address, data, set_qe=False, init_commands=[]):
config = self.find_config()
flash_proxy = self.find_flash_proxy()
script = "; ".join([
"init",
"jtagspi_init 0 {{{}}}".format(flash_proxy),
"jtagspi_init 0 {{{}}}".format(flash_proxy)
] + init_commands + [
"jtagspi set_qe 0 1" if set_qe else "",
"jtagspi_program {{{}}} 0x{:x}".format(data, address),
"fpga_program",
@ -192,7 +193,7 @@ proc jtagstream_serve {tap port} {
write_to_file("stream.cfg", cfg)
script = "; ".join([
"init",
"poll off",
#"poll off", # FIXME: not supported for ECP5
"irscan {} {:d}".format(tap_name, ir),
"jtagstream_serve {} {:d}".format(tap_name, port),
"exit",

View File

@ -217,13 +217,21 @@ class LiteXArgumentParser(argparse.ArgumentParser):
self._platform.fill_args(self._toolchain, self)
# Intercept selected CPU to fill arguments.
cpu_cls = cpu.CPUS.get(self.get_value_from_key("--cpu-type"), None)
default_cpu_type = self._args_default.get("cpu_type", None)
cpu_cls = cpu.CPUS.get(self.get_value_from_key("--cpu-type", default_cpu_type))
if cpu_cls is not None and hasattr(cpu_cls, "args_fill"):
cpu_cls.args_fill(self)
# Injects arguments default values
if len(self._args_default):
argparse.ArgumentParser.set_defaults(self, **self._args_default)
# Catch defaults which do not match any arguments - typos?
remaining = list(self._args_default.keys())
for action in self._actions:
if action.dest in remaining:
remaining.remove(action.dest)
if len(remaining) > 0:
raise ValueError(f"set_default() for invalid argument(s): {remaining}")
# Parse args.
self._args = argparse.ArgumentParser.parse_args(self, args, namespace)

View File

@ -5,9 +5,13 @@
# Copyright (c) 2021 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
# SPDX-License-Identifier: BSD-2-Clause
# See: https://f4pga-examples.readthedocs.io/en/latest/getting.html#toolchain-installation
# To install toolchain
import os
import sys
import subprocess
import json
from shutil import which
from migen.fhdl.structure import _Fragment
@ -47,48 +51,57 @@ class F4PGAToolchain(GenericToolchain):
tools.write_to_file(self._build_name + ".pcf", pcf)
return (self._build_name + ".pcf", "PCF")
# Build Makefile -------------------------------------------------------------------------------
# Timing constraints (.sdc) --------------------------------------------------------------------
def build_timing_constraints(self, vns):
sdc = []
for clk, [period, name] in sorted(self.clocks.items(), key=lambda x: x[0].duid):
clk_sig = vns.get_name(clk)
sdc.append("create_clock -period {} {}".format(str(period), clk_sig))
tools.write_to_file(self._build_name + "_in.sdc", "\n".join(sdc))
return (self._build_name + "_in.sdc", "SDC")
# Build flow.json ------------------------------------------------------------------------------
def build_script(self):
makefile = []
part = {"ql-eos-s3": "PU64"}.get(self.platform.device)
flow = {
"default_part": "EOS3FF512-PDN64",
"values": {
"top": self._build_name
},
"dependencies": {
"sources": [
f"{self._build_name}.v"
],
"synth_log": "synth.log",
"pack_log": "pack.log",
"analysis_log": "analysis.log"
},
"EOS3FF512-PDN64": {
"default_target": "bitstream",
"dependencies": {
"build_dir": self._build_dir,
"pcf": f"{self._build_name}.pcf",
"sdc-in": f"{self._build_name}_in.sdc"
},
"values": {
"part": self.platform.device,
"package": part
}
}
};
# Define Paths.
makefile.append("mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))")
makefile.append("current_dir := $(patsubst %/,%,$(dir $(mkfile_path)))")
# bit -> h and bit -> bin requires TOP_F
makefile.append(f"TOP_F={self._build_name}")
# Create Project.
# FIXME: Only use top file for now and ignore .init files.
makefile.append("all: {top}_bit.h {top}.bin build/{top}.bit".format(top=self._build_name))
# build bit file (default)
makefile.append(f"build/{self._build_name}.bit:")
makefile.append("\tql_symbiflow -compile -d {device} -P {part} -v {verilog} -t {top} -p {pcf}".format(
device = self.platform.device,
part = {"ql-eos-s3": "PU64"}.get(self.platform.device),
verilog = f"{self._build_name}.v",
top = self._build_name,
pcf = f"{self._build_name}.pcf"
))
# build header to include in CPU firmware
makefile.append("{top}_bit.h: build/{top}.bit".format(top=self._build_name))
makefile.append(f"\t(cd build; TOP_F=$(TOP_F) symbiflow_write_bitheader)")
# build binary to write in dedicated FLASH area
makefile.append("{top}.bin: build/{top}.bit".format(top=self._build_name))
makefile.append(f"\t(cd build; TOP_F=$(TOP_F) symbiflow_write_binary)")
# Generate Makefile.
tools.write_to_file("Makefile", "\n".join(makefile))
return "Makefile"
tools.write_to_file("flow.json", json.dumps(flow))
return "flow.json"
def run_script(self, script):
make_cmd = ["make", "-j1"]
if which("ql_symbiflow") is None:
msg = "Unable to find QuickLogic Symbiflow toolchain, please:\n"
msg += "- Add QuickLogic Symbiflow toolchain to your $PATH."
raise OSError(msg)
make_cmd = ["f4pga", "-vvv", "build", "--flow", "flow.json"]
if subprocess.call(make_cmd) != 0:
raise OSError("Error occured during QuickLogic Symbiflow's script execution.")
make_cmd.append("--target")
for target in ["bitstream_openocd", "bitstream_jlink", "bitstream_bitheader", "bitstream_binary"]:
if subprocess.call(make_cmd + [target]) != 0:
raise OSError(f"Error occured during QuickLogic Symbiflow's script execution for step {target}.")

View File

@ -19,10 +19,13 @@ class QuickLogicPlatform(GenericPlatform):
def __init__(self, *args, toolchain="f4pga", **kwargs):
GenericPlatform.__init__(self, *args, **kwargs)
if toolchain == "symbiflow" or toolchain == "f4pga":
self.toolchain = f4pga.F4PGAToolchain()
if isinstance(toolchain, str):
if toolchain == "symbiflow" or toolchain == "f4pga":
self.toolchain = f4pga.F4PGAToolchain()
else:
raise ValueError(f"Unknown toolchain {toolchain}")
else:
raise ValueError(f"Unknown toolchain {toolchain}")
self.toolchain = toolchain
def get_verilog(self, *args, special_overrides=dict(), **kwargs):
so = dict(common.quicklogic_special_overrides)

View File

@ -15,6 +15,7 @@ struct session_s {
char *tdo;
char *tck;
char *tms;
char *ntrst;
char *sys_clk;
struct event *ev;
char databuf[2048];
@ -199,6 +200,7 @@ static int jtagremote_add_pads(void *sess, struct pad_list_s *plist)
litex_sim_module_pads_get(pads, "tdi", (void**)&s->tdi);
litex_sim_module_pads_get(pads, "tdo", (void**)&s->tdo);
litex_sim_module_pads_get(pads, "tms", (void**)&s->tms);
litex_sim_module_pads_get(pads, "ntrst", (void**)&s->ntrst);
}
if(!strcmp(plist->name, "sys_clk"))
@ -227,19 +229,34 @@ static int jtagremote_tick(void *sess, uint64_t time_ps)
{
c = s->databuf[s->data_start];
if((c >= '0') && (c <= '7')){
*s->tck = ((c - '0') >> 2) & 1;
*s->tms = ((c - '0') >> 1) & 1;
*s->tdi = (c - '0') & 1;
}
if(c == 'R'){
val = *s->tdo + '0';
if(-1 == write(s->fd, &val, 1)) {
eprintf("Error writing on socket\n");
ret = RC_ERROR;
goto out;
}
switch(c) {
case '0'...'7':
*s->tck = ((c - '0') >> 2) & 1;
*s->tms = ((c - '0') >> 1) & 1;
*s->tdi = (c - '0') & 1;
break;
case 'r':
case 's':
/* Deassert reset */
*s->ntrst = 1;
break;
case 't':
case 'u':
/* Assert reset */
*s->ntrst = 0;
break;
case 'R':
val = *s->tdo + '0';
if(write(s->fd, &val, 1) == -1) {
eprintf("Error writing on socket\n");
ret = RC_ERROR;
goto out;
}
break;
default:
break;
}
s->data_start = (s->data_start + 1) % 2048;
s->datalen--;
}

View File

@ -17,7 +17,7 @@ bool fb_init(unsigned width, unsigned height, bool vsync, fb_handle_t *handle)
if (!handle->renderer)
return false;
handle->texture = SDL_CreateTexture(handle->renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_TARGET, width, height);
handle->texture = SDL_CreateTexture(handle->renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, width, height);
if (!handle->texture)
return false;

View File

@ -5,6 +5,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <json-c/json.h>
#include "error.h"
#include <unistd.h>
#include <event2/listener.h>
@ -29,6 +30,7 @@ struct session_s {
unsigned frame, stride;
uint8_t *buf, *pbuf;
fb_handle_t fb;
char render_on_vsync;
};
static int litex_sim_module_pads_get(struct pad_s *pads, char *name, void **signal)
@ -56,6 +58,30 @@ out:
return ret;
}
static int videosim_parse_args(struct session_s *s, const char *args)
{
int ret = RC_OK;
json_object *args_json = NULL;
json_object *render_on_vsync_json = NULL;
args_json = json_tokener_parse(args);
if (!args_json) {
ret = RC_JSERROR;
fprintf(stderr, "[video] Could not parse args: %s\n", args);
goto out;
}
if(json_object_object_get_ex(args_json, "render_on_vsync", &render_on_vsync_json)) {
s->render_on_vsync = json_object_get_boolean(render_on_vsync_json);
} else {
s->render_on_vsync = false;
}
out:
if(args_json) json_object_put(args_json);
return ret;
}
static int videosim_start(void *b)
{
printf("[video] loaded (%p)\n", (struct event_base *)b);
@ -79,6 +105,7 @@ static int videosim_new(void **sess, char *args)
}
memset(s, 0, sizeof(struct session_s));
if (args) videosim_parse_args(s, args);
out:
*sess = (void*) s;
return ret;
@ -143,6 +170,14 @@ static int videosim_tick(void *sess, uint64_t time_ps) {
fb_init(s->hres, s->vres, false, &s->fb);
s->stride = s->hres*sizeof(uint32_t);
}
if (s->render_on_vsync) {
if(fb_should_quit())
{
fb_deinit(&s->fb);
exit(1); //FIXME: end gracefully
}
fb_update(&s->fb, s->buf, s->stride);
}
s->y = 0;
s->pbuf = s->buf;
++s->frame;
@ -164,12 +199,14 @@ static int videosim_tick(void *sess, uint64_t time_ps) {
{
if(s->buf) //update each horizontal line
{
if(fb_should_quit())
{
fb_deinit(&s->fb);
exit(1); //FIXME: end gracefully
if (!s->render_on_vsync) {
if(fb_should_quit())
{
fb_deinit(&s->fb);
exit(1); //FIXME: end gracefully
}
fb_update(&s->fb, s->buf, s->stride);
}
fb_update(&s->fb, s->buf, s->stride);
s->pbuf = s->buf + s->y*s->stride;
}

View File

@ -114,9 +114,15 @@ def get_litex_git_revision():
os.chdir(d)
return r
def generated_banner(line_comment="//"):
def generated_separator(line_comment="//", msg=""):
r = line_comment + "-"*80 + "\n"
r += line_comment + " Auto-generated by LiteX ({}) on ".format(get_litex_git_revision())
r += "{}\n".format(datetime.datetime.fromtimestamp(time.time()).strftime("%Y-%m-%d %H:%M:%S"))
r += line_comment + " " + msg + "\n"
r += line_comment + "-"*80 + "\n"
return r
def generated_banner(line_comment="//"):
msg = "Auto-generated by LiteX ({}) on {}".format(
get_litex_git_revision(),
datetime.datetime.fromtimestamp(time.time()).strftime("%Y-%m-%d %H:%M:%S"),
)
return generated_separator(line_comment, msg)

View File

@ -61,6 +61,7 @@ class VHD2VConverter(Module):
self._params = params
self._force_convert = force_convert
self._add_instance = add_instance
self._work_package = work_package
self._ghdl_opts = ["--std=08", "--no-formal"]
@ -103,7 +104,7 @@ class VHD2VConverter(Module):
if self._platform.support_mixed_language and not self._force_convert:
ip_params = self._params
for file in self._sources:
self._platform.add_source(file)
self._platform.add_source(file, library=self._work_package)
else: # platform is only able to synthesis verilog -> convert vhdl to verilog
# check if more than one core is instanciated
# if so -> append with _X
@ -141,7 +142,8 @@ class VHD2VConverter(Module):
# more than one instance of this core? rename top entity to avoid conflict
if inst_name != self._top_entity:
tools.replace_in_file(verilog_out, f"module {self._top_entity}(", f"module {inst_name}(")
tools.replace_in_file(verilog_out, f"module {self._top_entity}", f"module {inst_name}")
tools.replace_in_file(verilog_out, f"\\", f"ghdl_") # FIXME: GHDL synth workaround, improve.
self._platform.add_source(verilog_out)
if self._add_instance:

View File

@ -141,9 +141,9 @@ class XilinxSDRTristateImpl(Module):
_o = Signal()
_oe_n = Signal()
_i = Signal()
self.specials += SDROutput(o, _o)
self.specials += SDROutput(~oe, _oe_n)
self.specials += SDRInput(_i, i)
self.specials += SDROutput(o, _o, clk)
self.specials += SDROutput(~oe, _oe_n, clk)
self.specials += SDRInput(_i, i, clk)
self.specials += Instance("IOBUF",
io_IO = io,
o_O = _i,

View File

@ -58,6 +58,13 @@ class XilinxYosysNextpnrToolchain(YosysNextPNRToolchain):
"z": "zynq7"
}
@property
def device(self):
return {
"xc7a35ticsg324-1L": "xc7a35tcsg324-1",
"xc7a200t-sbg484-1": "xc7a200tsbg484-1",
}.get(self.platform.device, self.platform.device)
def _check_properties(self):
pattern = re.compile("xc7([aksz])([0-9]+)(.*)-([0-9])")
g = pattern.search(self.platform.device)
@ -119,13 +126,13 @@ class XilinxYosysNextpnrToolchain(YosysNextPNRToolchain):
if nextpnr_xilinx_python_dir is None or nextpnr_xilinx_python_dir == "":
nextpnr_xilinx_python_dir = "/snap/openxc7/current/opt/nextpnr-xilinx/python"
bba = self.dbpart + ".bba"
bbaexport = [pypy3, os.path.join(nextpnr_xilinx_python_dir, "bbaexport.py"), "--device", self.platform.device, "--bba", bba]
bbaexport = [pypy3, os.path.join(nextpnr_xilinx_python_dir, "bbaexport.py"), "--device", self.device, "--bba", bba]
print(str(bbaexport))
subprocess.run(bbaexport)
subprocess.run(["bbasm", "-l", bba, chipdb])
os.remove(bba)
else:
print("Chip database file '{chipdb}' not found. Please check your toolchain installation!")
print(f"Chip database file '{chipdb}' not found. Please check your toolchain installation!")
exit(1)
# pnr options
@ -149,14 +156,14 @@ class XilinxYosysNextpnrToolchain(YosysNextPNRToolchain):
# pre packer options
self._pre_packer_opts[self._pre_packer_cmd[0]] = "--part {part} --db-root {db_root} {top}.fasm > {top}.frames".format(
part = self.platform.device,
part = self.device,
db_root = os.path.join(prjxray_db_dir, self._xc7family),
top = self._build_name
)
# packer options
self._packer_opts += "--part_file {db_dir}/{part}/part.yaml --part_name {part} --frm_file {top}.frames --output_file {top}.bit".format(
db_dir = os.path.join(prjxray_db_dir, self._xc7family),
part = self.platform.device,
part = self.device,
top = self._build_name
)

View File

@ -20,6 +20,18 @@ def colorer(s, color="bright", enable=True):
trailer = "\x1b[0m"
return (header + str(s) + trailer) if enable else str(s)
# Byte Size Definitions ----------------------------------------------------------------------------
# Short.
KB = 1024
MB = KB * 1024
GB = MB * 1024
# Long.
KILOBYTE = 1024
MEGABYTE = KILOBYTE * 1024
GIGABYTE = MEGABYTE * 1024
# Bit/Bytes Reversing ------------------------------------------------------------------------------
def reverse_bits(s):

View File

@ -41,7 +41,7 @@ class LiteXHierarchyExplorer:
r += self.get_tree(mod, ident + 1)
# Instances.
for s in module._fragment.specials:
for s in sorted(module._fragment.specials, key=lambda x: str(x)):
if (self.depth is None) or (ident <= self.depth):
if isinstance(s, Instance):
show = with_instances

View File

View File

@ -0,0 +1,175 @@
#
# CTU CAN-FD Core Wrapper for LiteX.
#
# Copyright (c) 2021 Andrew Dennison <andrew@motec.com.au>
# Copyright (c) 2021-2024 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2024 Gwenhael Goavec-Merou <gwenhael@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
# Documentation at https://canbus.pages.fel.cvut.cz/
import os
import subprocess
from migen import *
from litex.gen import *
from litex.build import tools
from litex.build.vhd2v_converter import VHD2VConverter
from litex.soc.interconnect import wishbone
from litex.soc.interconnect.csr_eventmanager import *
# CTU CAN-FD ---------------------------------------------------------------------------------------
class CTUCANFD(LiteXModule, EventManager):
def __init__(self, platform, pads, timestamp=0, force_convert=False):
# Parameters.
self.platform = platform
self.pads = pads
self._force_convert = force_convert
# Wishbone Bus.
self.bus = wishbone.Interface(data_width=32)
# CSRs.
self.control = CSRStorage(32, fields=[
CSRField("reset", size=1, values=[
("``0b0``", "Normal Operation."),
("``0b1`", "Hold the Core in Reset."),
], reset=0b0),
CSRField("reserved",size=31),
])
self.status = CSRStatus(32, fields=[
CSRField("ready", size=1, values=[
("``0b0``", "Core in Reset."),
("``0b1`", "Core Ready."),
]),
])
# IRQs.
self.irq = Signal()
# CTU CAN-FD Instance ----------------------------------------------------------------------
self.core_params = dict()
# Wishbone to CTU CAN-FD Memory Bus adaptation.
self.mem_scs = mem_scs = Signal()
self.mem_srd = mem_srd = Signal()
self.mem_swr = mem_swr = Signal()
self.mem_sbe = mem_sbe = Signal(4)
self.mem_adress = mem_adress = Signal(16)
self.mem_data_in = mem_data_in = Signal(32)
self.mem_data_out = mem_data_out = Signal(32)
self.comb += [
# On Wishbone Access cycle...
mem_scs.eq(self.bus.cyc & self.bus.stb),
mem_srd.eq(mem_scs & ~self.bus.we & ~self.bus.ack),
mem_swr.eq(mem_scs & self.bus.we),
mem_sbe.eq(self.bus.sel),
# Connect data_in/out.
mem_data_in.eq(self.bus.dat_w),
self.bus.dat_r.eq(mem_data_out),
# Convert 32-bit word addressing to bytes addressing.
mem_adress.eq(Cat(Signal(2), self.bus.adr)),
]
self.sync += [
self.bus.ack.eq(0),
If(mem_scs & ~self.bus.ack, self.bus.ack.eq(1)),
]
# CTU CAN-FD Parameters.
self.core_params.update(
# Target technology (ASIC or FPGA)
#p_target_technology = C_TECH_FPGA
# TX/RX Buffers.
p_txt_buffer_count = 4, # Number of TX Buffers.
p_rx_buffer_size = 32, # RX Buffer size (in 32-bit words).
# Filter A-C.
p_sup_filtA = False,
p_sup_filtB = False,
p_sup_filtC = False,
# Range Filter.
#p_sup_filtV = False,
# Synthesize Range Filter
p_sup_range = False,
# Test registers.
p_sup_test_registers = True, # True to have access to 0x9XX Tests registers
# Traffic counters.
p_sup_traffic_ctrs = False,
# Add parity bit to TXT Buffer and RX Buffer RAMs
p_sup_parity = False,
# Number of active timestamp bits
p_active_timestamp_bits = 63,
# Reset TXT / RX Buffer RAMs
p_reset_buffer_rams = False,
)
# CTU CAN-FD Signals.
self.core_params.update(
# Clk / Rst.
i_clk_sys = ClockSignal("sys"),
i_res_n = ~(ResetSignal("sys") | self.control.fields.reset),
o_res_n_out = self.status.fields.ready,
# DFT support (ASIC only).
i_scan_enable = 0,
# Timestamp (For time based transmission / reception).
i_timestamp = timestamp,
# Memory interface.
i_scs = mem_scs,
i_srd = mem_srd,
i_swr = mem_swr,
i_sbe = mem_sbe,
i_adress = mem_adress,
i_data_in = mem_data_in,
o_data_out = mem_data_out,
# Interrupt.
o_int = self.irq,
# CAN Bus.
o_can_tx = pads.tx,
i_can_rx = pads.rx,
# Debug.
#o_test_probe = ,
)
def add_sources(self, platform):
sources = []
sdir = "CTU-CAN-FD"
cdir = os.path.dirname(__file__)
if not os.path.exists(sdir):
os.system(f"git clone https://github.com/enjoy-digital/CTU-CAN-FD")
with open(os.path.join(cdir, 'rtl_lst.txt')) as f:
for line in f:
srcfile = os.path.join(sdir, line.strip().replace('rtl', 'src'))
self.vhd2v_converter.add_source(srcfile)
def do_finalize(self):
# CAN Core instance
self.vhd2v_converter = VHD2VConverter(self.platform,
top_entity = "can_top_level",
build_dir = os.path.abspath(os.path.dirname(__file__)),
work_package = "ctu_can_fd_rtl",
force_convert = self._force_convert,
params = self.core_params,
add_instance = True,
)
# Add Sources.
self.add_sources(self.platform)

View File

@ -0,0 +1,88 @@
rtl/packages/unary_ops_pkg.vhd
rtl/packages/can_fd_register_map.vhd
rtl/packages/can_fd_frame_format.vhd
rtl/packages/id_transfer_pkg.vhd
rtl/packages/can_constants_pkg.vhd
rtl/packages/can_config_pkg.vhd
rtl/packages/can_types_pkg.vhd
rtl/txt_buffer/txt_buffer_fsm.vhd
rtl/tx_arbitrator/tx_arbitrator_fsm.vhd
rtl/tx_arbitrator/priority_decoder.vhd
rtl/tx_arbitrator/tx_arbitrator.vhd
rtl/rx_buffer/rx_buffer_pointers.vhd
rtl/rx_buffer/rx_buffer_fsm.vhd
rtl/prescaler/trigger_generator.vhd
rtl/prescaler/synchronisation_checker.vhd
rtl/prescaler/segment_end_detector.vhd
rtl/prescaler/bit_time_fsm.vhd
rtl/prescaler/bit_time_counters.vhd
rtl/prescaler/bit_time_cfg_capture.vhd
rtl/prescaler/bit_segment_meter.vhd
rtl/prescaler/prescaler.vhd
rtl/memory_registers/generated/memory_reg_rw_lock.vhd
rtl/memory_registers/generated/memory_reg_rw.vhd
rtl/memory_registers/generated/memory_reg_os_lock.vhd
rtl/memory_registers/generated/memory_reg_os.vhd
rtl/memory_registers/generated/memory_bus.vhd
rtl/memory_registers/generated/data_mux.vhd
rtl/memory_registers/generated/cmn_reg_map_pkg.vhd
rtl/memory_registers/generated/can_registers_pkg.vhd
rtl/memory_registers/generated/test_registers_reg_map.vhd
rtl/memory_registers/generated/control_registers_reg_map.vhd
rtl/memory_registers/generated/address_decoder.vhd
rtl/memory_registers/generated/access_signaler.vhd
rtl/interrupt_manager/int_module.vhd
rtl/interface/apb_ifc.vhd
rtl/interface/ahb_ifc.vhd
rtl/frame_filters/range_filter.vhd
rtl/frame_filters/bit_filter.vhd
rtl/frame_filters/frame_filters.vhd
rtl/common_blocks/sig_sync.vhd
rtl/common_blocks/shift_reg_preload.vhd
rtl/common_blocks/shift_reg_byte.vhd
rtl/common_blocks/shift_reg.vhd
rtl/common_blocks/parity_calculator.vhd
rtl/common_blocks/rst_sync.vhd
rtl/common_blocks/rst_reg.vhd
rtl/common_blocks/mux2.vhd
rtl/common_blocks/majority_decoder_3.vhd
rtl/common_blocks/inf_ram_wrapper.vhd
rtl/txt_buffer/txt_buffer_ram.vhd
rtl/rx_buffer/rx_buffer_ram.vhd
rtl/common_blocks/endian_swapper.vhd
rtl/common_blocks/dlc_decoder.vhd
rtl/common_blocks/dff_arst_ce.vhd
rtl/common_blocks/dff_arst.vhd
rtl/interrupt_manager/int_manager.vhd
rtl/common_blocks/clk_gate.vhd
rtl/txt_buffer/txt_buffer.vhd
rtl/rx_buffer/rx_buffer.vhd
rtl/memory_registers/memory_registers.vhd
rtl/can_core/tx_shift_reg.vhd
rtl/can_core/trigger_mux.vhd
rtl/can_core/rx_shift_reg.vhd
rtl/can_core/retransmitt_counter.vhd
rtl/can_core/reintegration_counter.vhd
rtl/can_core/protocol_control_fsm.vhd
rtl/can_core/operation_control.vhd
rtl/can_core/fault_confinement_rules.vhd
rtl/can_core/fault_confinement_fsm.vhd
rtl/can_core/err_detector.vhd
rtl/can_core/err_counters.vhd
rtl/can_core/fault_confinement.vhd
rtl/can_core/crc_calc.vhd
rtl/can_core/control_counter.vhd
rtl/can_core/protocol_control.vhd
rtl/can_core/can_crc.vhd
rtl/can_core/bus_traffic_counters.vhd
rtl/can_core/bit_stuffing.vhd
rtl/can_core/bit_destuffing.vhd
rtl/can_core/can_core.vhd
rtl/bus_sampling/tx_data_cache.vhd
rtl/bus_sampling/trv_delay_meas.vhd
rtl/bus_sampling/ssp_generator.vhd
rtl/bus_sampling/sample_mux.vhd
rtl/bus_sampling/data_edge_detector.vhd
rtl/bus_sampling/bit_err_detector.vhd
rtl/bus_sampling/bus_sampling.vhd
rtl/can_top_level.vhd

View File

@ -158,10 +158,10 @@ class EFINIXPLL(LiteXModule):
# Pre-Divider (N).
# -----------------
# F_PFD is between 10e6 and 100e6
# so limit search to only acceptable factors
# so limit search to only acceptable factors
N_min = int(math.ceil(clk_in_freq / pfd_range[1]))
N_max = int(math.floor(clk_in_freq / pfd_range[0]))
## limit
## limit
### when fin is below FPLL_MAX min is < 1
if N_min < 1:
N_min = 1
@ -200,7 +200,7 @@ class EFINIXPLL(LiteXModule):
params_list = []
for n in range(N_min, N_max + 1):
fpfd_tmp = clk_in_freq / n
# limit range using FVCO_MAX & FVCO_MIN
# limit range using FVCO_MAX & FVCO_MIN
# fVCO = fPFD * M * O * Cfbk
# so:
# fVCO

View File

@ -32,7 +32,7 @@ class NXOSCA(LiteXModule):
clk_hf_freq_range = (1.76, 450e6)
clk_hf_freq = 450e6
def __init__(self):
def __init__(self, platform=None):
self.logger = logging.getLogger("NXOSCA")
self.logger.info("Creating NXOSCA.")
@ -40,6 +40,7 @@ class NXOSCA(LiteXModule):
self.hfsdc_clk_out = {}
self.lf_clk_out = None
self.params = {}
self.platform = platform
def create_hf_clk(self, cd, freq, margin=.05):
"""450 - 1.7 Mhz Clk"""
@ -86,21 +87,29 @@ class NXOSCA(LiteXModule):
def do_finalize(self):
if self.hf_clk_out:
divisor = self.compute_divisor(self.hf_clk_out[1], self.hf_clk_out[2])
clk_freq = self.hf_clk_out[1]
divisor = self.compute_divisor(clk_freq, self.hf_clk_out[2])
self.params["i_HFOUTEN"] = 0b1
self.params["p_HF_CLK_DIV"] = divisor
self.params["o_HFCLKOUT"] = self.hf_clk_out[0]
self.params["p_HF_OSC_EN"] = "ENABLED"
if self.platform:
self.platform.add_platform_command("create_clock -period {} -name OSCA_HFCLKOUT [get_pins OSCA.OSCA_inst/HFCLKOUT]".format(str(1e9/clk_freq)))
if self.hfsdc_clk_out:
divisor = self.compute_divisor(self.hfsdc_clk_out[1], self.hfsdc_clk_out[2])
clk_freq = self.hf_clk_out[1]
divisor = self.compute_divisor(clk_freq, self.hfsdc_clk_out[2])
self.params["i_HFSDSCEN"] = 0b1
self.params["p_HF_SED_SEC_DIV"] = divisor
self.params["o_HFSDCOUT"] = self.hfsdc_clk_out[0]
if self.platform:
self.platform.add_platform_command("create_clock -period {} -name OSCA_HFSDCOUT [get_pins OSCA.OSCA_inst/HFSDCOUT]".format(str(1e9/clk_freq)))
if self.lf_clk_out is not None:
self.params["o_LFCLKOUT"] = self.lf_clk_out[0]
self.params["p_LF_OUTPUT_EN"] = "ENABLED"
if self.platform:
self.platform.add_platform_command("create_clock -period {} -name OSCA_LF_OUTPUT_EN [get_pins OSCA.OSCA_inst/LF_OUTPUT_EN]".format(str(1e9/128e3)))
self.specials += Instance("OSCA", **self.params)

View File

@ -79,9 +79,13 @@ CPU_GCC_TRIPLE_RISCV64 = (
)
CPU_GCC_TRIPLE_RISCV32 = CPU_GCC_TRIPLE_RISCV64 + (
"riscv32-pc-linux-musl",
"riscv32-unknown-elf",
"riscv32-unknown-linux-gnu",
"riscv32-elf",
"riscv32-linux",
"riscv32-linux-gnu",
"riscv32-none-elf",
"riscv-none-embed",
"riscv-none-elf",
)

View File

@ -19,7 +19,7 @@ from litex.soc.cores.cpu import CPU, CPU_GCC_TRIPLE_RISCV32
# Variants -----------------------------------------------------------------------------------------
CPU_VARIANTS = ["standard", "full"]
CPU_VARIANTS = ["standard", "standard+fpu"]
# GCC Flags ----------------------------------------------------------------------------------------
@ -32,7 +32,7 @@ GCC_FLAGS = {
# | ||||/-- Double-Precision Floating-Point
# i macfd
"standard": "-march=rv32i2p0_mc -mabi=ilp32 ",
"full": "-march=rv32i2p0_mfc -mabi=ilp32 ",
"standard+fpu": "-march=rv32i2p0_mfc -mabi=ilp32 ",
}
# OBI / APB / Trace Layouts ------------------------------------------------------------------------
@ -59,18 +59,6 @@ apb_layout = [
("pslverr", 1),
]
trace_layout = [
("ivalid", 1),
("iexception", 1),
("interrupt", 1),
("cause", 5),
("tval", 32),
("priv", 3),
("iaddr", 32),
("instr", 32),
("compressed", 1),
]
# Helpers ------------------------------------------------------------------------------------------
def add_manifest_sources(platform, manifest):
@ -182,102 +170,6 @@ class Wishbone2APB(LiteXModule):
wb.dat_r.eq(apb.prdata),
]
# Trace Collector ----------------------------------------------------------------------------------
class TraceCollector(LiteXModule):
def __init__(self, trace_depth=16384):
self.bus = bus = wishbone.Interface(data_width=32, address_width=32, addressing="word")
self.sink = sink = stream.Endpoint([("data", 32)])
clear = Signal()
enable = Signal()
pointer = Signal(32)
self._enable = CSRStorage()
self._clear = CSRStorage()
self._pointer = CSRStatus(32)
mem = Memory(32, trace_depth)
rd_port = mem.get_port()
wr_port = mem.get_port(write_capable=True)
self.specials += rd_port, wr_port, mem
self.sync += [
# wishbone
bus.ack.eq(0),
If(bus.cyc & bus.stb & ~bus.ack, bus.ack.eq(1)),
# trace core
If(clear, pointer.eq(0)).Else(
If(sink.ready & sink.valid, pointer.eq(pointer+1)),
),
]
self.comb += [
# wishbone
rd_port.adr.eq(bus.adr),
bus.dat_r.eq(rd_port.dat_r),
# trace core
wr_port.adr.eq(pointer),
wr_port.dat_w.eq(sink.data),
wr_port.we.eq(sink.ready & sink.valid),
sink.ready.eq(enable & (pointer < trace_depth)),
# csrs
enable.eq(self._enable.storage),
clear.eq(self._clear.storage),
self._pointer.status.eq(pointer),
]
# Trace Debugger -----------------------------------------------------------------------------------
class TraceDebugger(LiteXModule):
def __init__(self):
self.bus = wishbone.Interface(data_width=32, address_width=32, addressing="word")
self.source = source = stream.Endpoint([("data", 32)])
self.trace_if = trace_if = Record(trace_layout)
apb = Record(apb_layout)
self.bus_conv = Wishbone2APB(self.bus, apb)
self.trace_params = dict(
# Clk / Rst.
i_clk_i = ClockSignal("sys"),
i_rst_ni = ~ResetSignal("sys"),
i_test_mode_i = 0,
# CPU Interface.
i_ivalid_i = trace_if.ivalid,
i_iexception_i = trace_if.iexception,
i_interrupt_i = trace_if.interrupt,
i_cause_i = trace_if.cause,
i_tval_i = trace_if.tval,
i_priv_i = trace_if.priv,
i_iaddr_i = trace_if.iaddr,
i_instr_i = trace_if.instr,
i_compressed_i = trace_if.compressed,
# APB Interface.
i_paddr_i = apb.paddr,
i_pwdata_i = apb.pwdata,
i_pwrite_i = apb.pwrite,
i_psel_i = apb.psel,
i_penable_i = apb.penable,
o_prdata_o = apb.prdata,
o_pready_o = apb.pready,
o_pslverr_o = apb.pslverr,
# Data Output.
o_packet_word_o = source.data,
o_packet_word_valid_o = source.valid,
i_grant_i = source.ready,
)
self.specials += Instance("trace_debugger", **self.trace_params)
@staticmethod
def add_sources(platform):
add_manifest_sources(platform, "cv32e40p_trace_manifest.flist")
# Debug Module -------------------------------------------------------------------------------------
class DebugModule(LiteXModule):
@ -350,10 +242,6 @@ class DebugModule(LiteXModule):
self.specials += Instance("dm_wrap", **self.dm_params)
@staticmethod
def add_sources(platform):
add_manifest_sources(platform, "cv32e40p_dm_manifest.flist")
# CV32E40P -----------------------------------------------------------------------------------------
class CV32E40P(CPU):
@ -368,8 +256,7 @@ class CV32E40P(CPU):
linker_output_format = "elf32-littleriscv"
nop = "nop"
io_regions = {0x80000000: 0x80000000} # Origin, Length.
has_fpu = ["full"]
has_fpu = ["standard+fpu"]
# GCC Flags.
@property
@ -379,14 +266,15 @@ class CV32E40P(CPU):
return flags
def __init__(self, platform, variant="standard"):
self.platform = platform
self.variant = variant
self.reset = Signal()
self.ibus = wishbone.Interface(data_width=32, address_width=32, addressing="word")
self.dbus = wishbone.Interface(data_width=32, address_width=32, addressing="word")
self.periph_buses = [self.ibus, self.dbus]
self.memory_buses = []
self.interrupt = Signal(15)
self.platform = platform
self.variant = variant
self.reset = Signal()
self.ibus = wishbone.Interface(data_width=32, address_width=32, addressing="word")
self.dbus = wishbone.Interface(data_width=32, address_width=32, addressing="word")
self.periph_buses = [self.ibus, self.dbus]
self.memory_buses = []
self.interrupt = Signal(16)
self.interrupt_padding = Signal(16)
ibus = Record(obi_layout)
dbus = Record(obi_layout)
@ -406,11 +294,12 @@ class CV32E40P(CPU):
i_rst_ni = ~ResetSignal("sys"),
# Controls.
i_clock_en_i = 1,
i_test_en_i = 0,
i_fregfile_disable_i = 0,
i_core_id_i = 0,
i_cluster_id_i = 0,
i_pulp_clock_en_i = 1,
i_scan_cg_en_i = 0,
i_mtvec_addr_i = 0,
i_dm_halt_addr_i = 0,
i_hart_id_i = 0,
i_dm_exception_addr_i = 0,
# IBus.
o_instr_req_o = ibus.req,
@ -429,56 +318,32 @@ class CV32E40P(CPU):
o_data_wdata_o = dbus.wdata,
i_data_rdata_i = dbus.rdata,
# APU.
i_apu_master_gnt_i = 0,
i_apu_master_valid_i = 0,
# IRQ.
i_irq_sec_i = 0,
i_irq_software_i = 0,
i_irq_external_i = 0,
i_irq_fast_i = self.interrupt,
i_irq_nmi_i = 0,
i_irq_fastx_i = 0,
i_irq_i = Cat(self.interrupt_padding, self.interrupt),
# Debug.
i_debug_req_i = 0,
i_debug_req_i = 0,
# CPU Control.
i_fetch_enable_i = 1,
i_fetch_enable_i = 1,
)
# Add Verilog sources.
add_manifest_sources(platform, 'cv32e40p_manifest.flist')
# Specific FPU variant parameters/files.
if variant in self.has_fpu:
# Specific FPU variant parameters/files.
self.cpu_params.update(p_FPU=1)
add_manifest_sources(platform, 'cv32e40p_fpu_manifest.flist')
else:
add_manifest_sources(platform, 'cv32e40p_manifest.flist')
def add_debug_module(self, dm):
self.cpu_params.update(i_debug_req_i=dm.debug_req)
self.cpu_params.update(i_rst_ni=~(ResetSignal("sys") | dm.ndmreset))
def add_trace_core(self, trace):
trace_if = trace.trace_if
self.cpu_params.update(
o_ivalid_o = trace_if.ivalid,
o_iexception_o = trace_if.iexception,
o_interrupt_o = trace_if.interrupt,
o_cause_o = trace_if.cause,
o_tval_o = trace_if.tval,
o_priv_o = trace_if.priv,
o_iaddr_o = trace_if.iaddr,
o_instr_o = trace_if.instr,
o_compressed_o = trace_if.compressed,
)
def set_reset_address(self, reset_address):
self.reset_address = reset_address
self.cpu_params.update(i_boot_addr_i=Signal(32, reset=reset_address))
def do_finalize(self):
assert hasattr(self, "reset_address")
self.specials += Instance("riscv_core", **self.cpu_params)
self.specials += Instance("cv32e40p_core", **self.cpu_params)

View File

@ -46,7 +46,7 @@ vector_table:
j trap_entry # 28 firq12
j trap_entry # 29 firq13
j trap_entry # 30 firq14
j trap_entry # 31 unused
j trap_entry # 31 firq15
.global trap_entry
trap_entry:
@ -116,10 +116,8 @@ bss_loop:
add a0,a0,4
j bss_loop
bss_done:
li a0, 0x7FFF0880 //7FFF0880 enable timer + external interrupt + fast interrupt sources (until mstatus.MIE is set, they will never trigger an interrupt)
li a0, 0xFFFF0880 //FFFF0880 enable timer + external interrupt + fast interrupt sources (until mstatus.MIE is set, they will never trigger an interrupt)
csrw mie,a0
j main
infinit_loop:
j infinit_loop

View File

@ -1,11 +1,14 @@
#ifndef CSR_DEFS__H
#define CSR_DEFS__H
/*Reference : https://docs.openhwgroup.org/projects/cv32e40p-user-manual/en/latest/control_status_registers.html */
#define CSR_MSTATUS_MIE 0x8
#define CSR_IRQ_MASK 0xBC0
#define CSR_IRQ_PENDING 0xFC0
#define CSR_IRQ_MASK 0x304
#define CSR_IRQ_PENDING 0x344
#define FIRQ_OFFSET 16
#define CSR_DCACHE_INFO 0xCC0
#endif /* CSR_DEFS__H */

View File

@ -20,17 +20,21 @@ static inline void irq_setie(unsigned int ie)
static inline unsigned int irq_getmask(void)
{
return 0; // FIXME
unsigned int mask;
asm volatile ("csrr %0, %1" : "=r"(mask) : "i"(CSR_IRQ_MASK));
return (mask >> FIRQ_OFFSET);
}
static inline void irq_setmask(unsigned int mask)
{
// FIXME
asm volatile ("csrw %0, %1" :: "i"(CSR_IRQ_MASK), "r"(mask << FIRQ_OFFSET));
}
static inline unsigned int irq_pending(void)
{
return 0;// FIXME
unsigned int pending;
asm volatile ("csrr %0, %1" : "=r"(pending) : "i"(CSR_IRQ_PENDING));
return (pending >> FIRQ_OFFSET);
}
#ifdef __cplusplus

View File

@ -7,17 +7,8 @@
extern "C" {
#endif
__attribute__((unused)) static void flush_cpu_icache(void)
{
// FIXME
asm volatile("nop");
}
__attribute__((unused)) static void flush_cpu_dcache(void)
{
// FIXME
asm volatile("nop");
}
__attribute__((unused)) static void flush_cpu_icache(void){}; /* No instruction cache */
__attribute__((unused)) static void flush_cpu_dcache(void){}; /* No instruction cache */
void flush_l2_cache(void);

View File

@ -116,10 +116,8 @@ bss_loop:
add a0,a0,4
j bss_loop
bss_done:
li a0, 0x7FFF0880 //7FFF0880 enable timer + external interrupt + fast interrupt sources (until mstatus.MIE is set, they will never trigger an interrupt)
li a0, 0xFFFF0880 //FFFF0880 enable timer + external interrupt + fast interrupt sources (until mstatus.MIE is set, they will never trigger an interrupt)
csrw mie,a0
j main
infinit_loop:
j infinit_loop

View File

@ -4,8 +4,8 @@
#define CSR_MSTATUS_MIE 0x8
#define CSR_IRQ_MASK 0x344
#define CSR_IRQ_PENDING 0x304
#define CSR_IRQ_MASK 0x304
#define CSR_IRQ_PENDING 0x344
#define FIRQ_OFFSET 16
#define CSR_DCACHE_INFO 0xCC0
@ -13,7 +13,7 @@
/*
For CV32E41P from https://docs.openhwgroup.org/projects/openhw-group-cv32e41p/control_status_registers.html
Machine Interrupt Pending Register (mip): CSR_IRQ_MASK: 0x344
Machine Interrupt Enable Register (mie): CSR_IRQ_PENDING: 0x304
For CV32E41P from https://docs.openhwgroup.org/projects/cv32e41p-user-manual/control_status_registers.html
Machine Interrupt Pending Register (mip): CSR_IRQ_PENDING: 0x344
Machine Interrupt Enable Register (mie): CSR_IRQ_MASK: 0x304
*/

View File

@ -90,6 +90,7 @@ class CVA6(CPU):
def gcc_flags(self):
flags = GCC_FLAGS[self.variant]
flags += "-D__cva6__ "
flags += "-D__riscv_plic__ "
#flags += f" -DUART_POLLING"
return flags

View File

@ -9,15 +9,16 @@ extern "C" {
#include <generated/csr.h>
#include <generated/soc.h>
#define PLIC_SOURCE_0 0x0c000004L // source 0 priority
#define PLIC_SOURCE_1 0x0c000008L // source 1 priority
#define PLIC_PENDING 0x0c001000L // start of pending array
#define PLIC_M_ENABLE 0x0c002000L // Start of Hart 0 M-mode enables
#define PLIC_S_ENABLE 0x0c002100L // Start of Hart 0 S-mode enables
#define PLIC_M_THRESHOLD 0x0c200000L // hart 0 M-mode priority threshold
#define PLIC_M_CLAIM 0x0c200004L // hart 0 M-mode priority claim/complete
#define PLIC_S_THRESHOLD 0x0c200100L // hart 0 S-mode priority threshold
#define PLIC_S_CLAIM 0x0c200104L // hart 0 S-mode priority claim/complete
// The CVA6 uses a Platform-Level Interrupt Controller (PLIC) which
// is programmed and queried via a set of MMIO registers.
#define PLIC_BASE 0x0c000000L // Base address and per-pin priority array
#define PLIC_PENDING 0x0c001000L // Bit field matching currently pending pins
#define PLIC_ENABLED 0x0c002000L // Bit field corresponding to the current mask
#define PLIC_THRSHLD 0x0c200000L // Per-pin priority must be >= this to trigger
#define PLIC_CLAIM 0x0c200004L // Claim & completion register address
#define PLIC_EXT_IRQ_BASE 1
static inline unsigned int irq_getie(void)
{
@ -31,12 +32,12 @@ static inline void irq_setie(unsigned int ie)
static inline unsigned int irq_getmask(void)
{
return *((unsigned int *)PLIC_M_ENABLE) >> 1;
return *((unsigned int *)PLIC_ENABLED) >> 1;
}
static inline void irq_setmask(unsigned int mask)
{
*((unsigned int *)PLIC_M_ENABLE) = mask << 1;
*((unsigned int *)PLIC_ENABLED) = mask << 1;
}
static inline unsigned int irq_pending(void)

View File

@ -110,10 +110,10 @@ class EOS_S3(CPU):
# Clocking.
# ---------
o_Sys_Clk0 = eos_s3_0_clk,
o_Sys_Clk0_Rst = eos_s3_0_rst,
o_Sys_Clk1 = eos_s3_1_clk,
o_Sys_Clk1_Rst = eos_s3_1_rst,
o_Clk_C16 = eos_s3_0_clk,
o_Clk_C16_Rst = eos_s3_0_rst,
o_Clk_C21 = eos_s3_1_clk,
o_Clk_C21_Rst = eos_s3_1_rst,
# Packet FIFO.
# ------------

View File

@ -93,7 +93,7 @@ class NaxRiscv(CPU):
def gcc_flags(self):
flags = f" -march={NaxRiscv.get_arch()} -mabi={NaxRiscv.get_abi()}"
flags += f" -D__NaxRiscv__"
flags += f" -DUART_POLLING"
flags += f" -D__riscv_plic__"
return flags
# Reserved Interrupts.
@ -294,7 +294,11 @@ class NaxRiscv(CPU):
@staticmethod
def git_setup(name, dir, repo, branch, hash):
def git_setup(name, dir, repo, branch, hash, update):
if update == "no":
return
if "recommended" not in update:
hash = ""
if not os.path.exists(dir):
# Clone Repo.
print(f"Cloning {name} Git repository...")
@ -306,7 +310,7 @@ class NaxRiscv(CPU):
print(f"Updating {name} Git repository...")
cwd = os.getcwd()
os.chdir(os.path.join(dir))
wipe_cmd = "&& git clean --force -d -x && git reset --hard" if "wipe" in NaxRiscv.update_repo else ""
wipe_cmd = "&& git clean --force -d -x && git reset --hard" if "wipe" in update else ""
checkout_cmd = f"&& git checkout {hash}" if hash is not None else ""
subprocess.check_call(f"cd {dir} {wipe_cmd} && git checkout {branch} && git submodule init && git pull --recurse-submodules {checkout_cmd}", shell=True)
os.chdir(cwd)
@ -316,10 +320,8 @@ class NaxRiscv(CPU):
def generate_netlist(reset_address):
vdir = get_data_mod("cpu", "naxriscv").data_location
ndir = os.path.join(vdir, "ext", "NaxRiscv")
sdir = os.path.join(vdir, "ext", "SpinalHDL")
if NaxRiscv.update_repo != "no":
NaxRiscv.git_setup("NaxRiscv", ndir, "https://github.com/SpinalHDL/NaxRiscv.git", "main", "ec3ee4dc" if NaxRiscv.update_repo=="recommended" else None)
NaxRiscv.git_setup("NaxRiscv", ndir, "https://github.com/SpinalHDL/NaxRiscv.git", "main", "ba63ee6d", NaxRiscv.update_repo)
gen_args = []
gen_args.append(f"--netlist-name={NaxRiscv.netlist_name}")
@ -365,6 +367,7 @@ class NaxRiscv(CPU):
# Add RAM.
# By default, use Generic RAM implementation.
ram_filename = "Ram_1w_1rs_Generic.v"
lutram_filename = "Ram_1w_1ra_Generic.v"
# On Altera/Intel platforms, use specific implementation.
from litex.build.altera import AlteraPlatform
if isinstance(platform, AlteraPlatform):
@ -374,6 +377,8 @@ class NaxRiscv(CPU):
if isinstance(platform, EfinixPlatform):
ram_filename = "Ram_1w_1rs_Efinix.v"
platform.add_source(os.path.join(vdir, ram_filename), "verilog")
platform.add_source(os.path.join(vdir, lutram_filename), "verilog")
# Add Cluster.
platform.add_source(os.path.join(vdir, self.netlist_name + ".v"), "verilog")
@ -399,13 +404,13 @@ class NaxRiscv(CPU):
if NaxRiscv.jtag_tap:
self.jtag_tms = Signal()
self.jtag_tck = Signal()
self.jtag_clk = Signal()
self.jtag_tdi = Signal()
self.jtag_tdo = Signal()
self.cpu_params.update(
i_jtag_tms = self.jtag_tms,
i_jtag_tck = self.jtag_tck,
i_jtag_tck = self.jtag_clk,
i_jtag_tdi = self.jtag_tdi,
o_jtag_tdo = self.jtag_tdo,
)
@ -452,10 +457,21 @@ class NaxRiscv(CPU):
# Reset SoC's CRG when debug_ndmreset rising edge.
self.sync.debug_por += debug_ndmreset_last.eq(debug_ndmreset)
self.comb += debug_ndmreset_rise.eq(debug_ndmreset & ~debug_ndmreset_last)
self.comb += If(debug_ndmreset_rise, soc.crg.rst.eq(1))
if soc.get_build_name() == "sim":
self.comb += If(debug_ndmreset_rise, soc.crg.cd_sys.rst.eq(1))
else:
self.comb += If(debug_ndmreset_rise, soc.crg.rst.eq(1))
self.soc_bus = soc.bus # FIXME: Save SoC Bus instance to retrieve the final mem layout on finalization.
def add_jtag(self, pads):
self.comb += [
self.jtag_tms.eq(pads.tms),
self.jtag_clk.eq(pads.tck),
self.jtag_tdi.eq(pads.tdi),
pads.tdo.eq(self.jtag_tdo),
]
def add_memory_buses(self, address_width, data_width):
NaxRiscv.litedram_width = data_width
nax_data_width = 64
@ -470,6 +486,16 @@ class NaxRiscv(CPU):
)
self.memory_buses.append(mbus)
self.comb += mbus.aw.cache.eq(0xF)
self.comb += mbus.aw.lock.eq(0)
self.comb += mbus.aw.prot.eq(1)
self.comb += mbus.aw.qos.eq(0)
self.comb += mbus.ar.cache.eq(0xF)
self.comb += mbus.ar.lock.eq(0)
self.comb += mbus.ar.prot.eq(1)
self.comb += mbus.ar.qos.eq(0)
self.cpu_params.update(
# Memory Bus (Master).
# --------------------

View File

@ -12,6 +12,16 @@
#define MSTATUS_FS_DIRTY (3 << 13)
#define MSTATUS_FS_MASK (3 << 13)
#if __riscv_xlen == 64
#define STORE sd
#define LOAD ld
#define WORD 8
#else
#define STORE sw
#define LOAD lw
#define WORD 4
#endif
_start:
j crt_init
nop
@ -24,41 +34,41 @@ _start:
.global trap_entry
trap_entry:
sw x1, - 1*4(sp)
sw x5, - 2*4(sp)
sw x6, - 3*4(sp)
sw x7, - 4*4(sp)
sw x10, - 5*4(sp)
sw x11, - 6*4(sp)
sw x12, - 7*4(sp)
sw x13, - 8*4(sp)
sw x14, - 9*4(sp)
sw x15, -10*4(sp)
sw x16, -11*4(sp)
sw x17, -12*4(sp)
sw x28, -13*4(sp)
sw x29, -14*4(sp)
sw x30, -15*4(sp)
sw x31, -16*4(sp)
addi sp,sp,-16*4
STORE x1, - 1*WORD(sp)
STORE x5, - 2*WORD(sp)
STORE x6, - 3*WORD(sp)
STORE x7, - 4*WORD(sp)
STORE x10, - 5*WORD(sp)
STORE x11, - 6*WORD(sp)
STORE x12, - 7*WORD(sp)
STORE x13, - 8*WORD(sp)
STORE x14, - 9*WORD(sp)
STORE x15, -10*WORD(sp)
STORE x16, -11*WORD(sp)
STORE x17, -12*WORD(sp)
STORE x28, -13*WORD(sp)
STORE x29, -14*WORD(sp)
STORE x30, -15*WORD(sp)
STORE x31, -16*WORD(sp)
addi sp,sp,-16*WORD
call isr
lw x1 , 15*4(sp)
lw x5, 14*4(sp)
lw x6, 13*4(sp)
lw x7, 12*4(sp)
lw x10, 11*4(sp)
lw x11, 10*4(sp)
lw x12, 9*4(sp)
lw x13, 8*4(sp)
lw x14, 7*4(sp)
lw x15, 6*4(sp)
lw x16, 5*4(sp)
lw x17, 4*4(sp)
lw x28, 3*4(sp)
lw x29, 2*4(sp)
lw x30, 1*4(sp)
lw x31, 0*4(sp)
addi sp,sp,16*4
LOAD x1 , 15*WORD(sp)
LOAD x5, 14*WORD(sp)
LOAD x6, 13*WORD(sp)
LOAD x7, 12*WORD(sp)
LOAD x10, 11*WORD(sp)
LOAD x11, 10*WORD(sp)
LOAD x12, 9*WORD(sp)
LOAD x13, 8*WORD(sp)
LOAD x14, 7*WORD(sp)
LOAD x15, 6*WORD(sp)
LOAD x16, 5*WORD(sp)
LOAD x17, 4*WORD(sp)
LOAD x28, 3*WORD(sp)
LOAD x29, 2*WORD(sp)
LOAD x30, 1*WORD(sp)
LOAD x31, 0*WORD(sp)
addi sp,sp,16*WORD
mret
.text
@ -113,8 +123,10 @@ bss_loop:
j bss_loop
bss_done:
li a0, 0x880 //880 enable timer + external interrupt sources (until mstatus.MIE is set, they will never trigger an interrupt)
csrw mie,a0
call plic_init // initialize external interrupt controller
li t0, 0x800 // external interrupt sources only (using LiteX timer);
// NOTE: must still enable mstatus.MIE!
csrw mie,t0
call main
infinit_loop:

View File

@ -9,30 +9,40 @@ extern "C" {
#include <generated/csr.h>
#include <generated/soc.h>
// NaxRiscv uses a Platform-Level Interrupt Controller (PLIC) which
// is programmed and queried via a set of MMIO registerss
#define PLIC_BASE 0xf0c00000L // Base address and per-pin priority array
#define PLIC_PENDING 0xf0c01000L // Bit field matching currently pending pins
#define PLIC_ENABLED 0xf0c02000L // Bit field corresponding to the current mask
#define PLIC_THRSHLD 0xf0e00000L // Per-pin priority must be >= this to trigger
#define PLIC_CLAIM 0xf0e00004L // Claim & completion register address
#define PLIC_EXT_IRQ_BASE 0
static inline unsigned int irq_getie(void)
{
return 0;
return (csrr(mstatus) & CSR_MSTATUS_MIE) != 0;
}
static inline void irq_setie(unsigned int ie)
{
if(ie) csrs(mstatus,CSR_MSTATUS_MIE); else csrc(mstatus,CSR_MSTATUS_MIE);
}
static inline unsigned int irq_getmask(void)
{
return 0;
return *((unsigned int *)PLIC_ENABLED) >> PLIC_EXT_IRQ_BASE;
}
static inline void irq_setmask(unsigned int mask)
{
*((unsigned int *)PLIC_ENABLED) = mask << PLIC_EXT_IRQ_BASE;
}
static inline unsigned int irq_pending(void)
{
return 0;
return *((unsigned int *)PLIC_PENDING) >> PLIC_EXT_IRQ_BASE;
}
#ifdef __cplusplus

View File

@ -171,11 +171,14 @@ class NEORV32(CPU):
"neorv32_application_image.vhd",
"neorv32_bootloader_image.vhd",
"neorv32_boot_rom.vhd",
"neorv32_cache.vhd",
"neorv32_cfs.vhd",
"neorv32_clockgate.vhd",
"neorv32_cpu_alu.vhd",
"neorv32_cpu_control.vhd",
"neorv32_cpu_cp_bitmanip.vhd",
"neorv32_cpu_cp_cfu.vhd",
"neorv32_cpu_cp_cond.vhd",
"neorv32_cpu_cp_fpu.vhd",
"neorv32_cpu_cp_muldiv.vhd",
"neorv32_cpu_cp_shifter.vhd",
@ -185,7 +188,6 @@ class NEORV32(CPU):
"neorv32_cpu_regfile.vhd",
"neorv32_cpu.vhd",
"neorv32_crc.vhd",
"neorv32_dcache.vhd",
"neorv32_debug_dm.vhd",
"neorv32_debug_dtm.vhd",
"neorv32_dma.vhd",
@ -193,7 +195,6 @@ class NEORV32(CPU):
"neorv32_fifo.vhd",
"neorv32_gpio.vhd",
"neorv32_gptmr.vhd",
"neorv32_icache.vhd",
"neorv32_imem.entity.vhd",
"neorv32_intercon.vhd",
"neorv32_mtime.vhd",
@ -210,7 +211,7 @@ class NEORV32(CPU):
"neorv32_twi.vhd",
"neorv32_uart.vhd",
"neorv32_wdt.vhd",
"neorv32_wishbone.vhd",
"neorv32_xbus.vhd",
"neorv32_xip.vhd",
"neorv32_xirq.vhd",
],
@ -226,8 +227,8 @@ class NEORV32(CPU):
}
# Download VHDL sources (if not already present).
# Version 1.8.9
sha1 = "fdb00a5d24e256ac9a9cb29410f2653c95068c91"
# Version 1.9.7
sha1 = "ed17ae4df64e6a5221562e4adf4de378eaf0c2e8"
for directory, vhds in sources.items():
for vhd in vhds:
self.vhd2v_converter.add_source(os.path.join(cdir, vhd))

View File

@ -89,6 +89,7 @@ class OpenC906(CPU):
flags = "-mno-save-restore "
flags += "-march=rv64gc -mabi=lp64d "
flags += "-D__openc906__ "
flags += "-D__riscv_plic__ "
flags += "-mcmodel=medany"
return flags

View File

@ -18,6 +18,8 @@ extern "C" {
#define PLIC_THRSHLD 0x90200000L // Per-pin priority must be >= this to trigger
#define PLIC_CLAIM 0x90200004L // Claim & completion register address
#define PLIC_EXT_IRQ_BASE 16
static inline unsigned int irq_getie(void)
{
return (csrr(mstatus) & CSR_MSTATUS_MIE) != 0;

View File

@ -168,8 +168,13 @@ class PicoRV32(CPU):
self.comb += [
idbus.adr.eq(mem_addr),
idbus.dat_w.eq(mem_wdata),
idbus.we.eq(mem_wstrb != 0),
idbus.sel.eq(mem_wstrb),
If(mem_wstrb != 0,
idbus.we.eq(1),
idbus.sel.eq(mem_wstrb),
).Else(
idbus.we.eq(0),
idbus.sel.eq(0b1111),
),
idbus.cyc.eq(mem_valid),
idbus.stb.eq(mem_valid),
idbus.cti.eq(0),

View File

@ -113,6 +113,7 @@ class Rocket(CPU):
flags = "-mno-save-restore "
flags += f"-march={self.get_arch(self.variant)} -mabi=lp64 "
flags += "-D__rocket__ "
flags += "-D__riscv_plic__ "
flags += "-mcmodel=medany"
return flags

View File

@ -18,6 +18,8 @@ extern "C" {
#define PLIC_THRSHLD 0x0c200000L // Per-pin priority must be >= this to trigger
#define PLIC_CLAIM 0x0c200004L // Claim & completion register address
#define PLIC_EXT_IRQ_BASE 1
static inline unsigned int irq_getie(void)
{
return (csrr(mstatus) & CSR_MSTATUS_MIE) != 0;

View File

@ -0,0 +1 @@
from litex.soc.cores.cpu.vexiiriscv.core import VexiiRiscv

View File

@ -0,0 +1,15 @@
.section .text, "ax", @progbits
.global boot_helper
.global smp_lottery_target
.global smp_lottery_lock
.global smp_lottery_args
boot_helper:
sw x10, smp_lottery_args , x14
sw x11, smp_lottery_args+4, x14
sw x12, smp_lottery_args+8, x14
sw x13, smp_lottery_target, x14
fence w, w
li x15, 1
sw x15, smp_lottery_lock, x14
jr x13

View File

@ -0,0 +1,582 @@
#
# This file is part of LiteX.
#
# Copyright (c) 2020-2022 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2020-2022 Dolu1990 <charles.papon.90@gmail.com>
# SPDX-License-Identifier: BSD-2-Clause
import os
import hashlib
import subprocess
import re
from migen import *
from litex.gen import *
from litex import get_data_mod
from litex.soc.cores.cpu.naxriscv import NaxRiscv
from litex.soc.interconnect import axi
from litex.soc.interconnect.csr import *
from litex.soc.integration.soc import SoCRegion
from litex.soc.cores.cpu import CPU, CPU_GCC_TRIPLE_RISCV32, CPU_GCC_TRIPLE_RISCV64
# Variants -----------------------------------------------------------------------------------------
CPU_VARIANTS = ["cached", "linux", "debian"]
# VexiiRiscv -----------------------------------------------------------------------------------------
class VexiiRiscv(CPU):
category = "softcore"
family = "riscv"
name = "vexiiriscv"
human_name = "VexiiRiscv"
variants = CPU_VARIANTS
data_width = 32
endianness = "little"
gcc_triple = CPU_GCC_TRIPLE_RISCV32
linker_output_format = "elf32-littleriscv"
nop = "nop"
io_regions = {0x8000_0000: 0x8000_0000} # Origin, Length.
# Default parameters.
netlist_name = None
xlen = 32
internal_bus_width = 32
litedram_width = 32
l2_bytes = 0
l2_ways = 4
l2_self_flush = None
with_rvc = False
with_rvm = False
with_rvf = False
with_rvd = False
with_rva = False
with_dma = False
with_axi3 = False
jtag_tap = False
jtag_instruction = False
vexii_args = ""
# ABI.
@staticmethod
def get_abi():
abi = "lp64" if VexiiRiscv.xlen == 64 else "ilp32"
if VexiiRiscv.with_rvd:
abi +="d"
elif VexiiRiscv.with_rvf:
abi +="f"
return abi
# Arch.
@staticmethod
def get_arch():
arch = f"rv{VexiiRiscv.xlen}i2p0_"
if VexiiRiscv.with_rvm:
arch += "m"
if VexiiRiscv.with_rva:
arch += "a"
if VexiiRiscv.with_rvf:
arch += "f"
if VexiiRiscv.with_rvd:
arch += "d"
if VexiiRiscv.with_rvc:
arch += "c"
# arch += "zicntr"
# arch += "zicsr"
# arch += "zifencei"
# arch += "zihpm"
# arch += "sscofpmf"
return arch
# Memory Mapping.
@property
def mem_map(self):
return {
"rom": 0x0000_0000,
"sram": 0x1000_0000,
"main_ram": 0x4000_0000,
"csr": 0xf000_0000,
"clint": 0xf001_0000,
"plic": 0xf0c0_0000,
}
# GCC Flags.
@property
def gcc_flags(self):
flags = f" -march={VexiiRiscv.get_arch()} -mabi={VexiiRiscv.get_abi()}"
flags += f" -D__VexiiRiscv__"
flags += f" -D__riscv_plic__"
return flags
# Reserved Interrupts.
@property
def reserved_interrupts(self):
return {"noirq": 0}
# Command line configuration arguments.
@staticmethod
def args_fill(parser):
cpu_group = parser.add_argument_group(title="CPU options")
cpu_group.add_argument("--vexii-args", default="", help="Specify the CPU configuration")
# cpu_group.add_argument("--xlen", default=32, help="Specify the RISC-V data width.")
cpu_group.add_argument("--cpu-count", default=1, help="How many VexiiRiscv CPU.")
cpu_group.add_argument("--with-coherent-dma", action="store_true", help="Enable coherent DMA accesses.")
cpu_group.add_argument("--with-jtag-tap", action="store_true", help="Add a embedded JTAG tap for debugging.")
cpu_group.add_argument("--with-jtag-instruction", action="store_true", help="Add a JTAG instruction port which implement tunneling for debugging (TAP not included).")
cpu_group.add_argument("--update-repo", default="recommended", choices=["latest","wipe+latest","recommended","wipe+recommended","no"], help="Specify how the VexiiRiscv & SpinalHDL repo should be updated (latest: update to HEAD, recommended: Update to known compatible version, no: Don't update, wipe+*: Do clean&reset before checkout)")
cpu_group.add_argument("--no-netlist-cache", action="store_true", help="Always (re-)build the netlist.")
# cpu_group.add_argument("--with-fpu", action="store_true", help="Enable the F32/F64 FPU.")
# cpu_group.add_argument("--with-rvc", action="store_true", help="Enable the Compress ISA extension.")
cpu_group.add_argument("--l2-bytes", default=0, help="VexiiRiscv L2 bytes, default 128 KB.")
cpu_group.add_argument("--l2-ways", default=0, help="VexiiRiscv L2 ways, default 8.")
cpu_group.add_argument("--l2-self-flush", default=None, help="VexiiRiscv L2 ways will self flush on from,to,cycles")
cpu_group.add_argument("--with-axi3", action="store_true", help="mbus will be axi3 instead of axi4")
@staticmethod
def args_read(args):
print(args)
vdir = get_data_mod("cpu", "vexiiriscv").data_location
ndir = os.path.join(vdir, "ext", "VexiiRiscv")
NaxRiscv.git_setup("VexiiRiscv", ndir, "https://github.com/SpinalHDL/VexiiRiscv.git", "dev", "36dad634", args.update_repo)
if not args.cpu_variant:
args.cpu_variant = "standard"
VexiiRiscv.vexii_args += " --with-mul --with-div --allow-bypass-from=0 --performance-counters=0"
VexiiRiscv.vexii_args += " --fetch-l1 --fetch-l1-ways=2"
VexiiRiscv.vexii_args += " --lsu-l1 --lsu-l1-ways=2 --with-lsu-bypass"
VexiiRiscv.vexii_args += " --relaxed-branch"
if args.cpu_variant in ["linux", "debian"]:
VexiiRiscv.vexii_args += " --with-rva --with-supervisor"
VexiiRiscv.vexii_args += " --fetch-l1-ways=4 --fetch-l1-mem-data-width-min=64"
VexiiRiscv.vexii_args += " --lsu-l1-ways=4 --lsu-l1-mem-data-width-min=64"
if args.cpu_variant in ["debian"]:
VexiiRiscv.vexii_args += " --xlen=64 --with-rvc --with-rvf --with-rvd --fma-reduced-accuracy --fpu-ignore-subnormal"
if args.cpu_variant in ["linux", "debian"]:
VexiiRiscv.vexii_args += " --with-btb --with-ras --with-gshare"
VexiiRiscv.jtag_tap = args.with_jtag_tap
VexiiRiscv.jtag_instruction = args.with_jtag_instruction
VexiiRiscv.with_dma = args.with_coherent_dma
VexiiRiscv.with_axi3 = args.with_axi3
VexiiRiscv.update_repo = args.update_repo
VexiiRiscv.no_netlist_cache = args.no_netlist_cache
VexiiRiscv.vexii_args += " " + args.vexii_args
md5_hash = hashlib.md5()
md5_hash.update(VexiiRiscv.vexii_args.encode('utf-8'))
vexii_args_hash = md5_hash.hexdigest()
ppath = os.path.join(vdir, str(vexii_args_hash) + ".py")
if VexiiRiscv.no_netlist_cache or not os.path.exists(ppath):
cmd = f"""cd {ndir} && sbt "runMain vexiiriscv.soc.litex.PythonArgsGen {VexiiRiscv.vexii_args} --python-file={str(ppath)}\""""
subprocess.check_call(cmd, shell=True)
with open(ppath) as file:
exec(file.read())
if VexiiRiscv.xlen == 64:
VexiiRiscv.gcc_triple = CPU_GCC_TRIPLE_RISCV64
VexiiRiscv.linker_output_format = f"elf{VexiiRiscv.xlen}-littleriscv"
if args.cpu_count:
VexiiRiscv.cpu_count = args.cpu_count
if args.l2_bytes:
VexiiRiscv.l2_bytes = args.l2_bytes
if args.l2_ways:
VexiiRiscv.l2_ways = args.l2_ways
if args.l2_self_flush:
VexiiRiscv.l2_self_flush = args.l2_self_flush
def __init__(self, platform, variant):
self.platform = platform
self.variant = "standard"
self.reset = Signal()
self.interrupt = Signal(32)
self.pbus = pbus = axi.AXILiteInterface(address_width=32, data_width=32)
self.periph_buses = [pbus] # Peripheral buses (Connected to main SoC's bus).
self.memory_buses = [] # Memory buses (Connected directly to LiteDRAM).
# # #
self.tracer_valid = Signal()
self.tracer_payload = Signal(8)
# CPU Instance.
self.cpu_params = dict(
# Clk/Rst.
i_system_clk = ClockSignal("sys"),
i_system_reset = ResetSignal("sys") | self.reset,
# Patcher/Tracer.
# o_patcher_tracer_valid = self.tracer_valid,
# o_patcher_tracer_payload = self.tracer_payload,
# Interrupt.
i_peripheral_externalInterrupts_port = self.interrupt,
# Peripheral Memory Bus (AXI Lite Slave).
o_pBus_awvalid = pbus.aw.valid,
i_pBus_awready = pbus.aw.ready,
o_pBus_awaddr = pbus.aw.addr,
o_pBus_awprot = Open(),
o_pBus_wvalid = pbus.w.valid,
i_pBus_wready = pbus.w.ready,
o_pBus_wdata = pbus.w.data,
o_pBus_wstrb = pbus.w.strb,
i_pBus_bvalid = pbus.b.valid,
o_pBus_bready = pbus.b.ready,
i_pBus_bresp = pbus.b.resp,
o_pBus_arvalid = pbus.ar.valid,
i_pBus_arready = pbus.ar.ready,
o_pBus_araddr = pbus.ar.addr,
o_pBus_arprot = Open(),
i_pBus_rvalid = pbus.r.valid,
o_pBus_rready = pbus.r.ready,
i_pBus_rdata = pbus.r.data,
i_pBus_rresp = pbus.r.resp,
)
if VexiiRiscv.with_dma:
self.dma_bus = dma_bus = axi.AXIInterface(data_width=VexiiRiscv.internal_bus_width, address_width=32, id_width=4)
self.cpu_params.update(
# DMA Bus.
# --------
# AW Channel.
o_dma_bus_awready = dma_bus.aw.ready,
i_dma_bus_awvalid = dma_bus.aw.valid,
i_dma_bus_awid = dma_bus.aw.id,
i_dma_bus_awaddr = dma_bus.aw.addr,
i_dma_bus_awlen = dma_bus.aw.len,
i_dma_bus_awsize = dma_bus.aw.size,
i_dma_bus_awburst = dma_bus.aw.burst,
i_dma_bus_awlock = dma_bus.aw.lock,
i_dma_bus_awcache = dma_bus.aw.cache,
i_dma_bus_awprot = dma_bus.aw.prot,
i_dma_bus_awqos = dma_bus.aw.qos,
# W Channel.
o_dma_bus_wready = dma_bus.w.ready,
i_dma_bus_wvalid = dma_bus.w.valid,
i_dma_bus_wdata = dma_bus.w.data,
i_dma_bus_wstrb = dma_bus.w.strb,
i_dma_bus_wlast = dma_bus.w.last,
# B Channel.
i_dma_bus_bready = dma_bus.b.ready,
o_dma_bus_bvalid = dma_bus.b.valid,
o_dma_bus_bid = dma_bus.b.id,
o_dma_bus_bresp = dma_bus.b.resp,
# AR Channel.
o_dma_bus_arready = dma_bus.ar.ready,
i_dma_bus_arvalid = dma_bus.ar.valid,
i_dma_bus_arid = dma_bus.ar.id,
i_dma_bus_araddr = dma_bus.ar.addr,
i_dma_bus_arlen = dma_bus.ar.len,
i_dma_bus_arsize = dma_bus.ar.size,
i_dma_bus_arburst = dma_bus.ar.burst,
i_dma_bus_arlock = dma_bus.ar.lock,
i_dma_bus_arcache = dma_bus.ar.cache,
i_dma_bus_arprot = dma_bus.ar.prot,
i_dma_bus_arqos = dma_bus.ar.qos,
# R Channel.
i_dma_bus_rready = dma_bus.r.ready,
o_dma_bus_rvalid = dma_bus.r.valid,
o_dma_bus_rid = dma_bus.r.id,
o_dma_bus_rdata = dma_bus.r.data,
o_dma_bus_rresp = dma_bus.r.resp,
o_dma_bus_rlast = dma_bus.r.last,
)
def set_reset_address(self, reset_address):
VexiiRiscv.reset_address = reset_address
VexiiRiscv.vexii_args += f" --reset-vector {reset_address}"
# Cluster Name Generation.
@staticmethod
def generate_netlist_name():
md5_hash = hashlib.md5()
md5_hash.update(str(VexiiRiscv.reset_address).encode('utf-8'))
md5_hash.update(str(VexiiRiscv.litedram_width).encode('utf-8'))
md5_hash.update(str(VexiiRiscv.xlen).encode('utf-8'))
md5_hash.update(str(VexiiRiscv.cpu_count).encode('utf-8'))
md5_hash.update(str(VexiiRiscv.l2_bytes).encode('utf-8'))
md5_hash.update(str(VexiiRiscv.l2_ways).encode('utf-8'))
md5_hash.update(str(VexiiRiscv.l2_self_flush).encode('utf-8'))
md5_hash.update(str(VexiiRiscv.jtag_tap).encode('utf-8'))
md5_hash.update(str(VexiiRiscv.jtag_instruction).encode('utf-8'))
md5_hash.update(str(VexiiRiscv.with_dma).encode('utf-8'))
md5_hash.update(str(VexiiRiscv.with_axi3).encode('utf-8'))
md5_hash.update(str(VexiiRiscv.memory_regions).encode('utf-8'))
md5_hash.update(str(VexiiRiscv.vexii_args).encode('utf-8'))
# md5_hash.update(str(VexiiRiscv.internal_bus_width).encode('utf-8'))
digest = md5_hash.hexdigest()
VexiiRiscv.netlist_name = "VexiiRiscvLitex_" + digest
# Netlist Generation.
@staticmethod
def generate_netlist():
vdir = get_data_mod("cpu", "vexiiriscv").data_location
ndir = os.path.join(vdir, "ext", "VexiiRiscv")
sdir = os.path.join(vdir, "ext", "SpinalHDL")
gen_args = []
gen_args.append(f"--netlist-name={VexiiRiscv.netlist_name}")
gen_args.append(f"--netlist-directory={vdir}")
gen_args.append(VexiiRiscv.vexii_args)
gen_args.append(f"--cpu-count={VexiiRiscv.cpu_count}")
gen_args.append(f"--l2-bytes={VexiiRiscv.l2_bytes}")
gen_args.append(f"--l2-ways={VexiiRiscv.l2_ways}")
if VexiiRiscv.l2_self_flush:
gen_args.append(f"--l2-self-flush={VexiiRiscv.l2_self_flush}")
gen_args.append(f"--litedram-width={VexiiRiscv.litedram_width}")
# gen_args.append(f"--internal_bus_width={VexiiRiscv.internal_bus_width}")
for region in VexiiRiscv.memory_regions:
gen_args.append(f"--memory-region={region[0]},{region[1]},{region[2]},{region[3]}")
if(VexiiRiscv.jtag_tap) :
gen_args.append(f"--with-jtag-tap")
if(VexiiRiscv.jtag_instruction) :
gen_args.append(f"--with-jtag-instruction")
if(VexiiRiscv.with_dma) :
gen_args.append(f"--with-dma")
if(VexiiRiscv.with_axi3) :
gen_args.append(f"--with-axi3")
cmd = f"""cd {ndir} && sbt "runMain vexiiriscv.soc.litex.SocGen {" ".join(gen_args)}\""""
print("VexiiRiscv generation command :")
print(cmd)
subprocess.check_call(cmd, shell=True)
def add_sources(self, platform):
vdir = get_data_mod("cpu", "vexiiriscv").data_location
print(f"VexiiRiscv netlist : {self.netlist_name}")
if VexiiRiscv.no_netlist_cache or not os.path.exists(os.path.join(vdir, self.netlist_name + ".v")):
self.generate_netlist()
# Add RAM.
# By default, use Generic RAM implementation.
ram_filename = "Ram_1w_1rs_Generic.v"
lutram_filename = "Ram_1w_1ra_Generic.v"
# On Altera/Intel platforms, use specific implementation.
from litex.build.altera import AlteraPlatform
if isinstance(platform, AlteraPlatform):
ram_filename = "Ram_1w_1rs_Intel.v"
# On Efinix platforms, use specific implementation.
from litex.build.efinix import EfinixPlatform
if isinstance(platform, EfinixPlatform):
ram_filename = "Ram_1w_1rs_Efinix.v"
platform.add_source(os.path.join(vdir, ram_filename), "verilog")
platform.add_source(os.path.join(vdir, lutram_filename), "verilog")
# Add Cluster.
platform.add_source(os.path.join(vdir, self.netlist_name + ".v"), "verilog")
def add_soc_components(self, soc):
# Set Human-name.
self.human_name = f"{self.human_name} {self.xlen}-bit"
# Set UART/Timer0 CSRs to the ones used by OpenSBI.
soc.csr.add("uart", n=2)
soc.csr.add("timer0", n=3)
# Add OpenSBI region.
soc.bus.add_region("opensbi", SoCRegion(origin=self.mem_map["main_ram"] + 0x00f0_0000, size=0x8_0000, cached=True, linker=True))
# Define ISA.
soc.add_config("CPU_COUNT", VexiiRiscv.cpu_count)
soc.add_config("CPU_ISA", VexiiRiscv.get_arch())
soc.add_config("CPU_MMU", {32 : "sv32", 64 : "sv39"}[VexiiRiscv.xlen])
soc.bus.add_region("plic", SoCRegion(origin=soc.mem_map.get("plic"), size=0x40_0000, cached=False, linker=True))
soc.bus.add_region("clint", SoCRegion(origin=soc.mem_map.get("clint"), size= 0x1_0000, cached=False, linker=True))
if VexiiRiscv.jtag_tap:
self.jtag_tms = Signal()
self.jtag_clk = Signal()
self.jtag_tdi = Signal()
self.jtag_tdo = Signal()
self.cpu_params.update(
i_debug_tap_jtag_tms = self.jtag_tms,
i_debug_tap_jtag_tck = self.jtag_clk,
i_debug_tap_jtag_tdi = self.jtag_tdi,
o_debug_tap_jtag_tdo = self.jtag_tdo,
)
if VexiiRiscv.jtag_instruction:
self.jtag_clk = Signal()
self.jtag_enable = Signal()
self.jtag_capture = Signal()
self.jtag_shift = Signal()
self.jtag_update = Signal()
self.jtag_reset = Signal()
self.jtag_tdo = Signal()
self.jtag_tdi = Signal()
self.cpu_params.update(
i_debug_tck = self.jtag_clk,
i_debug_instruction_instruction_enable = self.jtag_enable,
i_debug_instruction_instruction_capture = self.jtag_capture,
i_debug_instruction_instruction_shift = self.jtag_shift,
i_debug_instruction_instruction_update = self.jtag_update,
i_debug_instruction_instruction_reset = self.jtag_reset,
i_debug_instruction_instruction_tdi = self.jtag_tdi,
o_debug_instruction_instruction_tdo = self.jtag_tdo,
)
if VexiiRiscv.jtag_instruction or VexiiRiscv.jtag_tap:
# Create PoR Clk Domain for debug_reset.
self.cd_debug_por = ClockDomain()
self.comb += self.cd_debug_por.clk.eq(ClockSignal("sys"))
# Create PoR debug_reset.
debug_reset = Signal(reset=1)
self.sync.debug_por += debug_reset.eq(0)
# Debug resets.
debug_ndmreset = Signal()
debug_ndmreset_last = Signal()
debug_ndmreset_rise = Signal() # debug_ndmreset_rise is necessary because the PLL which generate the clock will be reseted aswell, so we need to sneak in a single cycle reset :(
self.cpu_params.update(
i_debugReset = debug_reset,
o_debug_dm_ndmreset = debug_ndmreset,
)
# Reset SoC's CRG when debug_ndmreset rising edge.
self.sync.debug_por += debug_ndmreset_last.eq(debug_ndmreset)
self.comb += debug_ndmreset_rise.eq(debug_ndmreset & ~debug_ndmreset_last)
if soc.get_build_name() == "sim":
self.comb += If(debug_ndmreset_rise, soc.crg.cd_sys.rst.eq(1))
else:
self.comb += If(debug_ndmreset_rise, soc.crg.rst.eq(1))
self.soc_bus = soc.bus # FIXME: Save SoC Bus instance to retrieve the final mem layout on finalization.
def add_memory_buses(self, address_width, data_width):
VexiiRiscv.litedram_width = data_width
mbus = axi.AXIInterface(
data_width = VexiiRiscv.litedram_width,
address_width = 32,
id_width = 8,
version = "axi3" if VexiiRiscv.with_axi3 else "axi4"
)
self.memory_buses.append(mbus)
self.comb += mbus.aw.cache.eq(0xF)
self.comb += mbus.aw.lock.eq(0)
self.comb += mbus.aw.prot.eq(1)
self.comb += mbus.aw.qos.eq(0)
#self.comb += mbus.aw.region.eq(0)
self.comb += mbus.ar.cache.eq(0xF)
self.comb += mbus.ar.lock.eq(0)
self.comb += mbus.ar.prot.eq(1)
self.comb += mbus.ar.qos.eq(0)
#self.comb += mbus.ar.region.eq(0)
self.cpu_params.update(
# Memory Bus (Master).
# --------------------
# AW Channel.
o_mBus_awvalid = mbus.aw.valid,
i_mBus_awready = mbus.aw.ready,
o_mBus_awaddr = mbus.aw.addr,
o_mBus_awid = mbus.aw.id,
o_mBus_awlen = mbus.aw.len,
o_mBus_awsize = mbus.aw.size,
o_mBus_awburst = mbus.aw.burst,
o_mBus_awallStrb = Open(),
# W Channel.
o_mBus_wvalid = mbus.w.valid,
i_mBus_wready = mbus.w.ready,
o_mBus_wdata = mbus.w.data,
o_mBus_wstrb = mbus.w.strb,
o_mBus_wlast = mbus.w.last,
# B Channel.
i_mBus_bvalid = mbus.b.valid,
o_mBus_bready = mbus.b.ready,
i_mBus_bid = mbus.b.id,
i_mBus_bresp = mbus.b.resp,
# AR Channel.
o_mBus_arvalid = mbus.ar.valid,
i_mBus_arready = mbus.ar.ready,
o_mBus_araddr = mbus.ar.addr,
o_mBus_arid = mbus.ar.id,
o_mBus_arlen = mbus.ar.len,
o_mBus_arsize = mbus.ar.size,
o_mBus_arburst = mbus.ar.burst,
# R Channel.
i_mBus_rvalid = mbus.r.valid,
o_mBus_rready = mbus.r.ready,
i_mBus_rdata = mbus.r.data,
i_mBus_rid = mbus.r.id,
i_mBus_rresp = mbus.r.resp,
i_mBus_rlast = mbus.r.last,
)
if VexiiRiscv.with_axi3:
self.cpu_params.update(
o_mBus_wid=mbus.w.id
)
def add_jtag(self, pads):
self.comb += [
self.jtag_tms.eq(pads.tms),
self.jtag_clk.eq(pads.tck),
self.jtag_tdi.eq(pads.tdi),
pads.tdo.eq(self.jtag_tdo),
]
def do_finalize(self):
assert hasattr(self, "reset_address")
# Generate memory map from CPU perspective
# vexiiriscv modes:
# r,w,x,c : readable, writeable, executable, caching allowed
# io : IO region (Implies P bus, preserve memory order, no dcache)
# vexiiriscv bus:
# p : peripheral
# m : memory
VexiiRiscv.memory_regions = []
# for name, region in self.soc_bus.io_regions.items():
# VexiiRiscv.memory_regions.append( (region.origin, region.size, "io", "p") ) # IO is only allowed on the p bus
for name, region in self.soc_bus.regions.items():
if region.linker: # Remove virtual regions.
continue
if len(self.memory_buses) and name == 'main_ram': # m bus
bus = "m"
else:
bus = "p"
mode = region.mode
mode += "c" if region.cached else ""
VexiiRiscv.memory_regions.append( (region.origin, region.size, mode, bus) )
self.generate_netlist_name()
# Do verilog instance.
self.specials += Instance(self.netlist_name, **self.cpu_params)
# Add verilog sources.
self.add_sources(self.platform)

View File

@ -0,0 +1,141 @@
.global main
.global isr
.global _start
.global smp_lottery_target
.global smp_lottery_lock
.global smp_lottery_args
.global smp_slave
#define MSTATUS_FS_INITIAL (1 << 13)
#define MSTATUS_FS_CLEAN (2 << 13)
#define MSTATUS_FS_DIRTY (3 << 13)
#define MSTATUS_FS_MASK (3 << 13)
#if __riscv_xlen == 64
#define STORE sd
#define LOAD ld
#define WORD 8
#else
#define STORE sw
#define LOAD lw
#define WORD 4
#endif
_start:
j crt_init
nop
nop
nop
nop
nop
nop
nop
.global trap_entry
trap_entry:
STORE x1, - 1*WORD(sp)
STORE x5, - 2*WORD(sp)
STORE x6, - 3*WORD(sp)
STORE x7, - 4*WORD(sp)
STORE x10, - 5*WORD(sp)
STORE x11, - 6*WORD(sp)
STORE x12, - 7*WORD(sp)
STORE x13, - 8*WORD(sp)
STORE x14, - 9*WORD(sp)
STORE x15, -10*WORD(sp)
STORE x16, -11*WORD(sp)
STORE x17, -12*WORD(sp)
STORE x28, -13*WORD(sp)
STORE x29, -14*WORD(sp)
STORE x30, -15*WORD(sp)
STORE x31, -16*WORD(sp)
addi sp,sp,-16*WORD
call isr
LOAD x1 , 15*WORD(sp)
LOAD x5, 14*WORD(sp)
LOAD x6, 13*WORD(sp)
LOAD x7, 12*WORD(sp)
LOAD x10, 11*WORD(sp)
LOAD x11, 10*WORD(sp)
LOAD x12, 9*WORD(sp)
LOAD x13, 8*WORD(sp)
LOAD x14, 7*WORD(sp)
LOAD x15, 6*WORD(sp)
LOAD x16, 5*WORD(sp)
LOAD x17, 4*WORD(sp)
LOAD x28, 3*WORD(sp)
LOAD x29, 2*WORD(sp)
LOAD x30, 1*WORD(sp)
LOAD x31, 0*WORD(sp)
addi sp,sp,16*WORD
mret
.text
crt_init:
la sp, _fstack
la a0, trap_entry
csrw mtvec, a0
enable_fpu:
li x1, MSTATUS_FS_INITIAL
csrs mstatus, x1
sw x0, smp_lottery_lock, a1
smp_tyranny:
csrr a0, mhartid
beqz a0, data_init
smp_slave:
lw a0, smp_lottery_lock
beqz a0, smp_slave
fence r, r
.word(0x100F) //i$ flush
lw x10, smp_lottery_args
lw x11, smp_lottery_args+4
lw x12, smp_lottery_args+8
lw x13, smp_lottery_target
jr x13
data_init:
la a0, _fdata
la a1, _edata
la a2, _fdata_rom
data_loop:
beq a0,a1,data_done
lw a3,0(a2)
sw a3,0(a0)
add a0,a0,4
add a2,a2,4
j data_loop
data_done:
bss_init:
la a0, _fbss
la a1, _ebss
bss_loop:
beq a0,a1,bss_done
sw zero,0(a0)
add a0,a0,4
j bss_loop
bss_done:
call plic_init // initialize external interrupt controller
li t0, 0x800 // external interrupt sources only (using LiteX timer);
// NOTE: must still enable mstatus.MIE!
csrw mie,t0
call main
infinit_loop:
j infinit_loop
//Initialized to avoid having them set to zero by BSS clear
.bss
smp_lottery_target: .word 0
smp_lottery_args: .word 0; .word 0; .word 0
smp_lottery_lock: .word 0

View File

@ -0,0 +1,9 @@
#ifndef CSR_DEFS__H
#define CSR_DEFS__H
#define CSR_MSTATUS_MIE 0x8
#define CSR_IRQ_MASK 0xBC0
#define CSR_IRQ_PENDING 0xFC0
#endif /* CSR_DEFS__H */

View File

@ -0,0 +1,52 @@
#ifndef __IRQ_H
#define __IRQ_H
#ifdef __cplusplus
extern "C" {
#endif
#include <system.h>
#include <generated/csr.h>
#include <generated/soc.h>
// VexiiRiscv uses a Platform-Level Interrupt Controller (PLIC) which
// is programmed and queried via a set of MMIO registerss
#define PLIC_BASE 0xf0c00000L // Base address and per-pin priority array
#define PLIC_PENDING 0xf0c01000L // Bit field matching currently pending pins
#define PLIC_ENABLED 0xf0c02000L // Bit field corresponding to the current mask
#define PLIC_THRSHLD 0xf0e00000L // Per-pin priority must be >= this to trigger
#define PLIC_CLAIM 0xf0e00004L // Claim & completion register address
#define PLIC_EXT_IRQ_BASE 0
static inline unsigned int irq_getie(void)
{
return (csrr(mstatus) & CSR_MSTATUS_MIE) != 0;
}
static inline void irq_setie(unsigned int ie)
{
if(ie) csrs(mstatus,CSR_MSTATUS_MIE); else csrc(mstatus,CSR_MSTATUS_MIE);
}
static inline unsigned int irq_getmask(void)
{
return *((unsigned int *)PLIC_ENABLED) >> PLIC_EXT_IRQ_BASE;
}
static inline void irq_setmask(unsigned int mask)
{
*((unsigned int *)PLIC_ENABLED) = mask << PLIC_EXT_IRQ_BASE;
}
static inline unsigned int irq_pending(void)
{
return *((unsigned int *)PLIC_PENDING) >> PLIC_EXT_IRQ_BASE;
}
#ifdef __cplusplus
}
#endif
#endif /* __IRQ_H */

View File

@ -0,0 +1,55 @@
#ifndef __SYSTEM_H
#define __SYSTEM_H
#include <csr-defs.h>
#ifdef __cplusplus
extern "C" {
#endif
__attribute__((unused)) static void flush_cpu_icache(void)
{
asm volatile(
"fence.i\n"
);
}
__attribute__((unused)) static void flush_cpu_dcache(void)
{
//asm volatile(".word(0x500F)\n");
}
void flush_l2_cache(void);
void busy_wait(unsigned int ms);
void busy_wait_us(unsigned int us);
#include <csr-defs.h>
#define csrr(reg) ({ unsigned long __tmp; \
asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \
__tmp; })
#define csrw(reg, val) ({ \
if (__builtin_constant_p(val) && (unsigned long)(val) < 32) \
asm volatile ("csrw " #reg ", %0" :: "i"(val)); \
else \
asm volatile ("csrw " #reg ", %0" :: "r"(val)); })
#define csrs(reg, bit) ({ \
if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \
asm volatile ("csrrs x0, " #reg ", %0" :: "i"(bit)); \
else \
asm volatile ("csrrs x0, " #reg ", %0" :: "r"(bit)); })
#define csrc(reg, bit) ({ \
if (__builtin_constant_p(bit) && (unsigned long)(bit) < 32) \
asm volatile ("csrrc x0, " #reg ", %0" :: "i"(bit)); \
else \
asm volatile ("csrrc x0, " #reg ", %0" :: "r"(bit)); })
#ifdef __cplusplus
}
#endif
#endif /* __SYSTEM_H */

View File

@ -210,6 +210,7 @@ class VexRiscv(CPU, AutoCSR):
self.o_cmd_ready = Signal()
self.o_rsp_data = Signal(32)
self.o_resetOut = Signal()
self.o_halted = Signal()
reset_debug_logic = Signal()
@ -278,7 +279,8 @@ class VexRiscv(CPU, AutoCSR):
i_debug_bus_cmd_payload_data = self.i_cmd_payload_data,
o_debug_bus_cmd_ready = self.o_cmd_ready,
o_debug_bus_rsp_data = self.o_rsp_data,
o_debug_resetOut = self.o_resetOut
o_debug_resetOut = self.o_resetOut,
o_halted = self.o_halted,
)
def add_cfu(self, cfu_filename):

View File

@ -168,8 +168,8 @@ class VexRiscvSMP(CPU):
@property
def gcc_flags(self):
flags = f" -march={VexRiscvSMP.get_arch()} -mabi={VexRiscvSMP.get_abi()}"
flags += f" -D__vexriscv__"
flags += f" -DUART_POLLING"
flags += f" -D__vexriscv_smp__"
flags += f" -D__riscv_plic__"
return flags
# Reserved Interrupts.
@ -463,6 +463,14 @@ class VexRiscvSMP(CPU):
add_synthesis_define(cluster_filename)
platform.add_source(cluster_filename, "verilog")
def add_jtag(self, pads):
self.comb += [
self.jtag_tms.eq(pads.tms),
self.jtag_clk.eq(pads.tck),
self.jtag_tdi.eq(pads.tdi),
pads.tdo.eq(self.jtag_tdo),
]
def add_soc_components(self, soc):
if self.variant == "linux":
# Set UART/Timer0 CSRs to the ones used by OpenSBI.

View File

@ -102,6 +102,11 @@ bss_loop:
j bss_loop
bss_done:
call plic_init // initialize external interrupt controller
li t0, 0x800 // external interrupt sources only (using LiteX timer);
// NOTE: must still enable mstatus.MIE!
csrw mie,t0
call main
infinit_loop:
j infinit_loop

View File

@ -9,30 +9,40 @@ extern "C" {
#include <generated/csr.h>
#include <generated/soc.h>
// VexRiscv-SMP uses a Platform-Level Interrupt Controller (PLIC) which
// is programmed and queried via a set of MMIO registerss
#define PLIC_BASE 0xf0c00000L // Base address and per-pin priority array
#define PLIC_PENDING 0xf0c01000L // Bit field matching currently pending pins
#define PLIC_ENABLED 0xf0c02000L // Bit field corresponding to the current mask
#define PLIC_THRSHLD 0xf0e00000L // Per-pin priority must be >= this to trigger
#define PLIC_CLAIM 0xf0e00004L // Claim & completion register address
#define PLIC_EXT_IRQ_BASE 0
static inline unsigned int irq_getie(void)
{
return 0;
return (csrr(mstatus) & CSR_MSTATUS_MIE) != 0;
}
static inline void irq_setie(unsigned int ie)
{
if(ie) csrs(mstatus,CSR_MSTATUS_MIE); else csrc(mstatus,CSR_MSTATUS_MIE);
}
static inline unsigned int irq_getmask(void)
{
return 0;
return *((unsigned int *)PLIC_ENABLED) >> PLIC_EXT_IRQ_BASE;
}
static inline void irq_setmask(unsigned int mask)
{
*((unsigned int *)PLIC_ENABLED) = mask << PLIC_EXT_IRQ_BASE;
}
static inline unsigned int irq_pending(void)
{
return 0;
return *((unsigned int *)PLIC_PENDING) >> PLIC_EXT_IRQ_BASE;
}
#ifdef __cplusplus

View File

@ -40,6 +40,7 @@ class Zynq7000(CPU):
def mem_map(self):
return {
"sram": 0x0010_0000, # DDR in fact
"csr": 0x4000_0000, # default GP0 address on Zynq
"rom": 0xfc00_0000,
}
@ -54,6 +55,13 @@ class Zynq7000(CPU):
self.axi_gp_slaves = [] # General Purpose AXI Slaves.
self.axi_hp_slaves = [] # High Performance AXI Slaves.
# PS7 peripherals.
self.can_use = []
# [ 7: 0]: SPI Numbers [68:61]
# [15: 8]: SPI Numbers [91:84]
self.interrupt = Signal(16)
# # #
# PS7 Clocking.
@ -62,6 +70,12 @@ class Zynq7000(CPU):
# PS7 (Minimal) ----------------------------------------------------------------------------
self.ps7_name = None
self.ps7_tcl = []
self.config = {
# Enable interrupts by default
"PCW_USE_FABRIC_INTERRUPT" : 1,
"PCW_IRQ_F2P_INTR" : 1,
"PCW_NUM_F2P_INTR_INPUTS" : 16,
}
ps7_rst_n = Signal()
ps7_ddram_pads = platform.request("ps7_ddram")
self.cpu_params = dict(
@ -95,6 +109,9 @@ class Zynq7000(CPU):
# USB0.
i_USB0_VBUS_PWRFAULT = 0,
# Interrupts PL -> PS.
i_IRQ_F2P = self.interrupt,
# Fabric Clk / Rst.
o_FCLK_CLK0 = ClockSignal("ps7"),
o_FCLK_RESET0_N = ps7_rst_n
@ -154,6 +171,10 @@ class Zynq7000(CPU):
if ps7_sdio0_wp_pads is not None:
self.cpu_params.update(i_SDIO0_WP = ps7_sdio0_wp_pads.wp)
# GP0 as Bus master ------------------------------------------------------------------------
self.pbus = self.add_axi_gp_master()
self.periph_buses.append(self.pbus)
def set_ps7_xci(self, xci):
# Add .xci as Vivado IP and set ps7_name from .xci filename.
self.ps7_xci = xci
@ -161,16 +182,9 @@ class Zynq7000(CPU):
self.platform.add_ip(xci)
def add_ps7_config(self, config):
# Check that PS7 has been set.
if self.ps7_name is None:
raise Exception("Please set PS7 with set_ps7 method first.")
# Config must be provided as a config, value dict.
assert isinstance(config, dict)
# Add configs to PS7.
self.ps7_tcl.append("set_property -dict [list \\")
for config, value in config.items():
self.ps7_tcl.append("CONFIG.{} {} \\".format(config, '{{' + str(value) + '}}'))
self.ps7_tcl.append(f"] [get_ips {self.ps7_name}]")
self.config.update(config)
def set_ps7(self, name=None, xci=None, preset=None, config=None):
# Check that PS7 has not already been set.
@ -214,8 +228,23 @@ class Zynq7000(CPU):
def add_axi_gp_master(self):
assert len(self.axi_gp_masters) < 2
n = len(self.axi_gp_masters)
axi_gpn = axi.AXIInterface(data_width=32, address_width=32, id_width=12)
axi_gpn = axi.AXIInterface(
data_width = 32,
address_width = 32,
id_width = 12,
version = "axi3"
)
self.axi_gp_masters.append(axi_gpn)
self.add_ps7_config({
f"PCW_USE_M_AXI_GP{n}": 1,
#f"PCW_M_AXI_GP{n}_FREQMHZ": 100, # FIXME: parameter?
f"PCW_M_AXI_GP{n}_ID_WIDTH": 12,
f"PCW_M_AXI_GP{n}_ENABLE_STATIC_REMAP": 0,
f"PCW_M_AXI_GP{n}_SUPPORT_NARROW_BURST": 0,
f"PCW_M_AXI_GP{n}_THREAD_ID_WIDTH": 12,
})
self.cpu_params.update({
# AXI GP clk.
f"i_M_AXI_GP{n}_ACLK" : ClockSignal("ps7"),
@ -275,7 +304,13 @@ class Zynq7000(CPU):
def add_axi_gp_slave(self, clock_domain="ps7"):
assert len(self.axi_gp_slaves) < 2
n = len(self.axi_gp_slaves)
axi_gpn = axi.AXIInterface(data_width=32, address_width=32, id_width=12, clock_domain=clock_domain)
axi_gpn = axi.AXIInterface(
data_width = 32,
address_width = 32,
id_width = 12,
version = "axi3",
clock_domain = clock_domain
)
self.axi_gp_slaves.append(axi_gpn)
self.cpu_params.update({
#AXI S GP clk.
@ -333,14 +368,20 @@ class Zynq7000(CPU):
# AXI High Performance Slave -------------------------------------------------------------------
def add_axi_hp_slave(self):
def add_axi_hp_slave(self, clock_domain="ps7"):
assert len(self.axi_hp_slaves) < 4
n = len(self.axi_hp_slaves)
axi_hpn = axi.AXIInterface(data_width=64, address_width=32, id_width=6)
axi_hpn = axi.AXIInterface(
data_width = 64,
address_width = 32,
id_width = 6,
version = "axi3",
clock_domain = clock_domain
)
self.axi_hp_slaves.append(axi_hpn)
self.cpu_params.update({
# AXI HP0 clk.
f"i_S_AXI_HP{n}_ACLK" : ClockSignal("ps7"),
f"i_S_AXI_HP{n}_ACLK" : ClockSignal(clock_domain),
# AXI HP0 aw.
f"i_S_AXI_HP{n}_AWVALID" : axi_hpn.aw.valid,
@ -392,10 +433,59 @@ class Zynq7000(CPU):
})
return axi_hpn
"""
Enable CANx peripheral. Peripheral may be optionally set
Attributes
==========
n: int
CAN id (0, 1)
pads:
Physicals pads (tx and rx)
ext_clk: int or None
When unset/None CAN is clocked by internal clock (IO PLL).
value must be 0 <= ext_clk < 54.
ext_clk_freq: float
when ext_clk is set, external clock frequency (Hz)
"""
def add_can(self, n, pads, ext_clk=None, ext_clk_freq=None):
assert n < 2 and not n in self.can_use
assert ext_clk is None or (ext_clk < 54 and ext_clk is not None)
assert pads is not None
# Mark as used
self.can_use.append(n)
# PS7 configuration.
self.add_ps7_config({
f"PCW_CAN{n}_PERIPHERAL_ENABLE": 1,
f"PCW_CAN{n}_CAN{n}_IO": "EMIO",
f"PCW_CAN{n}_GRP_CLK_ENABLE": {True: 0, False: 1}[ext_clk == None],
})
if ext_clk:
self.add_ps7_config({
f"PCW_CAN{n}_GRP_CLK_IO" : f"MIO {ext_clk}",
f"PCW_CAN{n}_PERIPHERAL_FREQMHZ" : int(clk_freq / 1e6),
})
# PS7 connections.
self.cpu_params.update({
f"i_CAN{n}_PHY_RX": pads.rx,
f"o_CAN{n}_PHY_TX": pads.tx,
})
def do_finalize(self):
if self.ps7_name is None:
raise Exception("PS7 must be set with set_ps7 or set_ps7_xci methods.")
if len(self.ps7_tcl):
# Add configs to PS7.
if len(self.config):
self.ps7_tcl.append("set_property -dict [list \\")
for config, value in self.config.items():
self.ps7_tcl.append("CONFIG.{} {} \\".format(
config, '{{' + str(value) + '}}'))
self.ps7_tcl.append(f"] [get_ips {self.ps7_name}]")
self.ps7_tcl += [
f"upgrade_ip [get_ips {self.ps7_name}]",
f"generate_target all [get_ips {self.ps7_name}]",

View File

@ -4,6 +4,8 @@
# Copyright (c) 2022 Ilia Sergachev <ilia.sergachev@protonmail.ch>
# SPDX-License-Identifier: BSD-2-Clause
import os
from migen import *
from litex.gen import *
@ -37,6 +39,7 @@ class ZynqMP(CPU):
def mem_map(self):
return {
"sram": 0x0000_0000, # DDR low in fact
"csr": 0xA000_0000, # ZynqMP M_AXI_HPM0_FPD (HPM0)
"rom": 0xc000_0000, # Quad SPI memory
}
@ -47,40 +50,352 @@ class ZynqMP(CPU):
self.periph_buses = [] # Peripheral buses (Connected to main SoC's bus).
self.memory_buses = [] # Memory buses (Connected directly to LiteDRAM).
self.axi_gp_masters = [None] * 3 # General Purpose AXI Masters.
self.gem_mac = [] # GEM MAC reserved ports.
self.i2c_use = [] # I2c reserved ports.
self.uart_use = [] # UART reserved ports.
self.can_use = [] # CAN reserved/used ports.
# [ 7: 0]: PL_PS_Group0 [128:121]
# [15: 8]: PL_PS_Group1 [143:136]
self.interrupt = Signal(16)
self.cd_ps = ClockDomain()
self.ps_name = "ps"
self.ps_tcl = []
self.config = {'PSU__FPGA_PL0_ENABLE': 1} # enable pl_clk0
self.config = {
'PSU__FPGA_PL0_ENABLE' : 1, # enable pl_clk0
'PSU__USE__IRQ0' : 1, # enable PL_PS_Group0
'PSU__NUM_F2P0__INTR__INPUTS': 8,
'PSU__USE__IRQ1' : 1, # enable PL_PS_Group1
'PSU__NUM_F2P1__INTR__INPUTS': 8,
'PSU__USE__M_AXI_GP1' : 0,
}
rst_n = Signal()
self.cpu_params = dict(
o_pl_clk0=ClockSignal("ps"),
o_pl_resetn0=rst_n
o_pl_resetn0=rst_n,
i_pl_ps_irq0 = self.interrupt[0: 8],
i_pl_ps_irq1 = self.interrupt[8:16]
)
# Use GP0 as peripheral bus / CSR
self.pbus = self.add_axi_gp_master(0)
self.periph_buses.append(self.pbus)
self.comb += ResetSignal("ps").eq(~rst_n)
self.ps_tcl.append(f"set ps [create_ip -vendor xilinx.com -name zynq_ultra_ps_e -module_name {self.ps_name}]")
def set_preset(self, preset):
preset = os.path.abspath(preset)
self.ps_tcl.append(f"source {preset}")
self.ps_tcl.append("set psu_cfg [apply_preset IPINST]")
self.ps_tcl.append("set_property -dict $psu_cfg [get_ips {}]".format(self.ps_name))
def add_axi_gp_master(self, n=0, data_width=32):
assert n < 3 and self.axi_gp_masters[n] is None
assert data_width in [32, 64, 128]
axi_gpn = axi.AXIInterface(data_width=data_width, address_width=32, id_width=16)
self.config[f'PSU__USE__M_AXI_GP{n}'] = 1
xpd = {0 : "fpd", 1 : "fpd", 2 : "lpd"}[n]
self.config[f'PSU__USE__M_AXI_GP{n}'] = 1
self.config[f'PSU__MAXIGP{n}__DATA_WIDTH'] = data_width
self.axi_gp_masters.append(axi_gpn)
xpd = {0 : "fpd", 1 : "fpd", 2 : "lpd"}[n]
self.cpu_params[f"i_maxihpm0_{xpd}_aclk"] = ClockSignal("ps")
layout = axi_gpn.layout_flat()
dir_map = {DIR_M_TO_S: 'o', DIR_S_TO_M: 'i'}
for group, signal, direction in layout:
sig_name = group + signal
if sig_name in ['bfirst', 'blast', 'rfirst', 'arfirst', 'arlast', 'awfirst', 'awlast', 'wfirst', 'wid']:
continue
direction = dir_map[direction]
self.cpu_params[f'{direction}_maxigp{n}_{group}{signal}'] = getattr(getattr(axi_gpn, group), signal)
self.cpu_params.update({
# AXI GPx clk.
f"i_maxihpm0_{xpd}_aclk" : ClockSignal("ps"),
# AXI GPx aw.
f"o_maxigp{n}_awid" : axi_gpn.aw.id,
f"o_maxigp{n}_awaddr" : axi_gpn.aw.addr,
f"o_maxigp{n}_awlen" : axi_gpn.aw.len,
f"o_maxigp{n}_awsize" : axi_gpn.aw.size,
f"o_maxigp{n}_awburst" : axi_gpn.aw.burst,
f"o_maxigp{n}_awlock" : axi_gpn.aw.lock,
f"o_maxigp{n}_awcache" : axi_gpn.aw.cache,
f"o_maxigp{n}_awprot" : axi_gpn.aw.prot,
f"o_maxigp{n}_awvalid" : axi_gpn.aw.valid,
f"o_maxigp{n}_awuser" : axi_gpn.aw.user,
f"i_maxigp{n}_awready" : axi_gpn.aw.ready,
f"o_maxigp{n}_awqos" : axi_gpn.aw.qos,
# AXI GPx w.
f"o_maxigp{n}_wdata" : axi_gpn.w.data,
f"o_maxigp{n}_wstrb" : axi_gpn.w.strb,
f"o_maxigp{n}_wlast" : axi_gpn.w.last,
f"o_maxigp{n}_wvalid" : axi_gpn.w.valid,
f"i_maxigp{n}_wready" : axi_gpn.w.ready,
# AXI GPx b.
f"i_maxigp{n}_bid" : axi_gpn.b.id,
f"i_maxigp{n}_bresp" : axi_gpn.b.resp,
f"i_maxigp{n}_bvalid" : axi_gpn.b.valid,
f"o_maxigp{n}_bready" : axi_gpn.b.ready,
# AXI GPx ar.
f"o_maxigp{n}_arid" : axi_gpn.ar.id,
f"o_maxigp{n}_araddr" : axi_gpn.ar.addr,
f"o_maxigp{n}_arlen" : axi_gpn.ar.len,
f"o_maxigp{n}_arsize" : axi_gpn.ar.size,
f"o_maxigp{n}_arburst" : axi_gpn.ar.burst,
f"o_maxigp{n}_arlock" : axi_gpn.ar.lock,
f"o_maxigp{n}_arcache" : axi_gpn.ar.cache,
f"o_maxigp{n}_arprot" : axi_gpn.ar.prot,
f"o_maxigp{n}_arvalid" : axi_gpn.ar.valid,
f"o_maxigp{n}_aruser" : axi_gpn.ar.user,
f"i_maxigp{n}_arready" : axi_gpn.ar.ready,
f"o_maxigp{n}_arqos" : axi_gpn.ar.qos,
# AXI GPx r.
f"i_maxigp{n}_rid" : axi_gpn.r.id,
f"i_maxigp{n}_rdata" : axi_gpn.r.data,
f"i_maxigp{n}_rresp" : axi_gpn.r.resp,
f"i_maxigp{n}_rlast" : axi_gpn.r.last,
f"i_maxigp{n}_rvalid" : axi_gpn.r.valid,
f"o_maxigp{n}_rready" : axi_gpn.r.ready,
})
return axi_gpn
def add_ethernet(self, n=0, pads=None, if_type="gmii"):
assert n < 3 and not n in self.gem_mac
assert pads is not None
# psu configuration
self.config[f"PSU__ENET{n}__PERIPHERAL__ENABLE"] = 1
self.config[f"PSU__ENET{n}__PERIPHERAL__IO"] = "EMIO"
self.config[f"PSU__ENET{n}__GRP_MDIO__ENABLE"] = 1
self.config[f"PSU__ENET{n}__GRP_MDIO__IO"] = "EMIO"
# psu GMII connection
gmii_rx_clk = Signal()
speed_mode = Signal(3)
gmii_crs = Signal()
gmii_col = Signal()
gmii_rxd = Signal(8)
gmii_rx_er = Signal()
gmii_rx_dv = Signal()
gmii_tx_clk = Signal()
gmii_txd = Signal(8)
gmii_tx_en = Signal()
gmii_tx_er = Signal()
self.cpu_params.update({
f"i_emio_enet{n}_gmii_rx_clk" : gmii_rx_clk,
f"o_emio_enet{n}_speed_mode" : speed_mode,
f"i_emio_enet{n}_gmii_crs" : gmii_crs,
f"i_emio_enet{n}_gmii_col" : gmii_col,
f"i_emio_enet{n}_gmii_rxd" : gmii_rxd,
f"i_emio_enet{n}_gmii_rx_er" : gmii_rx_er,
f"i_emio_enet{n}_gmii_rx_dv" : gmii_rx_dv,
f"i_emio_enet{n}_gmii_tx_clk" : gmii_tx_clk,
f"o_emio_enet{n}_gmii_txd" : gmii_txd,
f"o_emio_enet{n}_gmii_tx_en" : gmii_tx_en,
f"o_emio_enet{n}_gmii_tx_er" : gmii_tx_er,
})
# psu MDIO connection
mdio_mdc = Signal()
mdio_i = Signal()
mdio_o = Signal()
mdio_t = Signal()
self.cpu_params.update({
f"o_emio_enet{n}_mdio_mdc" : mdio_mdc,
f"i_emio_enet{n}_mdio_i" : mdio_i,
f"o_emio_enet{n}_mdio_o" : mdio_o,
f"o_emio_enet{n}_mdio_t" : mdio_t,
})
if if_type == "gmii":
self.comb += pads.mdc.eq(mdio_mdc)
self.specials += Instance("IOBUF",
i_I = mdio_o,
o_O = mdio_i,
i_T = mdio_t,
io_IO = pads.mdio
)
else:
phys_mdio_i = Signal()
phys_mdio_o = Signal()
phys_mdio_t = Signal()
self.specials += Instance("IOBUF",
i_I = phys_mdio_o,
o_O = phys_mdio_i,
i_T = phys_mdio_t,
io_IO = pads.mdio
)
self.comb += pads.rst_n.eq(~ResetSignal("sys"))
mac_params = dict(
i_tx_reset = ResetSignal("sys"),
i_rx_reset = ResetSignal("sys"),
i_clkin = ClockSignal("rgmii"),
# PS GEM: MDIO
i_mdio_gem_mdc = mdio_mdc,
o_mdio_gem_i = mdio_i,
i_mdio_gem_o = mdio_o,
i_mdio_gem_t = mdio_t,
# PS GEM: GMII
o_gmii_tx_clk = gmii_tx_clk,
i_gmii_tx_en = gmii_tx_en,
i_gmii_txd = gmii_txd,
i_gmii_tx_er = gmii_tx_er,
o_gmii_crs = gmii_crs,
o_gmii_col = gmii_col,
o_gmii_rx_clk = gmii_rx_clk,
o_gmii_rx_dv = gmii_rx_dv,
o_gmii_rxd = gmii_rxd,
o_gmii_rx_er = gmii_rx_er,
# PHY: RGMII
o_rgmii_txd = pads.tx_data,
o_rgmii_tx_ctl = pads.tx_ctl,
o_rgmii_txc = pads.txc,
i_rgmii_rxd = pads.rx_data,
i_rgmii_rx_ctl = pads.rx_ctl,
i_rgmii_rxc = pads.rxc,
# PHY: MDIO
o_mdio_phy_mdc = pads.mdc,
i_mdio_phy_i = phys_mdio_i,
o_mdio_phy_o = phys_mdio_o,
o_mdio_phy_t = phys_mdio_t,
o_ref_clk_out = Open(),
o_mmcm_locked_out = Open(),
o_gmii_clk_125m_out = Open(),
o_gmii_clk_25m_out = Open(),
o_gmii_clk_2_5m_out = Open(),
o_link_status = Open(),
o_clock_speed = Open(2),
o_duplex_status = Open(),
o_speed_mode = Open(2),
)
self.specials += Instance(f"gem{n}", **mac_params)
self.gem_mac.append(n)
def add_i2c(self, n, pads):
assert n < 2 and not n in self.i2c_use
assert pads is not None
# PSU configuration.
self.config[f"PSU__I2C{n}__PERIPHERAL__ENABLE"] = 1
self.config[f"PSU__I2C{n}__PERIPHERAL__IO"] = "EMIO"
# Signals.
scl_i = Signal()
scl_o = Signal()
scl_t = Signal()
sda_i = Signal()
sda_o = Signal()
sda_t = Signal()
# PSU connections.
self.specials += [
Instance("IOBUF",
i_I = sda_o,
o_O = sda_i,
i_T = sda_t,
io_IO = pads.sda
),
Instance("IOBUF",
i_I = scl_o,
o_O = scl_i,
i_T = scl_t,
io_IO = pads.scl
),
]
self.cpu_params.update({
f"i_emio_i2c{n}_scl_i" : scl_i,
f"o_emio_i2c{n}_scl_o" : scl_o,
f"o_emio_i2c{n}_scl_t" : scl_t,
f"i_emio_i2c{n}_sda_i" : sda_i,
f"o_emio_i2c{n}_sda_o" : sda_o,
f"o_emio_i2c{n}_sda_t" : sda_t,
})
def add_uart(self, n, pads):
assert n < 2 and not n in self.uart_use
assert pads is not None
self.config[f"PSU__UART{n}__PERIPHERAL__ENABLE"] = 1
self.config[f"PSU__UART{n}__PERIPHERAL__IO"] = "EMIO"
self.cpu_params.update({
f"i_emio_uart{n}_rxd" : pads.rx,
f"o_emio_uart{n}_txd" : pads.tx,
})
def add_gpios(self, pads):
assert pads is not None
# Parameters.
pads_len = len(pads)
# PSU configuration.
self.config["PSU__GPIO_EMIO__PERIPHERAL__ENABLE"] = 1
self.config["PSU__GPIO_EMIO__PERIPHERAL__IO"] = len(pads)
# Signals.
gpio_i = Signal(pads_len)
gpio_o = Signal(pads_len)
gpio_t = Signal(pads_len)
# PSU connections.
for i in range(pads_len):
self.specials += Instance("IOBUF",
i_I = gpio_o[i],
o_O = gpio_i[i],
i_T = gpio_t[i],
io_IO = pads[i]
)
self.cpu_params.update({
"i_emio_gpio_i" : gpio_i,
"o_emio_gpio_o" : gpio_o,
"o_emio_gpio_t" : gpio_t,
})
"""
Enable CANx peripheral. Peripheral may be optionally set
Attributes
==========
n: int
CAN id (0, 1)
pads:
Physicals pads (tx and rx)
ext_clk: int or None
When unset/None CAN is clocked by internal clock (IO PLL).
value must be 0 <= ext_clk < 54.
ext_clk_freq: float
when ext_clk is set, external clock frequency (Hz)
"""
def add_can(self, n, pads, ext_clk=None, ext_clk_freq=None):
assert n < 2 and not n in self.can_use
assert ext_clk is None or (ext_clk < 54 and ext_clk is not None)
assert pads is not None
# Mark as used
self.can_use.append(n)
# PSU configuration.
self.config[f"PSU__CAN{n}__PERIPHERAL__ENABLE"] = 1
self.config[f"PSU__CAN{n}__PERIPHERAL__IO"] = "EMIO"
self.config[f"PSU__CAN{n}__GRP_CLK__ENABLE"] = {True: 0, False: 1}[ext_clk == None]
if ext_clk:
self.config[f"PSU__CAN{n}__GRP_CLK__IO"] = f"MIO {ext_clk}"
self.config[f"PSU__CRL_APB__CAN{n}_REF_CTRL__FREQMHZ"] = int(clk_freq / 1e6)
# PS7 connections.
self.cpu_params.update({
f"i_emio_can{n}_phy_rx": pads.rx,
f"o_emio_can{n}_phy_tx": pads.tx,
})
def do_finalize(self):
if len(self.ps_tcl):
self.ps_tcl.append("set_property -dict [list \\")
@ -94,3 +409,23 @@ class ZynqMP(CPU):
]
self.platform.toolchain.pre_synthesis_commands += self.ps_tcl
self.specials += Instance(self.ps_name, **self.cpu_params)
# ethernet
if len(self.gem_mac):
mac_tcl = []
for i in self.gem_mac:
mac_tcl.append(f"set gem{i} [create_ip -vendor xilinx.com -name gmii_to_rgmii -module_name gem{i}]")
mac_tcl.append("set_property -dict [ list \\")
# FIXME: when more this sequence differs for the first and others
mac_tcl.append("CONFIG.{} {} \\".format("C_EXTERNAL_CLOCK", '{{false}}'))
mac_tcl.append("CONFIG.{} {} \\".format("C_USE_IDELAY_CTRL", '{{true}}'))
mac_tcl.append("CONFIG.{} {} \\".format("C_PHYADDR", '{{' + str(8 + i) + '}}'))
mac_tcl.append("CONFIG.{} {} \\".format("RGMII_TXC_SKEW", '{{' + str(0) + '}}'))
mac_tcl.append("CONFIG.{} {} \\".format("SupportLevel", '{{Include_Shared_Logic_in_Core}}'))
mac_tcl += [
f"] [get_ips gem{i}]",
f"generate_target all [get_ips gem{i}]",
f"synth_ip [get_ips gem{i}]"
]
self.platform.toolchain.pre_synthesis_commands += mac_tcl

View File

@ -73,13 +73,13 @@ class WishboneDMAReader(LiteXModule):
if with_csr:
self.add_csr()
def add_csr(self, default_base=0, default_length=0, default_enable=0, default_loop=0):
self._base = CSRStorage(64, reset=default_base)
self._length = CSRStorage(32, reset=default_length)
self._enable = CSRStorage(reset=default_enable)
self._done = CSRStatus()
self._loop = CSRStorage(reset=default_loop)
self._offset = CSRStatus(32)
def add_ctrl(self, default_base=0, default_length=0, default_enable=0, default_loop=0):
self.base = Signal(64, reset=default_base)
self.length = Signal(32, reset=default_length)
self.enable = Signal(reset=default_enable)
self.done = Signal()
self.loop = Signal(reset=default_loop)
self.offset = Signal(32)
# # #
@ -87,13 +87,13 @@ class WishboneDMAReader(LiteXModule):
base = Signal(self.bus.adr_width)
offset = Signal(self.bus.adr_width)
length = Signal(self.bus.adr_width)
self.comb += base.eq(self._base.storage[shift:])
self.comb += length.eq(self._length.storage[shift:])
self.comb += base.eq(self.base[shift:])
self.comb += length.eq(self.length[shift:])
self.comb += self._offset.status.eq(offset)
self.comb += self.offset.eq(offset)
self.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE"))
self.comb += fsm.reset.eq(~self._enable.storage)
self.comb += fsm.reset.eq(~self.enable)
fsm.act("IDLE",
NextValue(offset, 0),
NextState("RUN"),
@ -105,7 +105,7 @@ class WishboneDMAReader(LiteXModule):
If(self.sink.ready,
NextValue(offset, offset + 1),
If(self.sink.last,
If(self._loop.storage,
If(self.loop,
NextValue(offset, 0)
).Else(
NextState("DONE")
@ -113,7 +113,30 @@ class WishboneDMAReader(LiteXModule):
)
)
)
fsm.act("DONE", self._done.status.eq(1))
fsm.act("DONE", self.done.eq(1))
def add_csr(self, default_base=0, default_length=0, default_enable=0, default_loop=0):
if not hasattr(self, "base"):
self.add_ctrl()
self._base = CSRStorage(64, reset=default_base)
self._length = CSRStorage(32, reset=default_length)
self._enable = CSRStorage(reset=default_enable)
self._done = CSRStatus()
self._loop = CSRStorage(reset=default_loop)
self._offset = CSRStatus(32)
# # #
self.comb += [
# Control.
self.base.eq(self._base.storage),
self.length.eq(self._length.storage),
self.enable.eq(self._enable.storage),
self.loop.eq(self._loop.storage),
# Status.
self._done.status.eq(self.done),
self._offset.status.eq(self.offset),
]
# WishboneDMAWriter --------------------------------------------------------------------------------
@ -153,16 +176,16 @@ class WishboneDMAWriter(LiteXModule):
if with_csr:
self.add_csr()
def add_csr(self, default_base=0, default_length=0, default_enable=0, default_loop=0, ready_on_idle=1):
def add_ctrl(self, default_base=0, default_length=0, default_enable=0, default_loop=0, ready_on_idle=1):
self._sink = self.sink
self.sink = stream.Endpoint([("data", self.bus.data_width)])
self._base = CSRStorage(64, reset=default_base)
self._length = CSRStorage(32, reset=default_length)
self._enable = CSRStorage(reset=default_enable)
self._done = CSRStatus()
self._loop = CSRStorage(reset=default_loop)
self._offset = CSRStatus(32)
self.base = Signal(64, reset=default_base)
self.length = Signal(32, reset=default_length)
self.enable = Signal(reset=default_enable)
self.done = Signal()
self.loop = Signal(reset=default_loop)
self.offset = Signal(32)
# # #
@ -170,13 +193,13 @@ class WishboneDMAWriter(LiteXModule):
base = Signal(self.bus.adr_width)
offset = Signal(self.bus.adr_width)
length = Signal(self.bus.adr_width)
self.comb += base.eq(self._base.storage[shift:])
self.comb += length.eq(self._length.storage[shift:])
self.comb += base.eq(self.base[shift:])
self.comb += length.eq(self.length[shift:])
self.comb += self._offset.status.eq(offset)
self.comb += self.offset.eq(offset)
self.fsm = fsm = ResetInserter()(FSM(reset_state="IDLE"))
self.comb += fsm.reset.eq(~self._enable.storage)
self.comb += fsm.reset.eq(~self.enable)
fsm.act("IDLE",
self.sink.ready.eq(ready_on_idle),
NextValue(offset, 0),
@ -191,7 +214,7 @@ class WishboneDMAWriter(LiteXModule):
If(self.sink.valid & self.sink.ready,
NextValue(offset, offset + 1),
If(self._sink.last,
If(self._loop.storage,
If(self.loop,
NextValue(offset, 0)
).Else(
NextState("DONE")
@ -199,4 +222,27 @@ class WishboneDMAWriter(LiteXModule):
)
)
)
fsm.act("DONE", self._done.status.eq(1))
fsm.act("DONE", self.done.eq(1))
def add_csr(self, default_base=0, default_length=0, default_enable=0, default_loop=0):
if not hasattr(self, "base"):
self.add_ctrl()
self._base = CSRStorage(64, reset=default_base)
self._length = CSRStorage(32, reset=default_length)
self._enable = CSRStorage(reset=default_enable)
self._done = CSRStatus()
self._loop = CSRStorage(reset=default_loop)
self._offset = CSRStatus(32)
# # #
self.comb += [
# Control.
self.base.eq(self._base.storage),
self.length.eq(self._length.storage),
self.enable.eq(self._enable.storage),
self.loop.eq(self._loop.storage),
# Status.
self._done.status.eq(self.done),
self._offset.status.eq(self.offset),
]

View File

@ -1,16 +1,22 @@
#
# This file is part of LiteHyperBus
# This file is part of LiteX.
#
# Copyright (c) 2019-2022 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2019-2024 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2019 Antti Lukats <antti.lukats@gmail.com>
# Copyright (c) 2021 Franck Jullien <franck.jullien@collshade.fr>
# SPDX-License-Identifier: BSD-2-Clause
from migen import *
from migen.fhdl.specials import Tristate
from litex.build.io import SDRTristate
from litex.gen import *
from litex.gen.genlib.misc import WaitTimer
from litex.soc.interconnect.csr import *
from litex.soc.interconnect import stream
from litex.build.io import DifferentialOutput
from litex.soc.interconnect import wishbone
@ -21,143 +27,284 @@ class HyperRAM(LiteXModule):
tCSM = 4e-6
"""HyperRAM
Provides a very simple/minimal HyperRAM core that should work with all FPGA/HyperRam chips:
- FPGA vendor agnostic.
- no setup/chip configuration (use default latency).
Provides a very simple/minimal HyperRAM core with a Wishbone Interface that can work with all
FPGA/HyperRam chips:
- Vendor agnostic.
- Fixed/Variable latency.
- Latency/Registers (re-)configuration.
This core favors portability and ease of use over performance.
"""
def __init__(self, pads, latency=6, sys_clk_freq=None):
Parameters:
pads (Record) : Interface to the HyperRAM connection pads.
latency (int, optional) : Initial latency setting, defaults to 6.
latency_mode (str, optional) : Specifies the latency mode ('fixed' or 'variable'), defaults to 'variable'.
sys_clk_freq (float, optional) : System clock frequency in Hz.
with_csr (bool, optional) : Enables CSR interface for Latency/Registers configuration, defaults to True.
Attributes:
pads (Record) : Platform pads of HyperRAM.
bus (wishbone.Interface) : Wishbone Interface.
"""
def __init__(self, pads, latency=6, latency_mode="variable", sys_clk_freq=10e6, clk_ratio="4:1", with_csr=True):
self.pads = pads
self.bus = bus = wishbone.Interface(data_width=32, address_width=32, addressing="word")
# # #
# Parameters.
# -----------
dw = len(pads.dq) if not hasattr(pads.dq, "oe") else len(pads.dq.o)
assert dw in [8, 16]
assert latency_mode in ["fixed", "variable"]
assert clk_ratio in [
"4:1", # HyperRAM Clk = Sys Clk/4.
"2:1", # HyperRAM Clk = Sys Clk/2.
]
self.cd_io = cd_io = {
"4:1": "sys",
"2:1": "sys2x"
}[clk_ratio]
self.sync_io = sync_io = getattr(self.sync, cd_io)
# Config/Reg Interface.
# ---------------------
self.conf_rst = Signal()
self.conf_latency = Signal(8, reset=latency)
self.stat_latency_mode = Signal(reset={"fixed": 0, "variable": 1}[latency_mode])
self.reg_write = Signal()
self.reg_read = Signal()
self.reg_addr = Signal(2)
self.reg_write_done = Signal()
self.reg_read_done = Signal()
self.reg_write_data = Signal(16)
self.reg_read_data = Signal(16)
if with_csr:
self.add_csr(default_latency=latency)
# Internal Signals.
# -----------------
clk = Signal()
clk_phase = Signal(2)
cs = Signal()
ca = Signal(48)
ca_active = Signal()
ca_oe = Signal()
sr = Signal(48)
sr_new = Signal(48)
dq = self.add_tristate(pads.dq) if not hasattr(pads.dq, "oe") else pads.dq
rwds = self.add_tristate(pads.rwds) if not hasattr(pads.rwds, "oe") else pads.rwds
dw = len(pads.dq) if not hasattr(pads.dq, "oe") else len(pads.dq.o)
sr_next = Signal(48)
dq_o = Signal(dw)
dq_oe = Signal()
dq_i = Signal(dw)
rwds_o = Signal(dw//8)
rwds_oe = Signal()
rwds_i = Signal(dw//8)
assert dw in [8, 16]
# Tristates.
# ----------
dq = self.add_tristate(pads.dq, register=False) if not hasattr(pads.dq, "oe") else pads.dq
rwds = self.add_tristate(pads.rwds, register=False) if not hasattr(pads.rwds, "oe") else pads.rwds
self.comb += [ # FIXME: Try to move to sync to allow switching to SDRTristate.
# DQ.
dq.o.eq( dq_o),
dq.oe.eq(dq_oe),
# RWDS.
rwds.o.eq( rwds_o),
rwds.oe.eq(rwds_oe),
]
self.sync_io += [
# DQ.
dq_i.eq(dq.i),
# RWDS.
rwds_i.eq(rwds.i)
]
# Drive Control Signals --------------------------------------------------------------------
# Rst.
if hasattr(pads, "rst_n"):
self.comb += pads.rst_n.eq(1)
self.sync_io += pads.rst_n.eq(1 & ~self.conf_rst)
# CSn.
self.comb += pads.cs_n[0].eq(~cs)
assert len(pads.cs_n) <= 2
if len(pads.cs_n) == 2:
self.comb += pads.cs_n[1].eq(1)
pads.cs_n.reset = 2**len(pads.cs_n) - 1
self.sync_io += pads.cs_n[0].eq(~cs) # Only supporting 1 CS.
# Clk.
pads_clk = Signal()
self.sync_io += pads_clk.eq(clk)
if hasattr(pads, "clk"):
self.comb += pads.clk.eq(clk)
# Single Ended Clk.
self.comb += pads.clk.eq(pads_clk)
elif hasattr(pads, "clk_p"):
# Differential Clk.
self.specials += DifferentialOutput(pads_clk, pads.clk_p, pads.clk_n)
else:
self.specials += DifferentialOutput(clk, pads.clk_p, pads.clk_n)
raise ValueError
# Burst Timer ------------------------------------------------------------------------------
sys_clk_freq = 10e6 if sys_clk_freq is None else sys_clk_freq
burst_timer = WaitTimer(sys_clk_freq*self.tCSM)
self.burst_timer = burst_timer
self.burst_timer = burst_timer = WaitTimer(sys_clk_freq * self.tCSM)
# Clock Generation (sys_clk/4) -------------------------------------------------------------
self.sync += clk_phase.eq(clk_phase + 1)
cases = {}
cases[1] = clk.eq(cs) # Set pads clk on 90° (if cs is set)
cases[3] = clk.eq(0) # Clear pads clk on 270°
self.sync += Case(clk_phase, cases)
# Clk Generation ---------------------------------------------------------------------------
self.sync_io += [
clk_phase.eq(0b00),
If(cs,
clk_phase.eq(clk_phase + 1)
)
]
cases = {
0b00 : clk.eq(0), # 0°
0b01 : clk.eq(cs), # 90° / Set Clk.
0b10 : clk.eq(cs), # 180°
0b11 : clk.eq(0), # 270° / Clr Clk.
}
if clk_ratio in ["4:1"]:
self.comb += Case(clk_phase, cases)
if clk_ratio in ["2:1"]:
self.sync_io += Case(clk_phase, cases)
# Data Shift-In Register -------------------------------------------------------------------
dqi = Signal(dw)
self.sync += dqi.eq(dq.i) # Sample on 90° and 270°
self.comb += [
sr_new.eq(Cat(dqi, sr[:-dw])),
If(ca_active,
sr_new.eq(Cat(dqi[:8], sr[:-8])) # Only 8-bit during Command/Address.
# Command/Address: On 8-bit, so 8-bit shift and no input.
If(ca_oe,
sr_next[8:].eq(sr),
# Data: On dw-bit, so dw-bit shift.
).Else(
sr_next[:dw].eq(dq_i),
sr_next[dw:].eq(sr),
)
]
self.sync += If(clk_phase[0] == 0, sr.eq(sr_new)) # Shift on 0° and 180°
if clk_ratio in ["4:1"]:
self.sync += If(clk_phase[0] == 0, sr.eq(sr_next))
if clk_ratio in ["2:1"]:
self.sync += sr.eq(sr_next)
# Data Shift-Out Register ------------------------------------------------------------------
self.comb += bus.dat_r.eq(sr_next)
self.comb += [
bus.dat_r.eq(sr_new),
If(dq.oe,
dq.o.eq(sr[-dw:]),
If(ca_active,
dq.o.eq(sr[-8:]) # Only 8-bit during Command/Address.
)
# Command/Address: 8-bit.
If(ca_oe,
dq_o.eq(sr[-8:])
# Data: dw-bit.
).Else(
dq_o.eq(sr[-dw:])
)
]
# Register Access/Buffer -------------------------------------------------------------------
reg_write_req = Signal()
reg_read_req = Signal()
self.reg_buf = reg_buf = stream.SyncFIFO(
layout = [("write", 1), ("read", 1), ("addr", 4), ("data", 16)],
depth = 4,
)
reg_ep = reg_buf.source
self.comb += [
reg_buf.sink.valid.eq(self.reg_write | self.reg_read),
reg_buf.sink.write.eq(self.reg_write),
reg_buf.sink.read.eq(self.reg_read),
reg_buf.sink.addr.eq(self.reg_addr),
reg_buf.sink.data.eq(self.reg_write_data),
reg_write_req.eq(reg_ep.valid & reg_ep.write),
reg_read_req.eq( reg_ep.valid & reg_ep.read),
]
self.sync += If(reg_buf.sink.valid,
self.reg_write_done.eq(0),
self.reg_read_done.eq(0),
)
# Command generation -----------------------------------------------------------------------
ashift = {8:1, 16:0}[dw]
self.comb += [
ca[47].eq(~bus.we), # R/W#
ca[45].eq(1), # Burst Type (Linear)
ca[16:45].eq(bus.adr[3-ashift:]), # Row & Upper Column Address
ca[ashift:3].eq(bus.adr), # Lower Column Address
# Register Command Generation.
If(reg_write_req | reg_read_req,
ca[47].eq(reg_ep.read), # R/W#
ca[46].eq(1), # Register Space.
ca[45].eq(1), # Burst Type (Linear)
Case(reg_ep.addr, {
0 : ca[0:40].eq(0x00_00_00_00_00), # Identification Register 0 (Read Only).
1 : ca[0:40].eq(0x00_00_00_00_01), # Identification Register 1 (Read Only).
2 : ca[0:40].eq(0x00_01_00_00_00), # Configuration Register 0.
3 : ca[0:40].eq(0x00_01_00_00_01), # Configuration Register 1.
}),
# Wishbone Command Generation.
).Else(
ca[47].eq(~bus.we), # R/W#
ca[46].eq(0), # Memory Space.
ca[45].eq(1), # Burst Type (Linear)
ca[16:45].eq(bus.adr[3-ashift:]), # Row & Upper Column Address
ca[ashift:3].eq(bus.adr), # Lower Column Address
)
]
# Latency count starts from the middle of the command (thus the -4). In fixed latency mode
# (default), latency is 2 x Latency count. We have 4 x sys_clk per RAM clock:
latency_cycles = (latency * 2 * 4) - 4
# Bus Latch --------------------------------------------------------------------------------
bus_adr = Signal(32)
bus_we = Signal()
bus_sel = Signal(4)
bus_latch = Signal()
self.sync += If(bus_latch,
If(bus.we,
sr.eq(Cat(Signal(16), bus.dat_w)),
),
If(bus.we, sr.eq(Cat(Signal(16), bus.dat_w))),
bus_we.eq(bus.we),
bus_sel.eq(bus.sel),
bus_adr.eq(bus.adr)
)
# FSM (Sequencer) --------------------------------------------------------------------------
cycles = Signal(8)
first = Signal()
cycles = Signal(8)
first = Signal()
refresh = Signal()
self.fsm = fsm = FSM(reset_state="IDLE")
fsm.act("IDLE",
NextValue(first, 1),
If(bus.cyc & bus.stb,
If(clk_phase == 0,
NextValue(sr, ca),
NextState("SEND-COMMAND-ADDRESS")
)
If((bus.cyc & bus.stb) | reg_write_req | reg_read_req,
NextValue(sr, ca),
NextState("SEND-COMMAND-ADDRESS")
)
)
fsm.act("SEND-COMMAND-ADDRESS",
# Set CSn.
cs.eq(1),
# Send Command on DQ.
ca_active.eq(1),
dq.oe.eq(1),
# Wait for 6*2 cycles...
ca_oe.eq(1),
dq_oe.eq(1),
# Wait for 6*2 cycles.
If(cycles == (6*2 - 1),
NextState("WAIT-LATENCY")
If(reg_write_req,
NextValue(sr, Cat(Signal(40), self.reg_write_data[8:])),
NextState("REG-WRITE-0")
).Else(
# Sample RWDS to know if 1X/2X Latency should be used (Refresh).
NextValue(refresh, rwds_i | (latency_mode in ["fixed"])),
NextState("WAIT-LATENCY")
)
)
)
fsm.act("REG-WRITE-0",
# Send Reg on DQ.
ca_oe.eq(1),
dq_oe.eq(1),
# Wait for 2 cycles.
If(cycles == (2 - 1),
NextValue(sr, Cat(Signal(40), self.reg_write_data[:8])),
NextState("REG-WRITE-1")
)
)
fsm.act("REG-WRITE-1",
# Send Reg on DQ.
ca_oe.eq(1),
dq_oe.eq(1),
# Wait for 2 cycles.
If(cycles == (2 - 1),
reg_ep.ready.eq(1),
NextValue(self.reg_write_done, 1),
NextState("IDLE")
)
)
fsm.act("WAIT-LATENCY",
# Set CSn.
cs.eq(1),
# Wait for Latency cycles...
If(cycles == (latency_cycles - 1),
# Wait for 1X or 2X Latency cycles... (-4 since count start in the middle of the command).
If(((cycles == 2*(self.conf_latency * 4) - 4 - 1) & refresh) | # 2X Latency (No DRAM refresh required).
((cycles == 1*(self.conf_latency * 4) - 4 - 1) & ~refresh) , # 1X Latency ( DRAM refresh required).
# Latch Bus.
bus_latch.eq(1),
# Early Write Ack (to allow bursting).
bus.ack.eq(bus.we),
If(~reg_read_req,
bus.ack.eq(bus.we),
),
NextState("READ-WRITE-DATA0")
)
)
@ -166,15 +313,14 @@ class HyperRAM(LiteXModule):
fsm.act(f"READ-WRITE-DATA{n}",
# Enable Burst Timer.
burst_timer.wait.eq(1),
# Set CSn.
cs.eq(1),
ca_oe.eq(reg_read_req),
# Send Data on DQ/RWDS (for write).
If(bus_we,
dq.oe.eq(1),
rwds.oe.eq(1),
*[rwds.o[dw//8-1-i].eq(~bus_sel[4-1-n*dw//8-i]) for i in range(dw//8)],
dq_oe.eq(1),
rwds_oe.eq(1),
*[rwds_o[dw//8-1-i].eq(~bus_sel[4-1-n*dw//8-i]) for i in range(dw//8)],
),
# Wait for 2 cycles (since HyperRAM's Clk = sys_clk/4).
# Wait for 2 cycles.
If(cycles == (2 - 1),
# Set next default state (with rollover for bursts).
NextState(f"READ-WRITE-DATA{(n + 1)%states}"),
@ -182,7 +328,7 @@ class HyperRAM(LiteXModule):
If(n == (states - 1),
NextValue(first, 0),
# Continue burst when a consecutive access is ready.
If(bus.stb & bus.cyc & (bus.we == bus_we) & (bus.adr == (bus_adr + 1)) & (~burst_timer.done),
If(~reg_read_req & bus.stb & bus.cyc & (bus.we == bus_we) & (bus.adr == (bus_adr + 1)) & (~burst_timer.done),
# Latch Bus.
bus_latch.eq(1),
# Early Write Ack (to allow bursting).
@ -194,15 +340,118 @@ class HyperRAM(LiteXModule):
),
# Read Ack (when dat_r ready).
If((n == 0) & ~first,
bus.ack.eq(~bus_we),
If(reg_read_req,
reg_ep.ready.eq(1),
NextValue(self.reg_read_done, 1),
NextValue(self.reg_read_data, bus.dat_r),
NextState("IDLE"),
).Else(
bus.ack.eq(~bus_we),
)
)
)
)
fsm.finalize()
self.sync += cycles.eq(cycles + 1)
self.sync += If(fsm.next_state != fsm.state, cycles.eq(0))
def add_tristate(self, pad):
t = TSTriple(len(pad))
self.specials += t.get_tristate(pad)
# CS --------------------------------------------------------------------------------------
self.comb += If(~fsm.ongoing("IDLE"), cs.eq(1)) # CS when not in IDLE state.
self.comb += If(fsm.before_leaving("IDLE"), cs.eq(1)) # Early Set.
self.comb += If(fsm.before_entering("IDLE"), cs.eq(0)) # Early Clr.
# FSM Cycles -------------------------------------------------------------------------------
fsm.finalize()
cycles_rst = {
"4:1" : 0,
"2:1" : 1,
}[clk_ratio]
cycles_inc = {
"4:1" : 1,
"2:1" : 2,
}[clk_ratio]
self.sync += cycles.eq(cycles + cycles_inc)
self.sync += If(fsm.next_state != fsm.state, cycles.eq(cycles_rst))
def add_tristate(self, pad, register=False):
class TristatePads:
def __init__(self, width):
self.o = Signal(len(pad))
self.oe = Signal()
self.i = Signal(len(pad))
t = TristatePads(len(pad))
if register:
for n in range(len(pad)):
self.specials += SDRTristate(pad[n],
o = t.o[n],
oe = t.oe,
i = t.i[n],
clk = ClockSignal(cd_io),
)
else:
self.specials += Tristate(pad,
o = t.o,
oe = t.oe,
i = t.i,
)
return t
def add_csr(self, default_latency=6):
# Config/Status Interface.
# ------------------------
self.config = CSRStorage(fields=[
CSRField("rst", offset=0, size=1, pulse=True, description="HyperRAM Rst."),
CSRField("latency", offset=8, size=8, description="HyperRAM Latency (X1).", reset=default_latency),
])
self.comb += [
self.conf_rst.eq( self.config.fields.rst),
self.conf_latency.eq(self.config.fields.latency),
]
self.status = CSRStatus(fields=[
CSRField("latency_mode", offset=0, size=1, values=[
("``0b0``", "Fixed Latency."),
("``0b1``", "Variable Latency."),
]),
CSRField("clk_ratio", offset=1, size=4, values=[
("``4``", "HyperRAM Clk = Sys Clk/4."),
("``2``", "HyperRAM Clk = Sys Clk/2."),
]),
])
self.comb += [
self.status.fields.latency_mode.eq(self.stat_latency_mode),
self.status.fields.clk_ratio.eq({
"sys" : 4,
"sys2x": 2,
}[self.cd_io]),
]
# Reg Interface.
# --------------
self.reg_control = CSRStorage(fields=[
CSRField("write", offset=0, size=1, pulse=True, description="Issue Register Write."),
CSRField("read", offset=1, size=1, pulse=True, description="Issue Register Read."),
CSRField("addr", offset=8, size=4, values=[
("``0b00``", "Identification Register 0 (Read Only)."),
("``0b01``", "Identification Register 1 (Read Only)."),
("``0b10``", "Configuration Register 0."),
("``0b11``", "Configuration Register 1."),
]),
])
self.reg_status = CSRStatus(fields=[
CSRField("write_done", offset=0, size=1, description="Register Write Done."),
CSRField("read_done", offset=1, size=1, description="Register Read Done."),
])
self.reg_wdata = CSRStorage(16, description="Register Write Data.")
self.reg_rdata = CSRStatus( 16, description="Register Read Data.")
self.comb += [
# Control.
self.reg_write.eq(self.reg_control.fields.write),
self.reg_read.eq( self.reg_control.fields.read),
self.reg_addr.eq( self.reg_control.fields.addr),
# Status.
self.reg_status.fields.write_done.eq(self.reg_write_done),
self.reg_status.fields.read_done.eq( self.reg_read_done),
# Data.
self.reg_write_data.eq(self.reg_wdata.storage),
self.reg_rdata.status.eq(self.reg_read_data),
]

291
litex/soc/cores/i2c.py Normal file
View File

@ -0,0 +1,291 @@
#
# This file is part of MiSoC and has been adapted/modified for Litex.
#
# Copyright 2007-2023 / M-Labs Ltd
# Copyright 2012-2015 / Enjoy-Digital
# Copyright from Misoc LICENCE file added above
#
# Copyright 2023 Andrew Dennison <andrew@motec.com.au>
#
# SPDX-License-Identifier: BSD-2-Clause
from migen import *
from litex.gen import *
from litex.soc.interconnect import wishbone
from litex.soc.interconnect.csr_eventmanager import *
# I2C-----------------------------------------------------------------------------------------------
__all__ = [
"I2CMaster",
"I2C_XFER_ADDR", "I2C_CONFIG_ADDR",
"I2C_ACK", "I2C_READ", "I2C_WRITE", "I2C_STOP", "I2C_START", "I2C_IDLE",
]
class I2CClockGen(LiteXModule):
def __init__(self, width):
self.load = Signal(width)
self.clk2x = Signal()
cnt = Signal.like(self.load)
self.comb += [
self.clk2x.eq(cnt == 0),
]
self.sync += [
If(self.clk2x,
cnt.eq(self.load),
).Else(
cnt.eq(cnt - 1),
),
]
class I2CMasterMachine(LiteXModule):
def __init__(self, clock_width):
self.scl_o = Signal(reset=1)
self.sda_o = Signal(reset=1)
self.sda_i = Signal()
self.cg = CEInserter()(I2CClockGen(clock_width))
self.idle = Signal()
self.start = Signal()
self.stop = Signal()
self.write = Signal()
self.read = Signal()
self.ack = Signal()
self.data = Signal(8)
###
busy = Signal()
bits = Signal(4)
fsm = CEInserter()(FSM("IDLE"))
self.fsm = fsm
fsm.act("IDLE",
# Valid combinations (lowest to highest priority):
# stop: lowest priority
# read (& optional stop with automatic NACK)
# write (& optional stop)
# start (indicates start or restart)
# start & write (& optional stop)
# start & write & read (& optional stop)
# lowest priority
# *** TODO: support compound commands with I2CMaster ***
If(self.stop & ~self.scl_o,
# stop is only valid after an ACK
NextState("STOP0"),
),
If(self.read,
# post decrement so read first bit and shift in 7
NextValue(bits, 8-1),
NextState("READ0"),
),
If(self.write,
NextValue(bits, 8),
NextState("WRITE0"),
),
# start could be requesting a restart
If(self.start,
NextState("RESTART0"),
),
# highest priority: start only if scl is high
If(self.start & self.scl_o,
NextState("START0"),
),
)
fsm.act("START0",
# Always entered with scl_o = 1
NextValue(self.sda_o, 0),
NextState("IDLE"))
fsm.act("RESTART0",
# Only entered from IDLE with scl_o = 0
NextValue(self.sda_o, 1),
NextState("RESTART1"))
fsm.act("RESTART1",
NextValue(self.scl_o, 1),
NextState("START0"))
fsm.act("STOP0",
# Only entered from IDLE with scl_o = 0
NextValue(self.sda_o, 0),
NextState("STOP1"))
fsm.act("STOP1",
NextValue(self.scl_o, 1),
NextState("STOP2"))
fsm.act("STOP2",
NextValue(self.sda_o, 1),
NextState("IDLE"))
fsm.act("WRITE0",
NextValue(self.scl_o, 0),
If(bits == 0,
NextValue(self.sda_o, 1),
NextState("READACK0"),
).Else(
NextValue(self.sda_o, self.data[7]),
NextState("WRITE1"),
)
)
fsm.act("WRITE1",
NextValue(self.scl_o, 1),
NextValue(self.data[1:], self.data[:-1]),
NextValue(bits, bits - 1),
NextState("WRITE0"),
)
fsm.act("READACK0",
NextValue(self.scl_o, 1),
NextState("READACK1"),
)
fsm.act("READACK1",
# ACK => IDLE always with scl_o = 0
NextValue(self.scl_o, 0),
NextValue(self.ack, ~self.sda_i),
NextState("IDLE")
)
fsm.act("READ0",
# ACK => IDLE => READ0 always with scl_o = 0
NextValue(self.scl_o, 1),
NextState("READ1"),
)
fsm.act("READ1",
NextValue(self.data[0], self.sda_i),
NextValue(self.scl_o, 0),
If(bits == 0,
NextValue(self.sda_o, ~self.ack),
NextState("WRITEACK0"),
).Else(
#NextValue(self.sda_o, 1), must already be high
NextState("READ2"),
)
)
fsm.act("READ2",
NextValue(self.scl_o, 1),
NextValue(self.data[1:], self.data[:-1]),
NextValue(bits, bits - 1),
NextState("READ1"),
)
fsm.act("WRITEACK0",
NextValue(self.scl_o, 1),
NextState("WRITEACK1"),
)
fsm.act("WRITEACK1",
# ACK => IDLE always with scl_o = 0
NextValue(self.scl_o, 0),
NextValue(self.sda_o, 1),
NextState("IDLE")
)
run = Signal()
self.comb += [
run.eq(self.start | self.stop | self.write | self.read),
self.idle.eq(~run & fsm.ongoing("IDLE")),
self.cg.ce.eq(~self.idle),
fsm.ce.eq(run | self.cg.clk2x),
]
# Registers:
# config = Record([
# ("div", 20),
# ])
# xfer = Record([
# ("data", 8),
# ("ack", 1),
# ("read", 1),
# ("write", 1),
# ("start", 1),
# ("stop", 1),
# ("idle", 1),
# ])
class I2CMaster(LiteXModule):
def __init__(self, pads, bus=None):
if bus is None:
bus = wishbone.Interface(data_width=32)
self.bus = bus
###
# Wishbone
self.i2c = i2c = I2CMasterMachine(
clock_width=20)
self.sync += [
# read
If(bus.adr[0],
bus.dat_r.eq(i2c.cg.load),
).Else(
bus.dat_r.eq(Cat(i2c.data, i2c.ack, C(0, 4), i2c.idle)),
),
# write
i2c.read.eq(0),
i2c.write.eq(0),
i2c.start.eq(0),
i2c.stop.eq(0),
bus.ack.eq(0),
If(bus.cyc & bus.stb & ~bus.ack,
bus.ack.eq(1),
If(bus.we,
If(bus.adr[0],
i2c.cg.load.eq(bus.dat_w),
).Else(
i2c.data.eq(bus.dat_w[0:8]),
i2c.ack.eq(bus.dat_w[8]),
i2c.read.eq(bus.dat_w[9]),
i2c.write.eq(bus.dat_w[10]),
i2c.start.eq(bus.dat_w[11]),
i2c.stop.eq(bus.dat_w[12]),
)
)
)
]
# I/O
self.scl_t = TSTriple()
self.scl_tristate = self.scl_t.get_tristate(pads.scl)
self.comb += [
self.scl_t.oe.eq(~i2c.scl_o),
self.scl_t.o.eq(0),
]
self.sda_t = TSTriple()
self.sda_tristate = self.sda_t.get_tristate(pads.sda)
self.scl_i_n = Signal() # previous scl_t.i
self.sda_oe_n = Signal() # previous sda_t.oe
self.sync += [
self.scl_i_n.eq(self.scl_t.i),
self.sda_oe_n.eq(self.sda_t.oe),
]
self.comb += [
self.sda_t.oe.eq(self.sda_oe_n),
# only change SDA when SCL is stable
If(self.scl_i_n == i2c.scl_o,
self.sda_t.oe.eq(~i2c.sda_o),
),
self.sda_t.o.eq(0),
i2c.sda_i.eq(self.sda_t.i),
]
# Event Manager.
self.ev = EventManager()
self.ev.idle = EventSourceProcess(edge="rising")
self.ev.finalize()
self.comb += self.ev.idle.trigger.eq(i2c.idle)
I2C_XFER_ADDR, I2C_CONFIG_ADDR = range(2)
(
I2C_ACK,
I2C_READ,
I2C_WRITE,
I2C_START,
I2C_STOP,
I2C_IDLE,
) = (1 << i for i in range(8, 14))

View File

@ -79,10 +79,10 @@ class NXLRAM(LiteXModule):
wren = Signal()
self.comb += [
datain.eq(self.bus.dat_w[32*w:32*(w+1)]),
self.bus.dat_r[32*w:32*(w+1)].eq(dataout),
If(self.bus.adr[14:14+self.depth_cascading.bit_length()] == d,
cs.eq(1),
wren.eq(self.bus.we & self.bus.stb & self.bus.cyc)
wren.eq(self.bus.we & self.bus.stb & self.bus.cyc),
self.bus.dat_r[32*w:32*(w+1)].eq(dataout),
),
]
lram_block = Instance("SP512K",

View File

@ -35,6 +35,7 @@ SPI_SLOT_MODE_3 = 0b11
SPI_SLOT_LENGTH_32B = 0b00
SPI_SLOT_LENGTH_16B = 0b01
SPI_SLOT_LENGTH_8B = 0b10
SPI_SLOT_LENGTH_24B = 0b11
SPI_SLOT_BITORDER_MSB_FIRST = 0b0
SPI_SLOT_BITORDER_LSB_FIRST = 0b1
@ -206,7 +207,10 @@ class SPIMaster(LiteXModule):
self.sync += [
If(miso_shift,
miso_data.eq(Cat(miso, miso_data))
)
),
If(self.start,
miso_data.eq(0)
),
]
self.comb += self.miso.eq(miso_data)
@ -239,10 +243,18 @@ class SPICtrl(LiteXModule):
default_slot_bitorder = SPI_SLOT_BITORDER_MSB_FIRST,
default_slot_loopback = 0b1,
default_slot_divider = 2,
default_enable = 0b1,
default_slot_wait = 0,
):
self.nslots = nslots
self.slot_controls = []
self.slot_status = []
version = "SPI0"
self._version = CSRStatus(size=32, description="""SPI Module Version.""",
reset=int.from_bytes(str.encode(version), 'little'))
self.slot_count = CSRStatus(size=32, description="""SPI Module Slot Count.""",
reset=nslots)
# Create TX/RX Control/Status registers.
self.tx_control = CSRStorage(fields=[
@ -302,6 +314,13 @@ class SPICtrl(LiteXModule):
self.ev.rx.trigger.eq(self.rx_status.fields.level > self.rx_control.fields.threshold),
]
self.engine = CSRStorage(fields=[
CSRField("enable", size=1, offset=0, values=[
("``0b0``", "SPI Engine Disabled."),
("``0b1``", "SPI Engine Enabled."),
], reset=default_enable),
])
# Create Slots Control/Status registers.
for slot in range(nslots):
control = CSRStorage(name=f"slot_control{slot}", fields=[
@ -319,7 +338,7 @@ class SPICtrl(LiteXModule):
("``0b00``", "32-bit Max."),
("``0b01``", "16-bit Max."),
("``0b10``", " 8-bit Max."),
("``0b11``", "Reserved."),
("``0b11``", "24-bit Max."),
], reset=default_slot_length),
CSRField("bitorder", size=1, offset=5, values=[
("``0b0``", "MSB-First."),
@ -335,13 +354,15 @@ class SPICtrl(LiteXModule):
("``0x0002``", "SPI-Clk = Sys-Clk/2."),
("``0x0004``", "SPI-Clk = Sys-Clk/4."),
("``0xxxxx``", "SPI-Clk = Sys-Clk/xxxxx."),
], reset=default_slot_divider)
], reset=default_slot_divider),
CSRField("wait", size=16, offset=32, values=[
("``0x0000``", "No wait time."),
("``0x0001``", "wait = 1 / Sys-Clk."),
("``0xxxxx``", "wait = xxxx / Sys-Clk."),
], reset=default_slot_wait),
])
status = CSRStatus(name=f"slot_status{slot}") # CHECKME: Useful?
setattr(self, f"slot_control{slot}", control)
setattr(self, f"slot_status{slot}", status)
self.slot_controls.append(control)
self.slot_status.append(status)
def get_ctrl(self, name, slot=None, cs=None):
assert not ((slot is None) and (cs is None))
@ -487,7 +508,7 @@ class SPIRXMMAP(LiteXModule):
# SPI Engine ---------------------------------------------------------------------------------------
class SPIEngine(LiteXModule):
def __init__(self, pads, ctrl, data_width, sys_clk_freq, default_enable=0b1):
def __init__(self, pads, ctrl, data_width, sys_clk_freq):
self.sink = sink = stream.Endpoint(spi_layout(
data_width = data_width,
be_width = data_width//8,
@ -499,13 +520,6 @@ class SPIEngine(LiteXModule):
cs_width = len(pads.cs_n)
))
self.control = CSRStorage(fields=[
CSRField("enable", size=1, offset=0, values=[
("``0b0``", "SPI Engine Disabled."),
("``0b1``", "SPI Engine Enabled."),
], reset=default_enable),
])
# # #
# SPI Master.
@ -532,6 +546,7 @@ class SPIEngine(LiteXModule):
})
self.comb += Case(ctrl.get_ctrl("length", cs=sink.cs), {
SPI_SLOT_LENGTH_32B : spi_length_max.eq(32), # 32-bit access max.
SPI_SLOT_LENGTH_24B : spi_length_max.eq(24), # 24-bit access max.
SPI_SLOT_LENGTH_16B : spi_length_max.eq(16), # 16-bit access max.
SPI_SLOT_LENGTH_8B : spi_length_max.eq( 8), # 8-bit access max.
})
@ -543,8 +558,15 @@ class SPIEngine(LiteXModule):
)
]
# Wait between transfers.
ctrl_wait = ctrl.get_ctrl("wait", cs=sink.cs)
wait_ticks = Signal.like(ctrl_wait)
wait_count = Signal.like(ctrl_wait)
self.comb += wait_ticks.eq(ctrl_wait)
cs_wait = Signal()
# SPI CS. (Use Manual CS to allow back-to-back Xfers).
self.comb += If(self.control.fields.enable & sink.valid,
self.comb += If(ctrl.engine.fields.enable & sink.valid & ~cs_wait,
spi.cs.eq(sink.cs)
)
@ -555,7 +577,7 @@ class SPIEngine(LiteXModule):
# Control-Path.
self.fsm = fsm = FSM(reset_state="START")
fsm.act("START",
If(self.control.fields.enable & sink.valid,
If(ctrl.engine.fields.enable & sink.valid,
spi.start.eq(1),
NextState("XFER")
)
@ -571,6 +593,20 @@ class SPIEngine(LiteXModule):
source.be.eq(sink.be),
If(source.ready,
sink.ready.eq(1),
If(wait_ticks,
cs_wait.eq(1),
NextValue(wait_count, wait_ticks-1),
NextState("WAIT")
).Else(
NextState("START")
)
)
)
fsm.act("WAIT",
If(wait_count,
cs_wait.eq(1),
NextValue(wait_count, wait_count-1)
).Else(
NextState("START")
)
)
@ -580,9 +616,10 @@ class SPIEngine(LiteXModule):
# MSB First.
If(spi_bitorder == SPI_SLOT_BITORDER_MSB_FIRST,
# TX copy/bitshift.
Case(spi_length, {
Case(spi.length, {
8 : spi.mosi[24:32].eq(sink.data[0: 8]),
16 : spi.mosi[16:32].eq(sink.data[0:16]),
24 : spi.mosi[ 8:32].eq(sink.data[0:24]),
32 : spi.mosi[ 0:32].eq(sink.data[0:32]),
}),
# RX copy.
@ -593,9 +630,10 @@ class SPIEngine(LiteXModule):
# TX copy.
spi.mosi.eq(sink.data[::-1]),
# RX copy/bitshift.
Case(spi_length, {
Case(spi.length, {
8 : source.data[0: 8].eq(spi.miso[::-1][24:32]),
16 : source.data[0:16].eq(spi.miso[::-1][16:32]),
24 : source.data[0:24].eq(spi.miso[::-1][ 8:32]),
32 : source.data[0:32].eq(spi.miso[::-1][ 0:32]),
})
)

View File

@ -646,14 +646,17 @@ class VideoTerminal(LiteXModule):
class VideoFrameBuffer(LiteXModule):
"""Video FrameBuffer"""
def __init__(self, dram_port, hres=800, vres=600, base=0x00000000, fifo_depth=65536, clock_domain="sys", clock_faster_than_sys=False, format="rgb888"):
def __init__(self, dram_port, hres=800, vres=600, base=0x00000000, fifo_depth=64*KILOBYTE, clock_domain="sys", clock_faster_than_sys=False, format="rgb888"):
self.vtg_sink = vtg_sink = stream.Endpoint(video_timing_layout)
self.source = source = stream.Endpoint(video_data_layout)
self.underflow = Signal()
self.depth = depth = {
"rgb888" : 32,
"rgb565" : 16
"rgb565" : 16,
"rgb332" : 8,
"mono8" : 8,
"mono1" : 1,
}[format]
# # #
@ -728,16 +731,34 @@ class VideoFrameBuffer(LiteXModule):
if (depth == 32):
self.comb += [
source.r.eq(video_pipe_source.data[ 0: 8]),
source.g.eq(video_pipe_source.data[ 8:16]),
source.b.eq(video_pipe_source.data[16:24]),
source.r.eq(video_pipe_source.data[ 0: 8]),
source.g.eq(video_pipe_source.data[ 8:16]),
source.b.eq(video_pipe_source.data[16:24]),
]
else: # depth == 16
elif (depth == 16):
self.comb += [
source.r.eq(Cat(Signal(3, reset = 0), video_pipe_source.data[11:16])),
source.g.eq(Cat(Signal(2, reset = 0), video_pipe_source.data[ 5:11])),
source.b.eq(Cat(Signal(3, reset = 0), video_pipe_source.data[ 0: 5])),
]
elif (depth == 8 and format == "rgb332"):
self.comb += [
source.r.eq(Cat(Signal(5, reset = 0), video_pipe_source.data[5:8])),
source.g.eq(Cat(Signal(5, reset = 0), video_pipe_source.data[2:5])),
source.b.eq(Cat(Signal(6, reset = 0), video_pipe_source.data[0:2])),
]
elif (depth == 8 and format == "mono8"):
self.comb += [
source.r.eq(video_pipe_source.data[0:8]),
source.g.eq(video_pipe_source.data[0:8]),
source.b.eq(video_pipe_source.data[0:8]),
]
else: # depth == 1
self.comb += [
source.r.eq(Cat(Signal(7, reset = 0), video_pipe_source.data[0:1])),
source.g.eq(Cat(Signal(7, reset = 0), video_pipe_source.data[0:1])),
source.b.eq(Cat(Signal(7, reset = 0), video_pipe_source.data[0:1])),
]
# Underflow.
self.comb += self.underflow.eq(~source.valid)

View File

@ -0,0 +1,70 @@
#
# This file is part of LiteX.
#
# Copyright (c) 2024 Fin Maaß <f.maass@vogl-electronic.com>
# SPDX-License-Identifier: BSD-2-Clause
from migen import *
from litex.gen import *
from litex.gen.genlib.misc import WaitTimer
from litex.soc.interconnect.csr import *
from litex.soc.interconnect.csr_eventmanager import *
# Watchdog --------------------------------------------------------------------------------------------
class Watchdog(LiteXModule):
"""Watchdog
Provides a generic Watchdog core.
"""
def __init__(self, width=32, crg_rst=None, reset_delay=0, halted=None):
self.enable = Signal()
self.reset_mode = Signal()
self.feed = Signal()
self.halted = Signal()
self.execute = Signal()
self._control = CSRStorage(description="Watchdog Control.", fields=[
CSRField("feed", size=1, offset=0, pulse=True, description="Watchdog feed (Write ``1`` to feed)."),
CSRField("enable", size=1, offset=8, description="Watchdog enable."),
CSRField("reset", size=1, offset=16, description="Reset SoC when watchdog times out."),
CSRField("pause_halted", size=1, offset=24, description="Pause watchdog when CPU is halted.")
])
self.comb += [
self.enable.eq(self._control.fields.enable & ~self.halted),
self.feed.eq(self._control.fields.feed),
self.reset_mode.eq(self._control.fields.reset),
]
if isinstance(halted, Signal):
self.comb += self.halted.eq(halted & self._control.fields.pause_halted)
self._cycles = cycles = CSRStorage(description="Watchdog cycles until timeout.", size=width)
self._remaining = remaining = CSRStatus(description="Watchdog cycles remaining until timeout.", size=width)
self.ev = EventManager()
self.ev.wdt = EventSourceProcess(edge="rising")
self.ev.finalize()
self.sync += [
If(self.feed,
remaining.status.eq(cycles.storage)
).Elif(self.enable,
If(remaining.status != 0,
remaining.status.eq(remaining.status - 1)
),
self.execute.eq(remaining.status == 0),
)
]
self.comb += If(self.enable, self.ev.wdt.trigger.eq(self.execute))
if isinstance(crg_rst, Signal):
self.reset_timer = WaitTimer(reset_delay)
self.comb += self.reset_timer.wait.eq(self.enable & self.execute & self.reset_mode)
self.comb += If(self.reset_timer.done, crg_rst.eq(1))

View File

@ -92,7 +92,9 @@ class Builder:
# Documentation.
generate_doc = False):
self.soc = soc
# SoC/Builder Attach.
self.soc = soc # Attach SoC to Builder.
self.soc.builder = self # Attach Builder to SoC.
# Directories.
self.output_dir = os.path.abspath(output_dir or os.path.join("build", soc.platform.name))
@ -106,9 +108,9 @@ class Builder:
self.compile_gateware = compile_gateware
self.build_backend = build_backend
# Exports.
self.csr_csv = csr_csv
self.csr_json = csr_json
# Exports (Generated by default to output_dir with default name unless explicitly specified).
self.csr_csv = csr_csv if csr_csv else os.path.join(self.output_dir, "csr.csv")
self.csr_json = csr_json if csr_json else os.path.join(self.output_dir, "csr.json")
self.csr_svd = csr_svd
self.memory_x = memory_x
@ -138,27 +140,28 @@ class Builder:
def add_software_library(self, name):
self.software_libraries.append(name)
def add_json(self, filename, origin=0, name=""):
self.jsons.append((filename, origin, name))
def add_json(self, filename, origin=0, name="", exclude_constants=["_INTERRUPT"]):
self.jsons.append((filename, origin, name, exclude_constants))
def _get_json_mem_regions(self):
mem_regions = {}
for filename, name, origin in self.jsons:
_, _, _mem_regions = export.load_csr_json(filename, name, origin)
for filename, origin, name, _ in self.jsons:
_, _, _mem_regions = export.load_csr_json(filename, origin, name)
mem_regions.update(_mem_regions)
return mem_regions
def _get_json_constants(self):
constants = {}
for filename, name, origin in self.jsons:
_, _constants, _ = export.load_csr_json(filename, name, origin)
for filename, origin, name, exclude in self.jsons:
_, _constants, _ = export.load_csr_json(filename, origin, name)
_constants = {k: v for k, v in _constants.items() if not any(ex in k for ex in exclude)}
constants.update(_constants)
return constants
def _get_json_csr_regions(self):
csr_regions = {}
for filename, name, origin in self.jsons:
_csr_regions, _, _ = export.load_csr_json(filename, name, origin)
for filename, origin, name, _ in self.jsons:
_csr_regions, _, _ = export.load_csr_json(filename, origin, name)
csr_regions.update(_csr_regions)
return csr_regions
@ -250,7 +253,10 @@ class Builder:
csr_contents = export.get_csr_header(
regions = self.soc.csr_regions,
constants = self.soc.constants,
csr_base = self.soc.mem_regions["csr"].origin)
csr_base = self.soc.mem_regions["csr"].origin,
with_access_functions = True,
with_fields_access_functions = False,
)
write_to_file(os.path.join(self.generated_dir, "csr.h"), csr_contents)
# Generate Git SHA1 of tools to git.h
@ -331,7 +337,7 @@ class Builder:
)
# Initialize SoC with with BIOS data.
self.soc.initialize_rom(bios_data)
self.soc.init_rom(name="rom", contents=bios_data)
def build(self, **kwargs):
# Pass Output Directory to Platform.
@ -388,9 +394,16 @@ class Builder:
self._prepare_rom_software()
self._generate_rom_software(compile_bios=use_bios)
# Initialize Memories.
# Allow User Design to optionally initialize Memories through SoC.init_ram/init_rom.
if hasattr(self.soc, "init_mems"):
self.soc.init_mems(**kwargs)
# Initialize ROM.
if use_bios and self.soc.integrated_rom_size:
self._initialize_rom_software()
# Only initialize if not already initialized.
if not getattr(self.soc, "rom").mem.init:
self._initialize_rom_software()
# Translate compile_gateware to run.
if "run" not in kwargs:

View File

@ -31,7 +31,7 @@ from migen import *
from litex.soc.interconnect.csr import CSRStatus
from litex.soc.integration.soc import SoCRegion
from litex.build.tools import generated_banner
from litex.build.tools import generated_separator, generated_banner
from litex.soc.doc.rst import reflow
from litex.soc.doc.module import gather_submodules, ModuleNotDocumented, DocumentedModule, DocumentedInterrupts
@ -136,6 +136,9 @@ def get_linker_regions(regions):
# C Export -----------------------------------------------------------------------------------------
# Header.
def get_git_header():
from litex.build.tools import get_litex_git_revision
r = generated_banner("//")
@ -193,25 +196,75 @@ def get_soc_header(constants, with_access_functions=True):
r += "\n#endif\n"
return r
def _generate_csr_header_includes_c(with_access_functions):
includes = ""
if with_access_functions:
includes += "#include <generated/soc.h>\n"
includes += "#ifndef __GENERATED_CSR_H\n"
includes += "#define __GENERATED_CSR_H\n"
if with_access_functions:
includes += "#include <stdint.h>\n"
includes += "#include <system.h>\n"
includes += "#ifndef CSR_ACCESSORS_DEFINED\n"
includes += "#include <hw/common.h>\n"
includes += "#endif /* ! CSR_ACCESSORS_DEFINED */\n"
return includes
def _generate_csr_base_define_c(csr_base, with_csr_base_define):
includes = ""
if with_csr_base_define:
includes += "\n"
includes += "#ifndef CSR_BASE\n"
includes += f"#define CSR_BASE {hex(csr_base)}L\n"
includes += "#endif /* ! CSR_BASE */\n"
return includes
# CSR Definitions.
def _get_csr_addr(csr_base, addr, with_csr_base_define=True):
if with_csr_base_define:
return f"(CSR_BASE + {hex(addr)}L)"
else:
return f"{hex(csr_base + addr)}L"
def _get_rw_functions_c(reg_name, reg_base, nwords, busword, alignment, read_only, csr_base, with_csr_base_define, with_access_functions):
r = ""
def _generate_csr_definitions_c(reg_name, reg_base, nwords, csr_base, with_csr_base_define):
addr_str = f"CSR_{reg_name.upper()}_ADDR"
size_str = f"CSR_{reg_name.upper()}_SIZE"
definitions = f"#define {addr_str} {_get_csr_addr(csr_base, reg_base, with_csr_base_define)}\n"
definitions += f"#define {size_str} {nwords}\n"
return definitions
addr_str = f"CSR_{reg_name.upper()}_ADDR"
size_str = f"CSR_{reg_name.upper()}_SIZE"
r += f"#define {addr_str} {_get_csr_addr(csr_base, reg_base, with_csr_base_define)}\n"
def _generate_csr_region_definitions_c(name, region, origin, alignment, csr_base, with_csr_base_define):
base_define = with_csr_base_define and not isinstance(region, MockCSRRegion)
base = csr_base if not isinstance(region, MockCSRRegion) else 0
region_defs = f"\n/* {name.upper()} Registers */\n"
region_defs += f"#define CSR_{name.upper()}_BASE {_get_csr_addr(base, origin, base_define)}\n"
r += f"#define {size_str} {nwords}\n"
if not isinstance(region.obj, Memory):
for csr in region.obj:
nr = (csr.size + region.busword - 1) // region.busword
region_defs += _generate_csr_definitions_c(
reg_name = name + "_" + csr.name,
reg_base = origin,
nwords = nr,
csr_base = base,
with_csr_base_define = base_define,
)
origin += alignment // 8 * nr
size = nwords*busword//8
region_defs += f"\n/* {name.upper()} Fields */\n"
if not isinstance(region.obj, Memory):
for csr in region.obj:
if hasattr(csr, "fields"):
region_defs += _generate_csr_field_definitions_c(csr, name)
return region_defs
# CSR Read/Write Access Functions.
def _determine_ctype_and_stride_c(size, alignment):
if size > 8:
# Downstream should select appropriate `csr_[rd|wr]_buf_uintX()` pair!
return r
return None, None
elif size > 4:
ctype = "uint64_t"
elif size > 2:
@ -220,98 +273,177 @@ def _get_rw_functions_c(reg_name, reg_base, nwords, busword, alignment, read_onl
ctype = "uint16_t"
else:
ctype = "uint8_t"
stride = alignment // 8
return ctype, stride
stride = alignment//8;
if with_access_functions:
r += f"static inline {ctype} {reg_name}_read(void) {{\n"
if nwords > 1:
r += f"\t{ctype} r = csr_read_simple({_get_csr_addr(csr_base, reg_base, with_csr_base_define)});\n"
for sub in range(1, nwords):
r += f"\tr <<= {busword};\n"
r += f"\tr |= csr_read_simple({_get_csr_addr(csr_base, reg_base+sub*stride, with_csr_base_define)});\n"
r += "\treturn r;\n}\n"
else:
r += f"\treturn csr_read_simple({_get_csr_addr(csr_base, reg_base, with_csr_base_define)});\n}}\n"
def _generate_csr_read_function_c(reg_name, reg_base, nwords, busword, ctype, stride, csr_base, with_csr_base_define):
read_function = f"static inline {ctype} {reg_name}_read(void) {{\n"
if nwords > 1:
read_function += f"\t{ctype} r = csr_read_simple({_get_csr_addr(csr_base, reg_base, with_csr_base_define)});\n"
for sub in range(1, nwords):
read_function += f"\tr <<= {busword};\n"
read_function += f"\tr |= csr_read_simple({_get_csr_addr(csr_base, reg_base + sub * stride, with_csr_base_define)});\n"
read_function += "\treturn r;\n}\n"
else:
read_function += f"\treturn csr_read_simple({_get_csr_addr(csr_base, reg_base, with_csr_base_define)});\n}}\n"
return read_function
if not read_only:
r += f"static inline void {reg_name}_write({ctype} v) {{\n"
for sub in range(nwords):
shift = (nwords-sub-1)*busword
if shift:
v_shift = "v >> {}".format(shift)
else:
v_shift = "v"
r += f"\tcsr_write_simple({v_shift}, {_get_csr_addr(csr_base, reg_base+sub*stride, with_csr_base_define)});\n"
r += "}\n"
return r
def _generate_csr_write_function_c(reg_name, reg_base, nwords, busword, ctype, stride, csr_base, with_csr_base_define):
write_function = f"static inline void {reg_name}_write({ctype} v) {{\n"
for sub in range(nwords):
shift = (nwords - sub - 1) * busword
v_shift = f"v >> {shift}" if shift else "v"
write_function += f"\tcsr_write_simple({v_shift}, {_get_csr_addr(csr_base, reg_base + sub * stride, with_csr_base_define)});\n"
write_function += "}\n"
return write_function
def _get_csr_read_write_access_functions_c(reg_name, reg_base, nwords, busword, alignment, read_only, csr_base, with_csr_base_define):
result = ""
size = nwords * busword // 8
ctype, stride = _determine_ctype_and_stride_c(size, alignment)
if ctype is None:
return result
result += _generate_csr_read_function_c(reg_name, reg_base, nwords, busword, ctype, stride, csr_base, with_csr_base_define)
if not read_only:
result += _generate_csr_write_function_c(reg_name, reg_base, nwords, busword, ctype, stride, csr_base, with_csr_base_define)
return result
def _generate_csr_region_access_functions_c(name, region, origin, alignment, csr_base, with_csr_base_define):
base_define = with_csr_base_define and not isinstance(region, MockCSRRegion)
region_defs = f"\n/* {name.upper()} Access Functions */\n"
if not isinstance(region.obj, Memory):
for csr in region.obj:
nr = (csr.size + region.busword - 1) // region.busword
region_defs += _get_csr_read_write_access_functions_c(
reg_name = name + "_" + csr.name,
reg_base = origin,
nwords = nr,
busword = region.busword,
alignment = alignment,
read_only = getattr(csr, "read_only", False),
csr_base = csr_base,
with_csr_base_define = base_define,
)
origin += alignment // 8 * nr
return region_defs
# CSR Fields.
def _generate_csr_field_definitions_c(csr, name):
field_defs = ""
for field in csr.fields.fields:
offset = str(field.offset)
size = str(field.size)
field_defs += f"#define CSR_{name.upper()}_{csr.name.upper()}_{field.name.upper()}_OFFSET {offset}\n"
field_defs += f"#define CSR_{name.upper()}_{csr.name.upper()}_{field.name.upper()}_SIZE {size}\n"
return field_defs
def _generate_csr_field_accessors_c(name, csr, field):
accessors = ""
if csr.size <= 32:
reg_name = name + "_" + csr.name.lower()
field_name = reg_name + "_" + field.name.lower()
offset = str(field.offset)
size = str(field.size)
accessors += f"static inline uint32_t {field_name}_extract(uint32_t oldword) {{\n"
accessors += f"\tuint32_t mask = 0x{(1 << int(size)) - 1:x};\n"
accessors += f"\treturn ((oldword >> {offset}) & mask);\n}}\n"
accessors += f"static inline uint32_t {field_name}_read(void) {{\n"
accessors += f"\tuint32_t word = {reg_name}_read();\n"
accessors += f"\treturn {field_name}_extract(word);\n}}\n"
if not getattr(csr, "read_only", False):
accessors += f"static inline uint32_t {field_name}_replace(uint32_t oldword, uint32_t plain_value) {{\n"
accessors += f"\tuint32_t mask = 0x{(1 << int(size)) - 1:x};\n"
accessors += f"\treturn (oldword & (~(mask << {offset}))) | ((mask & plain_value) << {offset});\n}}\n"
accessors += f"static inline void {field_name}_write(uint32_t plain_value) {{\n"
accessors += f"\tuint32_t oldword = {reg_name}_read();\n"
accessors += f"\tuint32_t newword = {field_name}_replace(oldword, plain_value);\n"
accessors += f"\t{reg_name}_write(newword);\n}}\n"
return accessors
def _generate_csr_field_functions_c(csr, name):
field_funcs = ""
for field in csr.fields.fields:
field_funcs += _generate_csr_field_accessors_c(name, csr, field)
return field_funcs
def _generate_csr_fields_access_functions_c(name, region, origin, alignment, csr_base, with_csr_base_define):
base_define = with_csr_base_define and not isinstance(region, MockCSRRegion)
region_defs = f"\n/* {name.upper()} Fields Access Functions */\n"
if not isinstance(region.obj, Memory):
for csr in region.obj:
nr = (csr.size + region.busword - 1) // region.busword
origin += alignment // 8 * nr
if hasattr(csr, "fields"):
region_defs += _generate_csr_field_functions_c(csr, name)
return region_defs
# CSR Header.
def get_csr_header(regions, constants, csr_base=None, with_csr_base_define=True, with_access_functions=True, with_fields_access_functions=False):
"""
Generate the CSR header file content.
"""
def get_csr_header(regions, constants, csr_base=None, with_csr_base_define=True, with_access_functions=True):
alignment = constants.get("CONFIG_CSR_ALIGNMENT", 32)
r = generated_banner("//")
if with_access_functions: # FIXME
r += "#include <generated/soc.h>\n"
r += "#ifndef __GENERATED_CSR_H\n#define __GENERATED_CSR_H\n"
if with_access_functions:
r += "#include <stdint.h>\n"
r += "#include <system.h>\n"
r += "#ifndef CSR_ACCESSORS_DEFINED\n"
r += "#include <hw/common.h>\n"
r += "#endif /* ! CSR_ACCESSORS_DEFINED */\n"
# CSR Includes.
r += "\n"
r += generated_separator("//", "CSR Includes.")
r += "\n"
r += _generate_csr_header_includes_c(with_access_functions)
_csr_base = regions[next(iter(regions))].origin
csr_base = csr_base if csr_base is not None else _csr_base
if with_csr_base_define:
r += "\n#ifndef CSR_BASE\n"
r += f"#define CSR_BASE {hex(csr_base)}L\n"
r += "#endif\n"
csr_base = csr_base if csr_base is not None else _csr_base
r += _generate_csr_base_define_c(csr_base, with_csr_base_define)
# CSR Registers/Fields Definition.
r += "\n"
r += generated_separator("//", "CSR Registers/Fields Definition.")
for name, region in regions.items():
origin = region.origin - _csr_base
r += "\n/* "+name+" */\n"
r += f"#define CSR_{name.upper()}_BASE {_get_csr_addr(csr_base, origin, with_csr_base_define)}\n"
if not isinstance(region.obj, Memory):
for csr in region.obj:
nr = (csr.size + region.busword - 1)//region.busword
r += _get_rw_functions_c(
reg_name = name + "_" + csr.name,
reg_base = origin,
nwords = nr,
busword = region.busword,
alignment = alignment,
read_only = getattr(csr, "read_only", False),
csr_base = csr_base,
with_csr_base_define = with_csr_base_define,
with_access_functions = with_access_functions,
)
origin += alignment//8*nr
if hasattr(csr, "fields"):
for field in csr.fields.fields:
offset = str(field.offset)
size = str(field.size)
r += f"#define CSR_{name.upper()}_{csr.name.upper()}_{field.name.upper()}_OFFSET {offset}\n"
r += f"#define CSR_{name.upper()}_{csr.name.upper()}_{field.name.upper()}_SIZE {size}\n"
if with_access_functions and csr.size <= 32: # FIXME: Implement extract/read functions for csr.size > 32-bit.
reg_name = name + "_" + csr.name.lower()
field_name = reg_name + "_" + field.name.lower()
r += "static inline uint32_t " + field_name + "_extract(uint32_t oldword) {\n"
r += f"\tuint32_t mask = 0x{(1<<int(size))-1:x};\n"
r += "\treturn ( (oldword >> " + offset + ") & mask );\n}\n"
r += "static inline uint32_t " + field_name + "_read(void) {\n"
r += "\tuint32_t word = " + reg_name + "_read();\n"
r += "\treturn " + field_name + "_extract(word);\n"
r += "}\n"
if not getattr(csr, "read_only", False):
r += "static inline uint32_t " + field_name + "_replace(uint32_t oldword, uint32_t plain_value) {\n"
r += f"\tuint32_t mask = 0x{(1<<int(size))-1:x};\n"
r += "\treturn (oldword & (~(mask << " + offset + "))) | (mask & plain_value)<< " + offset + " ;\n}\n"
r += "static inline void " + field_name + "_write(uint32_t plain_value) {\n"
r += "\tuint32_t oldword = " + reg_name + "_read();\n"
r += "\tuint32_t newword = " + field_name + "_replace(oldword, plain_value);\n"
r += "\t" + reg_name + "_write(newword);\n"
r += "}\n"
r += _generate_csr_region_definitions_c(name, region, origin, alignment, csr_base, with_csr_base_define)
r += "\n#endif\n"
# CSR Registers Access Functions.
if with_access_functions:
r += "\n"
r += generated_separator("//", "CSR Registers Access Functions.")
r += "\n"
r += "#ifndef LITEX_CSR_ACCESS_FUNCTIONS\n"
r += "#define LITEX_CSR_ACCESS_FUNCTIONS 1\n"
r += "#endif\n"
r += "\n"
r += "#if LITEX_CSR_ACCESS_FUNCTIONS\n"
for name, region in regions.items():
origin = region.origin - _csr_base
r += _generate_csr_region_access_functions_c(name, region, origin, alignment, csr_base, with_csr_base_define)
r += "#endif /* LITEX_CSR_ACCESS_FUNCTIONS */\n"
# CSR Registers Field Access Functions.
if with_fields_access_functions:
r += "\n"
r += generated_separator("//", "CSR Registers Field Access Functions.")
r += "\n"
r += "#ifndef LITEX_CSR_FIELDS_ACCESS_FUNCTIONS\n"
r += "#define LITEX_CSR_FIELDS_ACCESS_FUNCTIONS 1\n"
r += "#endif\n"
r += "\n"
r += "#if LITEX_CSR_FIELDS_ACCESS_FUNCTIONS\n"
for name, region in regions.items():
origin = region.origin - _csr_base
r += _generate_csr_fields_access_functions_c(name, region, origin, alignment, csr_base, with_csr_base_define)
r += "#endif /* LITEX_CSR_FIELDS_ACCESS_FUNCTIONS */\n"
r += "\n#endif /* ! __GENERATED_CSR_H */\n"
return r
# C I2C Export -------------------------------------------------------------------------------------
def get_i2c_header(i2c_init_values):
i2c_devs, i2c_init = i2c_init_values

View File

@ -30,6 +30,7 @@ from litex.soc.interconnect import csr_bus
from litex.soc.interconnect import stream
from litex.soc.interconnect import wishbone
from litex.soc.interconnect import axi
from litex.soc.interconnect import ahb
# Helpers ------------------------------------------------------------------------------------------
@ -347,11 +348,24 @@ class SoCBusHandler(LiteXModule):
axi.AXILiteInterface : axi.AXILiteConverter,
axi.AXIInterface : axi.AXIConverter,
}[interface_cls]
adapted_interface = interface_cls(
data_width = self.data_width,
address_width = self.address_width,
addressing = interface.addressing,
)
args = {
"data_width" : self.data_width,
"address_width" : self.address_width,
"addressing" : interface.addressing,
"bursting" : interface.bursting,
}
if isinstance(interface, axi.AXIInterface):
args.update({
"version" : interface.version,
"id_width" : interface.id_width,
"aw_user_width" : interface.aw.user_width,
"w_user_width" : interface.w.user_width,
"b_user_width" : interface.b.user_width,
"ar_user_width" : interface.ar.user_width,
"r_user_width" : interface.r.user_width,
})
adapted_interface = interface_cls(**args)
if direction == "m2s":
master, slave = interface, adapted_interface
elif direction == "s2m":
@ -365,8 +379,8 @@ class SoCBusHandler(LiteXModule):
# Same Addressing, return un-modified interface.
if interface.addressing == self.addressing:
return interface
# AXI/AXI-Lite interface, Bus-Addressing conversion already handled in Bus-Standard conversion.
elif isinstance(interface, (axi.AXIInterface, axi.AXILiteInterface)):
# AXI/AXI-Lite/AHB interface, Bus-Addressing conversion already handled in Bus-Standard conversion.
elif isinstance(interface, (axi.AXIInterface, axi.AXILiteInterface, ahb.AHBInterface)):
return interface
# Different Addressing: Return adapted interface.
else:
@ -420,6 +434,7 @@ class SoCBusHandler(LiteXModule):
(axi.AXILiteInterface, axi.AXIInterface) : axi.AXILite2AXI,
(axi.AXIInterface, axi.AXILiteInterface): axi.AXI2AXILite,
(axi.AXIInterface, wishbone.Interface) : axi.AXI2Wishbone,
(ahb.AHBInterface, wishbone.Interface) : ahb.AHB2Wishbone,
}[type(master), type(slave)]
bridge = bridge_cls(master, slave)
self.submodules += bridge
@ -437,6 +452,7 @@ class SoCBusHandler(LiteXModule):
wishbone.Interface: "Wishbone",
axi.AXILiteInterface: "AXI-Lite",
axi.AXIInterface: "AXI",
ahb.AHBInterface: "AHB",
}
self.logger.info(fmt.format(
name = colorer(name),
@ -997,6 +1013,8 @@ class SoC(LiteXModule, SoCCoreCompat):
self.logger.info(self.irq)
self.logger.info(colorer("-"*80, color="bright"))
# SoC Configs ------------------------------------------------------------------------------
self.add_config("PLATFORM_NAME", platform.name)
self.add_config("CLOCK_FREQUENCY", int(sys_clk_freq))
# SoC Helpers ----------------------------------------------------------------------------------
@ -1041,6 +1059,8 @@ class SoC(LiteXModule, SoCCoreCompat):
raise SoCError()
# SoC Main Components --------------------------------------------------------------------------
# Add Controller -------------------------------------------------------------------------------
def add_controller(self, name="ctrl", **kwargs):
self.check_if_exists(name)
self.logger.info("Controller {} {}.".format(
@ -1048,6 +1068,7 @@ class SoC(LiteXModule, SoCCoreCompat):
colorer("added", color="green")))
self.add_module(name=name, module=SoCController(**kwargs))
# Add/Init RAM ---------------------------------------------------------------------------------
def add_ram(self, name, origin, size, contents=[], mode="rwx"):
ram_cls = {
"wishbone": wishbone.SRAM,
@ -1064,7 +1085,7 @@ class SoC(LiteXModule, SoCCoreCompat):
address_width = self.bus.address_width,
bursting = self.bus.bursting
)
ram = ram_cls(size, bus=ram_bus, init=contents, read_only=("w" not in mode), name=name)
ram = ram_cls(size, bus=ram_bus, init=contents, read_only=("w" not in mode), name=name)
self.bus.add_slave(name=name, slave=ram.bus, region=SoCRegion(origin=origin, size=size, mode=mode))
self.check_if_exists(name)
self.logger.info("RAM {} {} {}.".format(
@ -1075,21 +1096,50 @@ class SoC(LiteXModule, SoCCoreCompat):
if contents != []:
self.add_config(f"{name}_INIT", 1)
def init_ram(self, name, contents=[], auto_size=False):
# RAM Parameters.
ram = getattr(self, name)
ram_region = self.bus.regions[name]
ram_type = {
True : "ROM",
False : "RAM",
}["w" not in ram_region.mode]
contents_size = 4*len(contents) # FIXME.
# Size Check.
if ram_region.size < contents_size:
self.logger.error("Contents Size ({}) {} {} Size ({}).".format(
colorer(f"0x{contents_size:x}"),
colorer("exceeds", color="red"),
ram_type,
colorer(f"0x{ram_region.size:x}"),
))
raise SoCError()
# RAM Initialization.
self.logger.info("Initializing {} {} with contents (Size: {}).".format(
ram_type,
colorer(name),
colorer(f"0x{contents_size:x}")))
ram.mem.init = contents
# RAM Auto-Resize (Optional).
if auto_size and ("w" not in ram_region.mode):
self.logger.info("Auto-Resizing {} {} from {} to {}.".format(
ram_type,
colorer(name),
colorer(f"0x{ram_region.size:x}"),
colorer(f"0x{contents_size:x}")))
ram.mem.depth = len(contents)
# Add/Init ROM ---------------------------------------------------------------------------------
def add_rom(self, name, origin, size, contents=[], mode="rx"):
self.add_ram(name, origin, size, contents, mode=mode)
def init_rom(self, name, contents=[], auto_size=True):
self.logger.info("Initializing ROM {} with contents (Size: {}).".format(
colorer(name),
colorer(f"0x{4*len(contents):x}")))
getattr(self, name).mem.init = contents
if auto_size and "w" not in self.bus.regions[name].mode:
self.logger.info("Auto-Resizing ROM {} from {} to {}.".format(
colorer(name),
colorer(f"0x{self.bus.regions[name].size:x}"),
colorer(f"0x{4*len(contents):x}")))
getattr(self, name).mem.depth = len(contents)
self.init_ram(name, contents, auto_size)
# Add CSR Bridge -------------------------------------------------------------------------------
def add_csr_bridge(self, name="csr", origin=None, register=False):
csr_bridge_cls = {
"wishbone": wishbone.Wishbone2CSR,
@ -1128,6 +1178,7 @@ class SoC(LiteXModule, SoCCoreCompat):
self.add_config("CSR_DATA_WIDTH", self.csr.data_width)
self.add_config("CSR_ALIGNMENT", self.csr.alignment)
# Add CPU --------------------------------------------------------------------------------------
def add_cpu(self, name="vexriscv", variant="standard", reset_address=None, cfu=None):
from litex.soc.cores import cpu
@ -1269,11 +1320,13 @@ class SoC(LiteXModule, SoCCoreCompat):
# Add constants.
self.add_config(f"CPU_TYPE_{name}")
self.add_config(f"CPU_VARIANT_{str(variant.split('+')[0])}")
self.add_config("CPU_FAMILY", getattr(self.cpu, "family", "Unknown"))
self.add_config("CPU_NAME", getattr(self.cpu, "name", "Unknown"))
self.add_config("CPU_HUMAN_NAME", getattr(self.cpu, "human_name", "Unknown"))
if hasattr(self.cpu, "nop"):
self.add_config("CPU_NOP", self.cpu.nop)
# Add Timer ------------------------------------------------------------------------------------
def add_timer(self, name="timer0"):
from litex.soc.cores.timer import Timer
self.check_if_exists(name)
@ -1281,6 +1334,24 @@ class SoC(LiteXModule, SoCCoreCompat):
if self.irq.enabled:
self.irq.add(name, use_loc_if_exists=True)
# Add Watchdog ---------------------------------------------------------------------------------
def add_watchdog(self, name="watchdog0", width=32, crg_rst=None, reset_delay=None):
from litex.soc.cores.watchdog import Watchdog
if crg_rst is None:
crg_rst = getattr(self.crg, "rst", None) if hasattr(self, "crg") else None
if reset_delay is None:
reset_delay = self.sys_clk_freq
halted = getattr(self.cpu, "o_halted", None) if hasattr(self, "cpu") else None
self.check_if_exists(name)
watchdog = Watchdog(width=width, crg_rst=crg_rst, reset_delay=int(reset_delay), halted=halted)
self.add_module(name=name, module=watchdog)
if self.irq.enabled:
self.irq.add(name, use_loc_if_exists=True)
# SoC finalization -----------------------------------------------------------------------------
def finalize(self):
if self.finalized:
@ -1455,9 +1526,10 @@ class LiteXSoC(SoC):
else:
self.add_config("BIOS_NO_BUILD_TIME")
self.add_module(name=name, module=Identifier(identifier))
self.add_config(name, identifier)
# Add UART -------------------------------------------------------------------------------------
def add_uart(self, name="uart", uart_name="serial", baudrate=115200, fifo_depth=16):
def add_uart(self, name="uart", uart_name="serial", uart_pads=None, baudrate=115200, fifo_depth=16):
# Imports.
from litex.soc.cores.uart import UART, UARTCrossover
@ -1474,8 +1546,9 @@ class LiteXSoC(SoC):
"usb_acm",
"serial(x)",
]
uart_pads_name = "serial" if uart_name == "sim" else uart_name
uart_pads = self.platform.request(uart_pads_name, loose=True)
if uart_pads is None:
uart_pads_name = "serial" if uart_name == "sim" else uart_name
uart_pads = self.platform.request(uart_pads_name, loose=True)
uart_phy = None
uart = None
uart_kwargs = {
@ -1546,7 +1619,7 @@ class LiteXSoC(SoC):
if self.irq.enabled:
self.irq.add(name, use_loc_if_exists=True)
else:
self.add_constant("UART_POLLING")
self.add_constant("UART_POLLING", check_duplicate=False)
# Add UARTbone ---------------------------------------------------------------------------------
def add_uartbone(self, name="uartbone", uart_name="serial", clk_freq=None, baudrate=115200, cd="sys"):
@ -1795,14 +1868,14 @@ class LiteXSoC(SoC):
from liteeth.phy.model import LiteEthPHYModel
# MAC.
assert data_width in [8, 32]
assert data_width in [8, 32, 64]
with_sys_datapath = (data_width == 32)
self.check_if_exists(name)
if with_timestamp:
self.timer0.add_uptime()
ethmac = LiteEthMAC(
phy = phy,
dw = 32,
dw = {8: 32, 32: 32, 64: 64}[data_width],
interface = "wishbone",
endianness = self.cpu.endianness,
nrxslots = nrxslots, rxslots_read_only = rxslots_read_only,
@ -1816,10 +1889,31 @@ class LiteXSoC(SoC):
"eth_tx": phy_cd + "_tx",
"eth_rx": phy_cd + "_rx"})(ethmac)
self.add_module(name=name, module=ethmac)
# Compute Regions size and add it to the SoC.
ethmac_region_size = (ethmac.rx_slots.constant + ethmac.tx_slots.constant)*ethmac.slot_size.constant
ethmac_region = SoCRegion(origin=self.mem_map.get(name, None), size=ethmac_region_size, cached=False)
self.bus.add_slave(name=name, slave=ethmac.bus, region=ethmac_region)
ethmac_rx_region_size = ethmac.rx_slots.constant*ethmac.slot_size.constant
ethmac_tx_region_size = ethmac.tx_slots.constant*ethmac.slot_size.constant
ethmac_region_size = ethmac_rx_region_size + ethmac_tx_region_size
self.bus.add_region(name, SoCRegion(
origin = self.mem_map.get(name, None),
size = ethmac_region_size,
linker = True,
cached = False,
))
ethmac_rx_region = SoCRegion(
origin = self.bus.regions[name].origin + 0,
size = ethmac_rx_region_size,
linker = True,
cached = False,
)
self.bus.add_slave(name=f"{name}_rx", slave=ethmac.bus_rx, region=ethmac_rx_region)
ethmac_tx_region = SoCRegion(
origin = self.bus.regions[name].origin + ethmac_rx_region_size,
size = ethmac_tx_region_size,
linker = True,
cached = False,
)
self.bus.add_slave(name=f"{name}_tx", slave=ethmac.bus_tx, region=ethmac_tx_region)
# Add IRQs (if enabled).
if self.irq.enabled:
@ -1932,9 +2026,30 @@ class LiteXSoC(SoC):
ethcore.autocsr_exclude = {"mac"}
# Software Interface.
self.ethmac = ethmac = ethcore.mac
ethmac_region_size = (ethmac.rx_slots.constant + ethmac.tx_slots.constant)*ethmac.slot_size.constant
ethmac_region = SoCRegion(origin=self.mem_map.get("ethmac", None), size=ethmac_region_size, cached=False)
self.bus.add_slave(name="ethmac", slave=ethmac.bus, region=ethmac_region)
ethmac_rx_region_size = ethmac.rx_slots.constant*ethmac.slot_size.constant
ethmac_tx_region_size = ethmac.tx_slots.constant*ethmac.slot_size.constant
ethmac_region_size = ethmac_rx_region_size + ethmac_tx_region_size
self.bus.add_region("ethmac", SoCRegion(
origin = self.mem_map.get("ethmac", None),
size = ethmac_region_size,
linker = True,
cached = False,
))
ethmac_rx_region = SoCRegion(
origin = self.bus.regions["ethmac"].origin + 0,
size = ethmac_rx_region_size,
linker = True,
cached = False,
)
self.bus.add_slave(name=f"ethmac_rx", slave=ethmac.bus_rx, region=ethmac_rx_region)
ethmac_tx_region = SoCRegion(
origin = self.bus.regions["ethmac"].origin + ethmac_rx_region_size,
size = ethmac_tx_region_size,
linker = True,
cached = False,
)
self.bus.add_slave(name=f"ethmac_tx", slave=ethmac.bus_tx, region=ethmac_tx_region)
# Add IRQs (if enabled).
if self.irq.enabled:
self.irq.add("ethmac", use_loc_if_exists=True)
@ -1945,6 +2060,29 @@ class LiteXSoC(SoC):
add_ip_address_constants(self, "REMOTEIP", ethmac_remote_ip)
add_mac_address_constants(self, "MACADDR", ethmac_address)
# Add SPI Master --------------------------------------------------------------------------------
def add_spi_master(self, name="spimaster", pads=None, data_width=8, spi_clk_freq=1e6, with_clk_divider=True, **kwargs):
# Imports.
from litex.soc.cores.spi import SPIMaster
self.check_if_exists(f"{name}")
spi_clk_freq = int(spi_clk_freq)
if pads is None:
pads = self.platform.request(name)
spim = SPIMaster(pads, data_width, self.sys_clk_freq, spi_clk_freq, **kwargs)
if with_clk_divider:
spim.add_clk_divider()
self.add_module(name=f"{name}", module=spim)
self.add_constant(f"{name}_FREQUENCY", spi_clk_freq)
self.add_constant(f"{name}_DATA_WIDTH", data_width)
self.add_constant(f"{name}_MAX_CS", len(pads.cs_n))
# Add SPI Flash --------------------------------------------------------------------------------
def add_spi_flash(self, name="spiflash", mode="4x", clk_freq=20e6, module=None, phy=None, rate="1:1", software_debug=False, **kwargs):
# Imports.
@ -1971,6 +2109,72 @@ class LiteXSoC(SoC):
self.add_module(name=f"{name}_core", module=spiflash_core)
spiflash_region = SoCRegion(origin=self.mem_map.get(name, None), size=module.total_size, mode="rwx")
self.bus.add_slave(name=name, slave=spiflash_core.bus, region=spiflash_region)
self.comb += spiflash_core.mmap.offset.eq(self.bus.regions.get(name, None).origin)
# Constants.
self.add_constant(f"{name}_PHY_FREQUENCY", clk_freq)
self.add_constant(f"{name}_MODULE_NAME", module.name)
self.add_constant(f"{name}_MODULE_TOTAL_SIZE", module.total_size)
self.add_constant(f"{name}_MODULE_PAGE_SIZE", module.page_size)
if mode in [ "4x" ]:
if SpiNorFlashOpCodes.READ_1_1_4 in module.supported_opcodes:
self.add_constant(f"{name}_MODULE_QUAD_CAPABLE")
if SpiNorFlashOpCodes.READ_4_4_4 in module.supported_opcodes:
self.add_constant(f"{name}_MODULE_QPI_CAPABLE")
if software_debug:
self.add_constant(f"{name}_DEBUG")
# Add SPI RAM --------------------------------------------------------------------------------
def add_spi_ram(self, name="spiram", mode="4x", clk_freq=20e6, module=None, phy=None, rate="1:1", software_debug=False,
l2_cache_size = 8192,
l2_cache_reverse = False,
l2_cache_full_memory_we = True,
**kwargs):
# Imports.
from litespi import LiteSPI
from litespi.phy.generic import LiteSPIPHY
from litespi.opcodes import SpiNorFlashOpCodes
# Checks/Parameters.
assert mode in ["1x", "4x"]
default_divisor = math.ceil(self.sys_clk_freq/(2*clk_freq)) - 1
clk_freq = int(self.sys_clk_freq/(2*(default_divisor + 1)))
# PHY.
spiram_phy = phy
if spiram_phy is None:
self.check_if_exists(f"{name}_phy")
spiram_pads = self.platform.request(name if mode == "1x" else name + mode)
spiram_phy = LiteSPIPHY(spiram_pads, module, device=self.platform.device, default_divisor=default_divisor, rate=rate)
self.add_module(name=f"{name}_phy", module=spiram_phy)
# Core.
self.check_if_exists(f"{name}_mmap")
spiram_core = LiteSPI(spiram_phy, mmap_endianness=self.cpu.endianness, with_mmap_write=True, **kwargs)
self.add_module(name=f"{name}_core", module=spiram_core)
spiram_region = SoCRegion(origin=self.mem_map.get(name, None), size=module.total_size)
# Create Wishbone Slave.
wb_spiram = wishbone.Interface(data_width=32, address_width=32, addressing="word")
self.bus.add_slave(name=name, slave=wb_spiram, region=spiram_region)
self.comb += spiram_core.mmap.offset.eq(self.bus.regions.get(name, None).origin)
# L2 Cache
if l2_cache_size != 0:
# Insert L2 cache inbetween Wishbone bus and LiteSPI
l2_cache_size = max(l2_cache_size, int(2*32/8)) # Use minimal size if lower
l2_cache_size = 2**int(log2(l2_cache_size)) # Round to nearest power of 2
l2_cache = wishbone.Cache(
cachesize = l2_cache_size//4,
master = wb_spiram,
slave = spiram_core.bus,
reverse = l2_cache_reverse)
if l2_cache_full_memory_we:
l2_cache = FullMemoryWE()(l2_cache)
self.l2_cache = l2_cache
self.add_config("L2_SIZE", l2_cache_size)
else:
self.submodules += wishbone.Converter(wb_spiram, spiram_core.bus)
# Constants.
self.add_constant(f"{name}_PHY_FREQUENCY", clk_freq)
@ -2203,7 +2407,8 @@ class LiteXSoC(SoC):
with_dma_synchronizer = False,
with_dma_monitor = False,
with_dma_status = False,
with_msi = True, msi_type="msi", msi_width=32,
with_dma_table = True,
with_msi = True, msi_type="msi", msi_width=32, msis={},
with_ptm = False,
):
# Imports
@ -2245,7 +2450,7 @@ class LiteXSoC(SoC):
self.add_module(name=f"{name}_msi", module=msi)
if msi_type in ["msi", "msi-multi-vector"]:
self.comb += msi.source.connect(phy.msi)
self.msis = {}
self.msis = msis
# DMAs.
for i in range(ndmas):
@ -2257,16 +2462,18 @@ class LiteXSoC(SoC):
with_synchronizer = with_dma_synchronizer,
with_monitor = with_dma_monitor,
with_status = with_dma_status,
with_table = with_dma_table,
address_width = address_width,
data_width = data_width,
)
self.add_module(name=f"{name}_dma{i}", module=dma)
self.msis[f"{name.upper()}_DMA{i}_WRITER"] = dma.writer.irq
self.msis[f"{name.upper()}_DMA{i}_READER"] = dma.reader.irq
if with_dma_table:
self.msis[f"{name.upper()}_DMA{i}_WRITER"] = dma.writer.irq
self.msis[f"{name.upper()}_DMA{i}_READER"] = dma.reader.irq
self.add_constant("DMA_CHANNELS", ndmas)
self.add_constant("DMA_ADDR_WIDTH", address_width)
# Map/Connect IRQs.
# Map/Connect MSI IRQs.
if with_msi:
for i, (k, v) in enumerate(sorted(self.msis.items())):
self.comb += msi.irqs[i].eq(v)
@ -2333,7 +2540,7 @@ class LiteXSoC(SoC):
self.comb += vt.source.connect(phy if isinstance(phy, stream.Endpoint) else phy.sink)
# Add Video Framebuffer ------------------------------------------------------------------------
def add_video_framebuffer(self, name="video_framebuffer", phy=None, timings="800x600@60Hz", clock_domain="sys", format="rgb888"):
def add_video_framebuffer(self, name="video_framebuffer", phy=None, timings="800x600@60Hz", clock_domain="sys", format="rgb888", fifo_depth=64*KILOBYTE):
# Imports.
from litex.soc.cores.video import VideoTimingGenerator, VideoFrameBuffer
@ -2355,10 +2562,11 @@ class LiteXSoC(SoC):
hres = int(timings.split("@")[0].split("x")[0])
vres = int(timings.split("@")[0].split("x")[1])
vfb = VideoFrameBuffer(self.sdram.crossbar.get_port(),
hres = hres,
vres = vres,
base = base,
format = format,
hres = hres,
vres = vres,
base = base,
fifo_depth = fifo_depth,
format = format,
clock_domain = clock_domain,
clock_faster_than_sys = vtg.video_timings["pix_clk"] >= self.sys_clk_freq,
)

View File

@ -42,7 +42,7 @@ __all__ = [
# SoCCore ------------------------------------------------------------------------------------------
class SoCCore(LiteXSoC):
# Default register/interrupt/memory mappings (can be redefined by user)
# Default register/interrupt/memory mappings (can be redefined by user).
csr_map = {}
interrupt_map = {}
mem_map = {
@ -52,7 +52,7 @@ class SoCCore(LiteXSoC):
}
def __init__(self, platform, clk_freq,
# Bus parameters
# Bus parameters.
bus_standard = "wishbone",
bus_data_width = 32,
bus_address_width = 32,
@ -60,62 +60,67 @@ class SoCCore(LiteXSoC):
bus_bursting = False,
bus_interconnect = "shared",
# CPU parameters
# CPU parameters.
cpu_type = "vexriscv",
cpu_reset_address = None,
cpu_variant = None,
cpu_cfu = None,
# CFU parameters
# CFU parameters.
cfu_filename = None,
# ROM parameters
# ROM parameters.
integrated_rom_size = 0,
integrated_rom_mode = "rx",
integrated_rom_init = [],
# SRAM parameters
# SRAM parameters.
integrated_sram_size = 0x2000,
integrated_sram_init = [],
# MAIN_RAM parameters
# MAIN_RAM parameters.
integrated_main_ram_size = 0,
integrated_main_ram_init = [],
# CSR parameters
# CSR parameters.
csr_data_width = 32,
csr_address_width = 14,
csr_paging = 0x800,
csr_ordering = "big",
# Interrupt parameters
# Interrupt parameters.
irq_n_irqs = 32,
# Identifier parameters
# Identifier parameters.
ident = "",
ident_version = False,
# UART parameters
# UART parameters.
with_uart = True,
uart_name = "serial",
uart_baudrate = 115200,
uart_fifo_depth = 16,
# Timer parameters
# Timer parameters.
with_timer = True,
timer_uptime = False,
# Controller parameters
# Controller parameters.
with_ctrl = True,
# JTAGBone
# JTAGBone.
with_jtagbone = False,
jtagbone_chain = 1,
# UARTBone
# UARTBone.
with_uartbone = False,
# Others
# Watchdog.
with_watchdog = False,
watchdog_width = 32,
watchdog_reset_delay = None,
# Others.
**kwargs):
# New LiteXSoC class -----------------------------------------------------------------------
@ -138,7 +143,7 @@ class SoCCore(LiteXSoC):
irq_reserved_irqs = {},
)
# Attributes
# Attributes.
self.mem_regions = self.bus.regions
self.clk_freq = self.sys_clk_freq
self.mem_map = self.mem_map
@ -198,28 +203,29 @@ class SoCCore(LiteXSoC):
# JTAGBone and jtag_uart can't be used at the same time.
assert not (with_jtagbone and uart_name == "jtag_uart")
# UARTBone and serial can't be used at the same time.
assert not (with_uartbone and uart_name == "serial")
# Modules instances ------------------------------------------------------------------------
# Add SoCController
# Add SoCController.
if with_ctrl:
self.add_controller("ctrl")
# Add CPU
# Add CPU.
self.add_cpu(
name = str(cpu_type),
variant = "standard" if cpu_variant is None else cpu_variant,
reset_address = None if integrated_rom_size else cpu_reset_address,
cfu = cpu_cfu)
# Add User's interrupts
# Add User's interrupts.
if self.irq.enabled:
for name, loc in self.interrupt_map.items():
self.irq.add(name, loc)
# Add integrated ROM
# Add integrated ROM.
if integrated_rom_size:
self.add_rom("rom",
origin = self.cpu.reset_address,
@ -228,14 +234,14 @@ class SoCCore(LiteXSoC):
mode = integrated_rom_mode
)
# Add integrated SRAM
# Add integrated SRAM.
if integrated_sram_size:
self.add_ram("sram",
origin = self.mem_map["sram"],
size = integrated_sram_size,
)
# Add integrated MAIN_RAM (only useful when no external SRAM/SDRAM is available)
# Add integrated MAIN_RAM (only useful when no external SRAM/SDRAM is available).
if integrated_main_ram_size:
self.add_ram("main_ram",
origin = self.mem_map["main_ram"],
@ -243,36 +249,37 @@ class SoCCore(LiteXSoC):
contents = integrated_main_ram_init,
)
# Add Identifier
# Add Identifier.
if ident != "":
self.add_identifier("identifier", identifier=ident, with_build_time=ident_version)
# Add UARTBone
# Add UARTBone.
if with_uartbone:
self.add_uartbone(baudrate=uart_baudrate)
# Add UART
# Add UART.
if with_uart:
self.add_uart(name="uart", uart_name=uart_name, baudrate=uart_baudrate, fifo_depth=uart_fifo_depth)
# Add JTAGBone
# Add JTAGBone.
if with_jtagbone:
self.add_jtagbone(chain=jtagbone_chain)
# Add Timer
# Add Timer.
if with_timer:
self.add_timer(name="timer0")
if timer_uptime:
self.timer0.add_uptime()
# Add Watchdog.
if with_watchdog:
self.add_watchdog(name="watchdog0" ,width=watchdog_width, reset_delay=watchdog_reset_delay)
# Methods --------------------------------------------------------------------------------------
def add_csr(self, csr_name, csr_id=None, use_loc_if_exists=False):
self.csr.add(csr_name, csr_id, use_loc_if_exists=use_loc_if_exists)
def initialize_rom(self, data):
self.init_rom(name="rom", contents=data)
def add_memory_region(self, name, origin, length, type="cached"):
self.bus.add_region(name, SoCRegion(origin=origin, size=length,
cached="cached" in type,
@ -286,7 +293,7 @@ class SoCCore(LiteXSoC):
def soc_core_args(parser):
soc_group = parser.add_argument_group(title="SoC options")
# Bus parameters
# Bus parameters.
soc_group.add_argument("--bus-standard", default="wishbone", help="Select bus standard: {}.".format(", ".join(SoCBusHandler.supported_standard)))
soc_group.add_argument("--bus-data-width", default=32, type=auto_int, help="Bus data-width.")
soc_group.add_argument("--bus-address-width", default=32, type=auto_int, help="Bus address-width.")
@ -294,53 +301,58 @@ def soc_core_args(parser):
soc_group.add_argument("--bus-bursting", action="store_true", help="Enable burst cycles on the bus if supported.")
soc_group.add_argument("--bus-interconnect", default="shared", help="Select bus interconnect: shared (default) or crossbar.")
# CPU parameters
# CPU parameters.
soc_group.add_argument("--cpu-type", default="vexriscv", help="Select CPU: {}.".format(", ".join(iter(cpu.CPUS.keys()))))
soc_group.add_argument("--cpu-variant", default=None, help="CPU variant.")
soc_group.add_argument("--cpu-reset-address", default=None, type=auto_int, help="CPU reset address (Boot from Integrated ROM by default).")
soc_group.add_argument("--cpu-cfu", default=None, help="Optional CPU CFU file/instance to add to the CPU.")
# Controller parameters
# Controller parameters.
soc_group.add_argument("--no-ctrl", action="store_true", help="Disable Controller.")
# ROM parameters
# ROM parameters.
soc_group.add_argument("--integrated-rom-size", default=0x20000, type=auto_int, help="Size/Enable the integrated (BIOS) ROM (Automatically resized to BIOS size when smaller).")
soc_group.add_argument("--integrated-rom-init", default=None, type=str, help="Integrated ROM binary initialization file (override the BIOS when specified).")
# SRAM parameters
# SRAM parameters.
soc_group.add_argument("--integrated-sram-size", default=0x2000, type=auto_int, help="Size/Enable the integrated SRAM.")
# MAIN_RAM parameters
# MAIN_RAM parameters.
soc_group.add_argument("--integrated-main-ram-size", default=None, type=auto_int, help="size/enable the integrated main RAM.")
# CSR parameters
# CSR parameters.
soc_group.add_argument("--csr-data-width", default=32 , type=auto_int, help="CSR bus data-width (8 or 32).")
soc_group.add_argument("--csr-address-width", default=14, type=auto_int, help="CSR bus address-width.")
soc_group.add_argument("--csr-paging", default=0x800, type=auto_int, help="CSR bus paging.")
soc_group.add_argument("--csr-ordering", default="big", help="CSR registers ordering (big or little).")
# Identifier parameters
# Identifier parameters.
soc_group.add_argument("--ident", default=None, type=str, help="SoC identifier.")
soc_group.add_argument("--no-ident-version", action="store_true", help="Disable date/time in SoC identifier.")
# UART parameters
# UART parameters.
soc_group.add_argument("--no-uart", action="store_true", help="Disable UART.")
soc_group.add_argument("--uart-name", default="serial", type=str, help="UART type/name.")
soc_group.add_argument("--uart-baudrate", default=115200, type=auto_int, help="UART baudrate.")
soc_group.add_argument("--uart-fifo-depth", default=16, type=auto_int, help="UART FIFO depth.")
# UARTBone parameters
# UARTBone parameters.
soc_group.add_argument("--with-uartbone", action="store_true", help="Enable UARTbone.")
# JTAGBone parameters
# JTAGBone parameters.
soc_group.add_argument("--with-jtagbone", action="store_true", help="Enable Jtagbone support.")
soc_group.add_argument("--jtagbone-chain", default=1, type=int, help="Jtagbone chain index.")
# Timer parameters
# Timer parameters.
soc_group.add_argument("--no-timer", action="store_true", help="Disable Timer.")
soc_group.add_argument("--timer-uptime", action="store_true", help="Add an uptime capability to Timer.")
# L2 Cache
# Watchdog parameters.
soc_group.add_argument("--with-watchdog", action="store_true", help="Enable Watchdog.")
soc_group.add_argument("--watchdog-width", default=32, type=auto_int, help="Watchdog width.")
soc_group.add_argument("--watchdog-reset-delay", default=None, type=auto_int, help="Watchdog width.")
# L2 Cache.
soc_group.add_argument("--l2-size", default=8192, type=auto_int, help="L2 cache size.")
def soc_core_argdict(args):
@ -366,7 +378,7 @@ def soc_core_argdict(args):
r[a] = arg
return r
# SoCMini ---------------------------------------------------------------------------------------
# SoCMini ------------------------------------------------------------------------------------------
class SoCMini(SoCCore):
def __init__(self, *args, **kwargs):
@ -381,7 +393,7 @@ class SoCMini(SoCCore):
SoCCore.__init__(self, *args, **kwargs)
# SoCMini arguments -----------------------------------------------------------------------------
# SoCMini arguments --------------------------------------------------------------------------------
soc_mini_args = soc_core_args
soc_mini_argdict = soc_core_argdict

View File

@ -41,11 +41,12 @@ def ahb_description(data_width, address_width):
]
class AHBInterface(Record):
def __init__(self, data_width=32, address_width=32):
def __init__(self, data_width=32, address_width=32, addressing="byte"):
assert addressing == "byte"
Record.__init__(self, ahb_description(data_width, address_width))
self.data_width = data_width
self.address_width = address_width
self.addressing = "byte"
self.addressing = addressing
# AHB to Wishbone ---------------------------------------------------------------------------------

View File

@ -57,6 +57,7 @@ class AXILiteInterface:
# -----------
self.data_width = data_width
self.address_width = address_width
self.bursting = bursting
self.addressing = addressing
self.clock_domain = clock_domain
@ -148,7 +149,7 @@ class AXILiteRemapper(LiteXModule):
# AXI-Lite to Simple Bus ---------------------------------------------------------------------------
def axi_lite_to_simple(axi_lite, port_adr, port_dat_r, port_dat_w=None, port_we=None):
def axi_lite_to_simple(axi_lite, port_adr, port_dat_r, port_dat_w=None, port_re=None, port_we=None):
"""Connection of AXILite to simple bus with 1-cycle latency, such as CSR bus or Memory port"""
bus_data_width = axi_lite.data_width
adr_shift = log2_int(bus_data_width//8)
@ -168,6 +169,11 @@ def axi_lite_to_simple(axi_lite, port_adr, port_dat_r, port_dat_w=None, port_we=
else:
comb.append(port_we.eq(axi_lite.w.valid & axi_lite.w.ready & (axi_lite.w.strb != 0)))
if port_re is not None:
comb.append(port_re.eq(axi_lite.ar.valid & axi_lite.ar.ready))
port_adr_reg = Signal(len(port_adr))
fsm = FSM()
fsm.act("START-TRANSACTION",
# If the last access was a read, do a write, and vice versa.
@ -186,12 +192,24 @@ def axi_lite_to_simple(axi_lite, port_adr, port_dat_r, port_dat_w=None, port_we=
If(axi_lite.w.valid,
axi_lite.w.ready.eq(1),
NextState("SEND-WRITE-RESPONSE")
).Else(
# write data is not yet available - register the address
# and wait until the master provides the data
NextValue(port_adr_reg, port_adr),
NextState("WAIT-FOR-WRITE-DATA")
)
).Elif(do_read,
port_adr.eq(axi_lite.ar.addr[adr_shift:]),
NextState("LATCH-READ-RESPONSE"),
)
)
fsm.act("WAIT-FOR-WRITE-DATA",
port_adr.eq(port_adr_reg),
If(axi_lite.w.valid,
axi_lite.w.ready.eq(1),
NextState("SEND-WRITE-RESPONSE")
)
),
fsm.act("LATCH-READ-RESPONSE",
NextValue(port_dat_r_latched, port_dat_r),
NextState("SEND-READ-RESPONSE")
@ -219,6 +237,7 @@ def axi_lite_to_simple(axi_lite, port_adr, port_dat_r, port_dat_w=None, port_we=
# AXI-Lite SRAM ------------------------------------------------------------------------------------
class AXILiteSRAM(LiteXModule):
autocsr_exclude = {"mem"}
def __init__(self, mem_or_size, read_only=None, init=None, bus=None, name=None):
if bus is None:
bus = AXILiteInterface()
@ -240,8 +259,11 @@ class AXILiteSRAM(LiteXModule):
# # #
# Create memory port
port = self.mem.get_port(write_capable=not read_only, we_granularity=8,
mode=READ_FIRST if read_only else WRITE_FIRST)
port = self.mem.get_port(
write_capable = not read_only,
we_granularity = 8,
mode = READ_FIRST if read_only else WRITE_FIRST,
)
self.specials += self.mem, port
# Generate write enable signal
@ -257,7 +279,7 @@ class AXILiteSRAM(LiteXModule):
port_dat_r = port.dat_r,
port_dat_w = port.dat_w if not read_only else None,
port_we = port.we if not read_only else None)
self.fsm = fsm
self.submodules.fsm = fsm
self.comb += comb
# AXI-Lite Data-Width Converter --------------------------------------------------------------------

View File

@ -32,6 +32,7 @@ class AXILite2CSR(LiteXModule):
fsm, comb = axi_lite_to_simple(
axi_lite = self.axi_lite,
port_adr = self.csr.adr,
port_re = self.csr.re,
port_dat_r = self.csr.dat_r,
port_dat_w = self.csr.dat_w,
port_we = self.csr.we)

View File

@ -18,9 +18,11 @@ sys_clk domain of the SoC, completing writes in a single cycle and reads in two
Write in 1 cycle:
- adr/we/dat_w set by bridge.
adr
Read in 2 cycles:
Main SoC Bus CSR we - adr set by bridge
Bridge - dat_r set returned by user logic.
| | Read in 2 cycles:
re - adr and re set by bridge.
Main SoC Bus - User logic can ignore re if there is no reading side effect.
| CSR we - dat_r set returned by user logic.
Bridge
dat_w
dat_r
@ -46,6 +48,7 @@ from litex.soc.interconnect.csr import CSRStorage
_layout = [
("adr", "address_width", DIR_M_TO_S),
("re", 1, DIR_M_TO_S),
("we", 1, DIR_M_TO_S),
("dat_w", "data_width", DIR_M_TO_S),
("dat_r", "data_width", DIR_S_TO_M)
@ -81,9 +84,11 @@ class Interface(Record):
def read(self, adr):
yield self.adr.eq(adr)
yield self.re.eq(1)
yield
yield
return (yield self.dat_r)
value = (yield self.dat_r)
yield self.re.eq(0)
return value
# CSR Interconnect ---------------------------------------------------------------------------------
@ -97,6 +102,7 @@ class InterconnectShared(Module):
intermediate = Interface.like(masters[0])
self.comb += [
intermediate.adr.eq( Reduce("OR", [masters[i].adr for i in range(len(masters))])),
intermediate.re.eq( Reduce("OR", [masters[i].re for i in range(len(masters))])),
intermediate.we.eq( Reduce("OR", [masters[i].we for i in range(len(masters))])),
intermediate.dat_w.eq(Reduce("OR", [masters[i].dat_w for i in range(len(masters))]))
]
@ -207,8 +213,8 @@ class CSRBank(csr.GenericBank):
self.comb += [
c.r.eq(self.bus.dat_w[:c.size]),
If(sel & (self.bus.adr[:log2_int(aligned_paging)] == i),
c.re.eq( self.bus.we),
c.we.eq(~self.bus.we)
c.re.eq(self.bus.we),
c.we.eq(self.bus.re)
)
]

View File

@ -23,10 +23,10 @@ class Status(LiteXModule):
self.ongoing = Signal()
ongoing = Signal()
self.comb += If(endpoint.valid, self.last.eq(endpoint.last & endpoint.ready))
self.sync += ongoing.eq((endpoint.valid | ongoing) & ~self.last)
self.comb += self.last.eq(endpoint.valid & endpoint.last & endpoint.ready)
self.comb += self.ongoing.eq((endpoint.valid | ongoing) & ~self.last)
self.sync += [
ongoing.eq(self.ongoing),
If(self.last,
self.first.eq(1)
).Elif(endpoint.valid & endpoint.ready,
@ -101,7 +101,7 @@ class Dispatcher(LiteXModule):
# Header -------------------------------------------------------------------------------------------
class HeaderField:
def __init__(self, byte, offset, width):
def __init__(self, byte=0, offset=0, width=1):
self.byte = byte
self.offset = offset
self.width = width
@ -386,7 +386,7 @@ class PacketFIFO(LiteXModule):
sink.connect(param_fifo.sink, keep=set([e[0] for e in param_layout])),
sink.connect(payload_fifo.sink, keep=set([e[0] for e in payload_layout] + ["last"])),
param_fifo.sink.valid.eq(sink.valid & sink.last),
payload_fifo.sink.valid.eq(sink.valid & payload_fifo.sink.ready),
payload_fifo.sink.valid.eq(sink.valid & param_fifo.sink.ready),
sink.ready.eq(param_fifo.sink.ready & payload_fifo.sink.ready),
]

View File

@ -254,8 +254,16 @@ class ClockDomainCrossing(LiteXModule, DUID):
# Same Clk Domains.
if cd_from == cd_to:
# No adaptation.
self.comb += self.sink.connect(self.source)
if buffered:
# Add Buffer.
self.buffer = ClockDomainsRenamer(cd_from)(Buffer(layout))
self.comb += [
self.sink.connect(self.buffer.sink),
self.buffer.source.connect(self.source),
]
else:
# No adaptation.
self.comb += self.sink.connect(self.source)
# Different Clk Domains.
else:
if with_common_rst:
@ -688,19 +696,22 @@ class Monitor(LiteXModule):
# Generic Monitor Counter ------------------------------------------------------------------
class MonitorCounter(Module):
def __init__(self, reset, latch, enable, count):
_count = Signal.like(count)
_count_latched = Signal.like(count)
_count = Signal(len(count), reset_less=True)
_count_latched = Signal(len(count), reset_less=True)
_sync = getattr(self.sync, clock_domain)
_sync += [
# Count.
If(reset,
_count.eq(0),
_count_latched.eq(0),
).Elif(enable,
If(_count != (2**len(count)-1),
_count.eq(_count + 1)
)
),
If(latch,
# Latch.
If(reset,
_count_latched.eq(0),
).Elif(latch,
_count_latched.eq(_count)
)
]

View File

@ -90,6 +90,8 @@ class Interface(Record):
yield self.bte.eq(bte)
yield self.we.eq(1)
yield from self._do_transaction()
if (yield self.err):
raise ValueError("bus error")
def read(self, adr, cti=None, bte=None):
yield self.adr.eq(adr)
@ -99,6 +101,8 @@ class Interface(Record):
if bte is not None:
yield self.bte.eq(bte)
yield from self._do_transaction()
if (yield self.err):
raise ValueError("bus error")
return (yield self.dat_r)
def get_ios(self, bus_name="wb"):
@ -459,7 +463,8 @@ class Converter(LiteXModule):
# Wishbone SRAM ------------------------------------------------------------------------------------
class SRAM(Module): # FIXME: Switch to LiteXModule.
class SRAM(LiteXModule):
autocsr_exclude = {"mem"}
def __init__(self, mem_or_size, read_only=None, write_only=None, init=None, bus=None, name=None):
if bus is None:
bus = Interface(data_width=32, address_width=32, addressing="word")
@ -554,8 +559,11 @@ class SRAM(Module): # FIXME: Switch to LiteXModule.
# Memory.
# -------
port = self.mem.get_port(write_capable=not read_only, we_granularity=8,
mode=READ_FIRST if read_only else WRITE_FIRST)
port = self.mem.get_port(
write_capable = not read_only,
we_granularity = 8,
mode = READ_FIRST if read_only else WRITE_FIRST,
)
self.specials += self.mem, port
# Generate write enable signal
if not read_only:
@ -607,12 +615,14 @@ class Wishbone2CSR(LiteXModule):
NextValue(self.csr.dat_w, self.wishbone.dat_w),
If(self.wishbone.cyc & self.wishbone.stb,
NextValue(self.csr.adr, self.wishbone.adr[wishbone_adr_shift:]),
NextValue(self.csr.we, self.wishbone.we & (self.wishbone.sel != 0)),
NextValue(self.csr.re, ~self.wishbone.we & (self.wishbone.sel != 0)),
NextValue(self.csr.we, self.wishbone.we & (self.wishbone.sel != 0)),
NextState("WRITE-READ")
)
)
fsm.act("WRITE-READ",
NextValue(self.csr.adr, 0),
NextValue(self.csr.re, 0),
NextValue(self.csr.we, 0),
NextState("ACK")
)
@ -628,7 +638,8 @@ class Wishbone2CSR(LiteXModule):
self.csr.dat_w.eq(self.wishbone.dat_w),
If(self.wishbone.cyc & self.wishbone.stb,
self.csr.adr.eq(self.wishbone.adr[wishbone_adr_shift:]),
self.csr.we.eq(self.wishbone.we & (self.wishbone.sel != 0)),
self.csr.re.eq(~self.wishbone.we & (self.wishbone.sel != 0)),
self.csr.we.eq( self.wishbone.we & (self.wishbone.sel != 0)),
NextState("ACK")
)
)

View File

@ -18,18 +18,24 @@ extern unsigned int _ftext, _edata_rom;
#define NUMBER_OF_BYTES_ON_A_LINE 16
void dump_bytes(unsigned int *ptr, int count, unsigned long addr)
{
char *data = (char *)ptr;
uint32_t *dptr = (uint32_t *)ptr;
char data[NUMBER_OF_BYTES_ON_A_LINE];
int line_bytes = 0, i = 0;
fputs("Memory dump:", stdout);
while (count > 0) {
line_bytes =
(count > NUMBER_OF_BYTES_ON_A_LINE)?
NUMBER_OF_BYTES_ON_A_LINE : count;
for (i = 0; i < line_bytes; i+=4){
*((uint32_t*)&data[i]) = *(dptr++);
}
printf("\n0x%08lx ", addr);
for (i = 0; i < line_bytes; i++)
printf("%02x ", *(unsigned char *)(data+i));
printf("%02x ", (unsigned char)data[i]);
for (; i < NUMBER_OF_BYTES_ON_A_LINE; i++)
printf(" ");
@ -37,16 +43,15 @@ void dump_bytes(unsigned int *ptr, int count, unsigned long addr)
printf(" ");
for (i = 0; i<line_bytes; i++) {
if ((*(data+i) < 0x20) || (*(data+i) > 0x7e))
if ((data[i] < 0x20) || (data[i] > 0x7e))
printf(".");
else
printf("%c", *(data+i));
printf("%c", data[i]);
}
for (; i < NUMBER_OF_BYTES_ON_A_LINE; i++)
printf(" ");
data += (char)line_bytes;
count -= line_bytes;
addr += line_bytes;
}

View File

@ -38,6 +38,7 @@
#include <libbase/spiflash.h>
#include <libbase/uart.h>
#include <libbase/i2c.h>
#include <libbase/hyperram.h>
#include <liblitedram/sdram.h>
#include <liblitedram/utils.h>
@ -173,7 +174,11 @@ __attribute__((__used__)) int main(int i, char **c)
printf("\n");
#endif
sdr_ok = 1;
sdr_ok = 1;
#ifdef CSR_HYPERRAM_BASE
hyperram_init();
#endif
#if defined(CSR_ETHMAC_BASE) || defined(MAIN_RAM_BASE) || defined(CSR_SPIFLASH_CORE_BASE)
printf("--========== \e[1mInitialization\e[0m ============--\n");

View File

@ -11,7 +11,8 @@ OBJECTS = \
uart.o \
spiflash.o \
i2c.o \
isr.o
isr.o \
hyperram.o
all: libbase.a

View File

@ -0,0 +1,96 @@
// This file is Copyright (c) 2024 Florent Kermarrec <florent@enjoy-digital.fr>
// License: BSD
#include <stdio.h>
#include <libbase/hyperram.h>
#include <generated/csr.h>
#ifdef CSR_HYPERRAM_BASE
static void hyperram_write_reg(uint16_t reg_addr, uint16_t data) {
/* Write data to the register */
hyperram_reg_wdata_write(data);
hyperram_reg_control_write(
1 << CSR_HYPERRAM_REG_CONTROL_WRITE_OFFSET |
0 << CSR_HYPERRAM_REG_CONTROL_READ_OFFSET |
reg_addr << CSR_HYPERRAM_REG_CONTROL_ADDR_OFFSET
);
/* Wait for write to complete */
while ((hyperram_reg_status_read() & (1 << CSR_HYPERRAM_REG_STATUS_WRITE_DONE_OFFSET)) == 0);
}
static uint16_t hyperram_read_reg(uint16_t reg_addr) {
/* Read data from the register */
hyperram_reg_control_write(
0 << CSR_HYPERRAM_REG_CONTROL_WRITE_OFFSET |
1 << CSR_HYPERRAM_REG_CONTROL_READ_OFFSET |
reg_addr << CSR_HYPERRAM_REG_CONTROL_ADDR_OFFSET
);
/* Wait for read to complete */
while ((hyperram_reg_status_read() & (1 << CSR_HYPERRAM_REG_STATUS_READ_DONE_OFFSET)) == 0);
return hyperram_reg_rdata_read();
}
/* Configuration and Utility Functions */
static uint16_t hyperram_get_core_latency_setting(uint32_t clk_freq) {
/* Raw clock latency settings for the HyperRAM core */
if (clk_freq <= 85000000) return 3; /* 3 Clock Latency */
if (clk_freq <= 104000000) return 4; /* 4 Clock Latency */
if (clk_freq <= 133000000) return 5; /* 5 Clock Latency */
if (clk_freq <= 166000000) return 6; /* 6 Clock Latency */
if (clk_freq <= 250000000) return 7; /* 7 Clock Latency */
return 7; /* Default to highest latency for safety */
}
static uint16_t hyperram_get_chip_latency_setting(uint32_t clk_freq) {
/* LUT/Translated settings for the HyperRAM chip */
if (clk_freq <= 85000000) return 0b1110; /* 3 Clock Latency */
if (clk_freq <= 104000000) return 0b1111; /* 4 Clock Latency */
if (clk_freq <= 133000000) return 0b0000; /* 5 Clock Latency */
if (clk_freq <= 166000000) return 0b0001; /* 6 Clock Latency */
if (clk_freq <= 250000000) return 0b0010; /* 7 Clock Latency */
return 0b0010; /* Default to highest latency for safety */
}
static void hyperram_configure_latency(void) {
uint16_t config_reg_0 = 0x8f2f;
uint8_t core_clk_ratio;
uint16_t core_latency_setting;
uint16_t chip_latency_setting;
/* Compute Latency settings */
core_clk_ratio = (hyperram_status_read() >> CSR_HYPERRAM_STATUS_CLK_RATIO_OFFSET & 0xf);
printf("HyperRAM Clk Ratio %d:1.\n", core_clk_ratio);
core_latency_setting = hyperram_get_core_latency_setting(CONFIG_CLOCK_FREQUENCY/core_clk_ratio);
chip_latency_setting = hyperram_get_chip_latency_setting(CONFIG_CLOCK_FREQUENCY/core_clk_ratio);
/* Write Latency to HyperRAM Core */
printf("HyperRAM Core Latency: %d CK (X1).\n", core_latency_setting);
hyperram_config_write(core_latency_setting << CSR_HYPERRAM_CONFIG_LATENCY_OFFSET);
/* Enable Variable Latency on HyperRAM Chip */
if (hyperram_status_read() & 0x1)
config_reg_0 &= ~(0b1 << 3); /* Enable Variable Latency */
/* Update Latency on HyperRAM Chip */
config_reg_0 &= ~(0b1111 << 4);
config_reg_0 |= chip_latency_setting << 4;
/* Write Configuration Register 0 to HyperRAM Chip */
hyperram_write_reg(2, config_reg_0);
/* Read current configuration */
config_reg_0 = hyperram_read_reg(2);
printf("HyperRAM Configuration Register 0: %08x\n", config_reg_0);
}
void hyperram_init(void) {
printf("HyperRAM init...\n");
hyperram_configure_latency();
printf("\n");
}
#endif

View File

@ -0,0 +1,17 @@
// This file is Copyright (c) 2024 Florent Kermarrec <florent@enjoy-digital.fr>
// License: BSD
#ifndef __HYPERRAM_H
#define __HYPERRAM_H
#ifdef __cplusplus
extern "C" {
#endif
void hyperram_init(void);
#ifdef __cplusplus
}
#endif
#endif /* __HYPERRAM_H */

View File

@ -3,7 +3,6 @@
// This file is Copyright (c) 2020 Raptor Engineering, LLC <sales@raptorengineering.com>
// License: BSD
#include <generated/csr.h>
#include <generated/soc.h>
#include <irq.h>
@ -19,61 +18,84 @@ void isr(void);
#ifdef CONFIG_CPU_HAS_INTERRUPT
#if defined(__blackparrot__) /*TODO: Update this function for BP*/ //
void isr(void)
/*******************************************************/
/* Common Interrupt Table for All CPUs. */
/*******************************************************/
struct irq_table
{
static int onetime = 0;
if ( onetime == 0){
printf("ISR blackparrot\n");
printf("TRAP!!\n");
onetime++;
}
isr_t isr;
} irq_table[CONFIG_CPU_INTERRUPTS];
int irq_attach(unsigned int irq, isr_t isr)
{
if (irq >= CONFIG_CPU_INTERRUPTS) {
printf("Inv irq %d\n", irq);
return -1;
}
unsigned int ie = irq_getie();
irq_setie(0);
irq_table[irq].isr = isr;
irq_setie(ie);
return irq;
}
#elif defined(__rocket__) || defined(__openc906__)
#if defined(__openc906__)
#define PLIC_EXT_IRQ_BASE 16
#else
#define PLIC_EXT_IRQ_BASE 1
#endif
void plic_init(void);
int irq_detach(unsigned int irq)
{
return irq_attach(irq, NULL);
}
/***********************************************************/
/* ISR and PLIC Initialization for RISC-V PLIC-based CPUs. */
/***********************************************************/
#if defined(__riscv_plic__)
/* PLIC initialization. */
void plic_init(void)
{
int i;
int i;
/* Set priorities for the first 8 external interrupts to 1. */
for (i = 0; i < 8; i++)
*((unsigned int *)PLIC_BASE + PLIC_EXT_IRQ_BASE + i) = 1;
// priorities for first 8 external interrupts
for (i = 0; i < 8; i++)
*((unsigned int *)PLIC_BASE + PLIC_EXT_IRQ_BASE + i) = 1;
// enable first 8 external interrupts
*((unsigned int *)PLIC_ENABLED) = 0xff << PLIC_EXT_IRQ_BASE;
// set priority threshold to 0 (any priority > 0 triggers interrupt)
*((unsigned int *)PLIC_THRSHLD) = 0;
/* Enable the first 8 external interrupts. */
*((unsigned int *)PLIC_ENABLED) = 0xff << PLIC_EXT_IRQ_BASE;
/* Set priority threshold to 0 (any priority > 0 triggers an interrupt). */
*((unsigned int *)PLIC_THRSHLD) = 0;
}
/* Interrupt Service Routine. */
void isr(void)
{
unsigned int claim;
unsigned int claim;
while ((claim = *((unsigned int *)PLIC_CLAIM))) {
switch (claim - PLIC_EXT_IRQ_BASE) {
case UART_INTERRUPT:
uart_isr();
break;
default:
printf("## PLIC: Unhandled claim: %d\n", claim);
printf("# plic_enabled: %08x\n", irq_getmask());
printf("# plic_pending: %08x\n", irq_pending());
printf("# mepc: %016lx\n", csrr(mepc));
printf("# mcause: %016lx\n", csrr(mcause));
printf("# mtval: %016lx\n", csrr(mtval));
printf("# mie: %016lx\n", csrr(mie));
printf("# mip: %016lx\n", csrr(mip));
printf("###########################\n\n");
break;
}
*((unsigned int *)PLIC_CLAIM) = claim;
}
/* Claim and handle pending interrupts. */
while ((claim = *((unsigned int *)PLIC_CLAIM))) {
unsigned int irq = claim - PLIC_EXT_IRQ_BASE;
if (irq < CONFIG_CPU_INTERRUPTS && irq_table[irq].isr) {
irq_table[irq].isr();
} else {
/* Unhandled interrupt source, print diagnostic information. */
printf("## PLIC: Unhandled claim: %d\n", claim);
printf("# plic_enabled: %08x\n", irq_getmask());
printf("# plic_pending: %08x\n", irq_pending());
printf("# mepc: %016lx\n", csrr(mepc));
printf("# mcause: %016lx\n", csrr(mcause));
printf("# mtval: %016lx\n", csrr(mtval));
printf("# mie: %016lx\n", csrr(mie));
printf("# mip: %016lx\n", csrr(mip));
printf("###########################\n\n");
}
/* Acknowledge the interrupt. */
*((unsigned int *)PLIC_CLAIM) = claim;
}
}
#elif defined(__cv32e40p__)
/************************************************/
/* ISR Handling for CV32E40P and CV32E41P CPUs. */
/************************************************/
#elif defined(__cv32e40p__) || defined(__cv32e41p__)
#define FIRQ_OFFSET 16
#define IRQ_MASK 0x7FFFFFFF
@ -81,164 +103,131 @@ void isr(void)
#define ECALL 11
#define RISCV_TEST
/* Interrupt Service Routine. */
void isr(void)
{
unsigned int cause = csrr(mcause) & IRQ_MASK;
if (csrr(mcause) & 0x80000000) {
#ifndef UART_POLLING
if (cause == (UART_INTERRUPT+FIRQ_OFFSET)){
uart_isr();
/* Handle fast interrupts (FIRQ). */
unsigned int irq = cause - FIRQ_OFFSET;
if (irq < CONFIG_CPU_INTERRUPTS && irq_table[irq].isr) {
irq_table[irq].isr();
}
#endif
} else {
/* Handle regular exceptions and system calls. */
#ifdef RISCV_TEST
int gp;
asm volatile ("mv %0, gp" : "=r"(gp));
asm volatile("mv %0, gp" : "=r"(gp));
printf("E %d\n", cause);
if (cause == INVINST) {
printf("Inv Instr\n");
for(;;);
for (;;);
}
if (cause == ECALL) {
printf("Ecall (gp: %d)\n", gp);
csrw(mepc, csrr(mepc)+4);
csrw(mepc, csrr(mepc) + 4);
}
#endif
}
}
/*************************************/
/* ISR Handling for BlackParrot CPU. */
/*************************************/
#elif defined(__blackparrot__) /*TODO: Update this function for BP.*/
/* Interrupt Service Routine. */
void isr(void)
{
static int onetime = 0;
if (onetime == 0) {
printf("ISR blackparrot\n");
printf("TRAP!!\n");
onetime++;
}
}
/***********************************/
/* ISR Handling for Microwatt CPU. */
/***********************************/
#elif defined(__microwatt__)
void isr(uint64_t vec)
{
if (vec == 0x900)
return isr_dec();
if (vec == 0x900)
return isr_dec();
if (vec == 0x500) {
// Read interrupt source
uint32_t xirr = xics_icp_readw(PPC_XICS_XIRR);
uint32_t irq_source = xirr & 0x00ffffff;
if (vec == 0x500) {
/* Read interrupt source. */
uint32_t xirr = xics_icp_readw(PPC_XICS_XIRR);
uint32_t irq_source = xirr & 0x00ffffff;
__attribute__((unused)) unsigned int irqs;
__attribute__((unused)) unsigned int irqs;
// Handle IPI interrupts separately
if (irq_source == 2) {
// IPI interrupt
xics_icp_writeb(PPC_XICS_MFRR, 0xff);
}
else {
// External interrupt
irqs = irq_pending() & irq_getmask();
/* Handle IPI interrupts separately. */
if (irq_source == 2) {
/* IPI interrupt. */
xics_icp_writeb(PPC_XICS_MFRR, 0xff);
} else {
/* External interrupt. */
irqs = irq_pending() & irq_getmask();
#ifndef UART_POLLING
if(irqs & (1 << UART_INTERRUPT))
uart_isr();
#endif
}
if (irqs) {
const unsigned int irq = __builtin_ctz(irqs);
if (irq < CONFIG_CPU_INTERRUPTS && irq_table[irq].isr) {
irq_table[irq].isr();
} else {
irq_setmask(irq_getmask() & ~(1 << irq));
printf("\n*** disabled spurious irq %d ***\n", irq);
}
irqs &= irqs - 1; /* Clear this IRQ (the first bit set). */
}
}
// Clear interrupt
xics_icp_writew(PPC_XICS_XIRR, xirr);
/* Clear interrupt. */
xics_icp_writew(PPC_XICS_XIRR, xirr);
return;
}
return;
}
}
void isr_dec(void)
{
// For now, just set DEC back to a large enough value to slow the flood of DEC-initiated timer interrupts
mtdec(0x000000000ffffff);
}
#elif defined(__cva6__)
void plic_init(void);
void plic_init(void)
{
int i;
// priorities for interrupt pins 0...7
for (i = 0; i < 8; i++)
*((unsigned int *)PLIC_SOURCE_0 + i) = 1;
// enable interrupt pins 0...7 (M-mode)
*((unsigned int *)PLIC_M_ENABLE) = 0xff;
// set priority threshold to 0 (any priority > 0 triggers interrupt)
*((unsigned int *)PLIC_M_THRESHOLD) = 0;
}
void isr(void)
{
unsigned int claim;
while ((claim = *((unsigned int *)PLIC_M_CLAIM))) {
switch (claim - 1) {
case UART_INTERRUPT:
uart_isr();
break;
default:
printf("## PLIC: Unhandled claim: %d\n", claim);
printf("# plic_enabled: %08x\n", irq_getmask());
printf("# plic_pending: %08x\n", irq_pending());
printf("# mepc: %016lx\n", csrr(mepc));
printf("# mcause: %016lx\n", csrr(mcause));
printf("# mtval: %016lx\n", csrr(mtval));
printf("# mie: %016lx\n", csrr(mie));
printf("# mip: %016lx\n", csrr(mip));
printf("###########################\n\n");
break;
}
*((unsigned int *)PLIC_M_CLAIM) = claim;
}
/* Set DEC back to a large enough value to slow the flood of DEC-initiated timer interrupts. */
mtdec(0x000000000ffffff);
}
/*******************************************************/
/* Generic ISR Handling for CPUs with Interrupt Table. */
/*******************************************************/
#else
struct irq_table
{
isr_t isr;
} irq_table[CONFIG_CPU_INTERRUPTS];
int irq_attach(unsigned int irq, isr_t isr)
{
if (irq >= CONFIG_CPU_INTERRUPTS) {
printf("Inv irq %d\n", irq);
return -1;
}
unsigned int ie = irq_getie();
irq_setie(0);
irq_table[irq].isr = isr;
irq_setie(ie);
return irq;
}
int irq_detach(unsigned int irq)
{
return irq_attach(irq, NULL);
}
/* Interrupt Service Routine. */
void isr(void)
{
unsigned int irqs = irq_pending() & irq_getmask();
unsigned int irqs = irq_pending() & irq_getmask();
while (irqs)
{
const unsigned int irq = __builtin_ctz(irqs);
if ((irq < CONFIG_CPU_INTERRUPTS) && irq_table[irq].isr)
irq_table[irq].isr();
else {
irq_setmask(irq_getmask() & ~(1<<irq));
printf("\n*** disabled spurious irq %d ***\n", irq);
}
irqs &= irqs - 1; // clear this irq (the first bit set)
}
while (irqs) {
const unsigned int irq = __builtin_ctz(irqs);
if ((irq < CONFIG_CPU_INTERRUPTS) && irq_table[irq].isr)
irq_table[irq].isr();
else {
irq_setmask(irq_getmask() & ~(1 << irq));
printf("\n*** disabled spurious irq %d ***\n", irq);
}
irqs &= irqs - 1; /* Clear this IRQ (the first bit set). */
}
}
#endif
#else
#if defined(__microwatt__)
void isr(uint64_t vec){};
void isr(uint64_t vec) {};
#else
void isr(void){};
void isr(void) {};
#endif
#endif

View File

@ -74,23 +74,42 @@ void spiflash_dummy_bits_setup(unsigned int dummy_bits)
#ifdef CSR_SPIFLASH_CORE_MASTER_CS_ADDR
static void spiflash_len_mask_width_write(uint32_t len, uint32_t width, uint32_t mask)
{
uint32_t tmp = len & ((1 << CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_LEN_SIZE) - 1);
uint32_t word = tmp << CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_LEN_OFFSET;
tmp = width & ((1 << CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_WIDTH_SIZE) - 1);
word |= tmp << CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_WIDTH_OFFSET;
tmp = mask & ((1 << CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_MASK_SIZE) - 1);
word |= tmp << CSR_SPIFLASH_CORE_MASTER_PHYCONFIG_MASK_OFFSET;
spiflash_core_master_phyconfig_write(word);
}
static bool spiflash_tx_ready(void)
{
return (spiflash_core_master_status_read() >> CSR_SPIFLASH_CORE_MASTER_STATUS_TX_READY_OFFSET) & 1;
}
static bool spiflash_rx_ready(void)
{
return (spiflash_core_master_status_read() >> CSR_SPIFLASH_CORE_MASTER_STATUS_RX_READY_OFFSET) & 1;
}
static void spiflash_master_write(uint32_t val, size_t len, size_t width, uint32_t mask)
{
/* Be sure to empty RX queue before doing Xfer. */
while (spiflash_core_master_status_rx_ready_read())
while (spiflash_rx_ready())
spiflash_core_master_rxtx_read();
/* Configure Master */
spiflash_core_master_phyconfig_len_write(8 * len);
spiflash_core_master_phyconfig_mask_write(mask);
spiflash_core_master_phyconfig_width_write(width);
spiflash_len_mask_width_write(8*len, width, mask);
/* Set CS. */
spiflash_core_master_cs_write(1);
/* Do Xfer. */
spiflash_core_master_rxtx_write(val);
while (!spiflash_core_master_status_rx_ready_read());
while (!spiflash_rx_ready());
/* Clear CS. */
spiflash_core_master_cs_write(0);
@ -102,21 +121,19 @@ static volatile uint8_t r_buf[SPI_FLASH_BLOCK_SIZE + 4];
static uint32_t transfer_byte(uint8_t b)
{
/* wait for tx ready */
while (!spiflash_core_master_status_tx_ready_read());
while (!spiflash_tx_ready());
spiflash_core_master_rxtx_write((uint32_t)b);
/* wait for rx ready */
while (!spiflash_core_master_status_rx_ready_read());
while (!spiflash_rx_ready());
return spiflash_core_master_rxtx_read();
}
static void transfer_cmd(volatile uint8_t *bs, volatile uint8_t *resp, int len)
{
spiflash_core_master_phyconfig_len_write(8);
spiflash_core_master_phyconfig_width_write(1);
spiflash_core_master_phyconfig_mask_write(1);
spiflash_len_mask_width_write(8, 1, 1);
spiflash_core_master_cs_write(1);
flush_cpu_dcache();

View File

@ -7,55 +7,50 @@
# Copyright (c) 2020 Antmicro <www.antmicro.com>
# SPDX-License-Identifier: BSD-2-Clause
import os
import sys
import json
import argparse
import os
from litex.gen.common import KILOBYTE, MEGABYTE
def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_device=None, polling=False):
kB = 1024
mB = kB*1024
aliases = {}
# CPU Architectures ----------------------------------------------------------------------------
# CHECKME: Move to core and generate a constant for each CPU?
cpu_architectures = {
"mor1kx" : "or1k",
"marocchino" : "or1k",
"vexriscv" : "riscv",
"rocket" : "riscv",
"naxriscv" : "riscv",
}
# CPU Parameters -------------------------------------------------------------------------------
ncpus = int(d["constants"].get("config_cpu_count", 1))
cpu_name = d["constants"].get("config_cpu_name")
cpu_arch = cpu_architectures[cpu_name]
cpu_isa = d["constants"].get("config_cpu_isa", None)
cpu_mmu = d["constants"].get("config_cpu_mmu", None)
cpu_count = int(d["constants"].get("config_cpu_count", 1))
cpu_name = d["constants"].get("config_cpu_name")
cpu_family = d["constants"].get("config_cpu_family")
cpu_isa = d["constants"].get("config_cpu_isa", None)
cpu_mmu = d["constants"].get("config_cpu_mmu", None)
# Header ---------------------------------------------------------------------------------------
platform = d["constants"]["config_platform_name"]
dts = """
/dts-v1/;
/ {
/ {{
compatible = "litex,{platform}", "litex,soc";
model = "{identifier}";
#address-cells = <1>;
#size-cells = <1>;
"""
""".format(
platform=platform,
identifier=d["constants"].get("identifier", platform),
)
# Boot Arguments -------------------------------------------------------------------------------
# Init Ram Disk.
default_initrd_start = {
"or1k": 8*mB,
"riscv": 16*mB,
"or1k": 8 * MEGABYTE,
"riscv": 16 * MEGABYTE,
}
default_initrd_size = 8*mB
default_initrd_size = 8 * MEGABYTE
if initrd_start is None:
initrd_start = default_initrd_start[cpu_arch]
initrd_start = default_initrd_start[cpu_family]
if initrd_size is None:
initrd_size = default_initrd_size
@ -68,23 +63,28 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
initrd_enabled = True
initrd_size = os.path.getsize(initrd)
# if json constants contains localip ethernet has been enabled
if "localip1" in d["constants"]:
localip = '.'.join([str(d["constants"][f"localip{i}"]) for i in range(1,5)])
remoteip = '.'.join([str(d["constants"][f"remoteip{i}"]) for i in range(1,5)])
ip = f" ip={localip}:{remoteip}:{remoteip}:255.255.255.0::eth0:off:::"
else:
ip = ""
# Root Filesystem.
if root_device is None:
root_device = "ram0"
# Ethernet IP Address.
def get_eth_ip_config():
def get_ip_address(prefix):
return '.'.join(str(d["constants"][f"{prefix}{i+1}"]) for i in range(4))
ip_config = ""
if all(f"localip{i + 1}" in d["constants"] for i in range(4)):
local_ip = get_ip_address("localip")
remote_ip = get_ip_address("remoteip")
ip_config = f" ip={local_ip}:{remote_ip}:{remote_ip}:255.255.255.0::eth0:off:::"
return ip_config
# Bootargs Generation.
dts += """
chosen {{
bootargs = "{console} {rootfs}{ip}";""".format(
console = "console=liteuart earlycon=liteuart,0x{:x}".format(d["csr_bases"]["uart"]),
rootfs = "rootwait root=/dev/{}".format(root_device),
ip = ip)
ip = get_eth_ip_config())
if initrd_enabled is True:
dts += """
@ -97,21 +97,46 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
};
"""
# Clocks ------------------------------------------------------------------------------------------
# Clocks ---------------------------------------------------------------------------------------
dts += """
sys_clk: pll {{
for c in [c for c in d["constants"].keys() if c.endswith("config_clock_frequency")]:
name = c.removesuffix("config_clock_frequency") + "sys_clk"
dts += """
{name}: clock-{freq} {{
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <{sys_clk_freq}>;
clock-frequency = <{freq}>;
}};
""".format(sys_clk_freq=d["constants"]["config_clock_frequency"])
""".format(
name=name,
freq=d["constants"][c],
)
# CPU ------------------------------------------------------------------------------------------
# RISC-V
# ------
if cpu_arch == "riscv":
if cpu_family == "riscv":
def get_riscv_cpu_isa_base(cpu_isa):
return cpu_isa[:5]
def get_riscv_cpu_isa_extensions(cpu_isa, cpu_name):
isa_extensions = set(["i"])
# Collect common extensions.
common_extensions = {'i', 'm', 'a', 'f', 'd', 'c'}
for extension in cpu_isa[5:]:
if extension in common_extensions:
isa_extensions.update({extension})
# Add rocket-specific extensions.
if cpu_name == "rocket":
isa_extensions.update({"zicsr", "zifencei", "zihpm"})
# Format extensions.
return ", ".join(f"\"{extension}\"" for extension in sorted(isa_extensions))
# Cache description.
cache_desc = ""
if "config_cpu_dcache_size" in d["constants"]:
@ -137,6 +162,7 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
tlb_desc = ""
if "config_cpu_dtlb_size" in d["constants"]:
tlb_desc += """
tlb-split;
d-tlb-size = <{d_tlb_size}>;
d-tlb-sets = <{d_tlb_ways}>;
""".format(
@ -151,25 +177,23 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
i_tlb_ways = d["constants"]["config_cpu_itlb_ways"])
# Rocket specific attributes
if ("rocket" in cpu_name):
cpu_isa = cpu_isa.replace("2p0_", "")
if (cpu_name == "rocket"):
extra_attr = """
hardware-exec-breakpoint-count = <1>;
next-level-cache = <&memory>;
riscv,pmpgranularity = <4>;
riscv,pmpregions = <8>;
tlb-split;
"""
else:
extra_attr = ""
# CPU(s) Topology.
cpu_map = ""
if ncpus > 1:
if cpu_count > 1:
cpu_map += """
cpu-map {
cluster0 {"""
for cpu in range(ncpus):
for cpu in range(cpu_count):
cpu_map += """
core{cpu} {{
cpu = <&CPU{cpu}>;
@ -184,12 +208,14 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
#size-cells = <0>;
timebase-frequency = <{sys_clk_freq}>;
""".format(sys_clk_freq=d["constants"]["config_clock_frequency"])
for cpu in range(ncpus):
for cpu in range(cpu_count):
dts += """
CPU{cpu}: cpu@{cpu} {{
device_type = "cpu";
compatible = "riscv";
riscv,isa = "{cpu_isa}";
riscv,isa-base = "{cpu_isa_base}";
riscv,isa-extensions = {cpu_isa_extensions};
mmu-type = "riscv,{cpu_mmu}";
reg = <{cpu}>;
clock-frequency = <{sys_clk_freq}>;
@ -205,12 +231,14 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
}};
}};
""".format(cpu=cpu, irq=cpu,
sys_clk_freq = d["constants"]["config_clock_frequency"],
cpu_isa = cpu_isa,
cpu_mmu = cpu_mmu,
cache_desc = cache_desc,
tlb_desc = tlb_desc,
extra_attr = extra_attr)
sys_clk_freq = d["constants"]["config_clock_frequency"],
cpu_isa = cpu_isa,
cpu_isa_base = get_riscv_cpu_isa_base(cpu_isa), # Required for kernel >= 6.6.0
cpu_isa_extensions = get_riscv_cpu_isa_extensions(cpu_isa, cpu_name), # Required for kernel >= 6.6.0
cpu_mmu = cpu_mmu,
cache_desc = cache_desc,
tlb_desc = tlb_desc,
extra_attr = extra_attr)
dts += """
{cpu_map}
}};
@ -218,7 +246,7 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
# Or1k
# ----
elif cpu_arch == "or1k":
elif cpu_family == "or1k":
dts += """
cpus {{
#address-cells = <1>;
@ -305,7 +333,7 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
# Interrupt Controller -------------------------------------------------------------------------
if (cpu_arch == "riscv") and ("rocket" in cpu_name):
if (cpu_family == "riscv") and (cpu_name in ["rocket", "vexiiriscv"]):
# FIXME : L4 definitiion?
# CHECKME: interrupts-extended.
dts += """
@ -317,10 +345,10 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
reg-names = "control";
}};
""".format(
clint_base=d["memories"]["clint"]["base"],
cpu_mapping =("\n" + " "*20).join(["&L{} 3 &L{} 7".format(cpu, cpu) for cpu in range(ncpus)]))
if cpu_arch == "riscv":
if "rocket" in cpu_name:
clint_base = d["memories"]["clint"]["base"],
cpu_mapping = ("\n" + " "*20).join(["&L{} 3 &L{} 7".format(cpu, cpu) for cpu in range(cpu_count)]))
if cpu_family == "riscv":
if cpu_name == "rocket":
extra_attr = """
reg-names = "control";
riscv,max-priority = <7>;
@ -341,11 +369,11 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
{extra_attr}
}};
""".format(
plic_base =d["memories"]["plic"]["base"],
cpu_mapping =("\n" + " "*20).join(["&L{} 11 &L{} 9".format(cpu, cpu) for cpu in range(ncpus)]),
extra_attr =extra_attr)
plic_base = d["memories"]["plic"]["base"],
cpu_mapping = ("\n" + " "*20).join(["&L{} 11 &L{} 9".format(cpu, cpu) for cpu in range(cpu_count)]),
extra_attr = extra_attr)
elif cpu_arch == "or1k":
elif cpu_family == "or1k":
dts += """
intc0: interrupt-controller {
interrupt-controller;
@ -354,7 +382,7 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
status = "okay";
};
"""
if (cpu_arch == "riscv") and ("rocket" in cpu_name):
if (cpu_family == "riscv") and (cpu_name == "rocket"):
dts += """
dbg_ctl: debug-controller@0 {{
compatible = "sifive,debug-013", "riscv,debug-013";
@ -376,11 +404,12 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
reg-names = "mem";
}};
""".format(
cpu_mapping =("\n" + " "*20).join(["&L{} 0x3F".format(cpu) for cpu in range(ncpus)]))
cpu_mapping =("\n" + " "*20).join(["&L{} 0x3F".format(cpu) for cpu in range(cpu_count)]))
# UART -----------------------------------------------------------------------------------------
if "uart" in d["csr_bases"]:
aliases["serial0"] = "liteuart0"
it_incr = {True: 1, False: 0}[cpu_name == "rocket"]
dts += """
liteuart0: serial@{uart_csr_base:x} {{
compatible = "litex,liteuart";
@ -390,13 +419,17 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
}};
""".format(
uart_csr_base = d["csr_bases"]["uart"],
uart_interrupt = "" if polling else "interrupts = <{}>;".format(d["constants"]["uart_interrupt"]))
uart_interrupt = "" if polling else "interrupts = <{}>;".format(int(d["constants"]["uart_interrupt"]) + it_incr))
# Ethernet -------------------------------------------------------------------------------------
if "ethphy" in d["csr_bases"] and "ethmac" in d["csr_bases"]:
dts += """
mac0: mac@{ethmac_csr_base:x} {{
for i in [''] + list(range(0, 10)):
idx = (0 if i == '' else i)
ethphy_name = "ethphy" + str(i)
ethmac_name = "ethmac" + str(i)
it_incr = {True: 1, False: 0}[cpu_name == "rocket"]
if ethphy_name in d["csr_bases"] and ethmac_name in d["csr_bases"]:
dts += """
mac{idx}: mac@{ethmac_csr_base:x} {{
compatible = "litex,liteeth";
reg = <0x{ethmac_csr_base:x} 0x7c>,
<0x{ethphy_csr_base:x} 0x0a>,
@ -409,14 +442,15 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
status = "okay";
}};
""".format(
ethphy_csr_base = d["csr_bases"]["ethphy"],
ethmac_csr_base = d["csr_bases"]["ethmac"],
ethmac_mem_base = d["memories"]["ethmac"]["base"],
ethmac_mem_size = d["memories"]["ethmac"]["size"],
ethmac_rx_slots = d["constants"]["ethmac_rx_slots"],
ethmac_tx_slots = d["constants"]["ethmac_tx_slots"],
ethmac_slot_size = d["constants"]["ethmac_slot_size"],
ethmac_interrupt = "" if polling else "interrupts = <{}>;".format(d["constants"]["ethmac_interrupt"]))
idx = idx,
ethphy_csr_base = d["csr_bases"][ethphy_name],
ethmac_csr_base = d["csr_bases"][ethmac_name],
ethmac_mem_base = d["memories"][ethmac_name]["base"],
ethmac_mem_size = d["memories"][ethmac_name]["size"],
ethmac_rx_slots = d["constants"][ethmac_name + "_rx_slots"],
ethmac_tx_slots = d["constants"][ethmac_name + "_tx_slots"],
ethmac_slot_size = d["constants"][ethmac_name + "_slot_size"],
ethmac_interrupt = "" if polling else "interrupts = <{}>;".format(int(d["constants"][ethmac_name + "_interrupt"]) + it_incr))
# USB OHCI -------------------------------------------------------------------------------------
@ -599,6 +633,25 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
}};
""".format(xadc_csr_base=d["csr_bases"]["xadc"])
# CAN ------------------------------------------------------------------------------------------
for mem in d["memories"]:
if "can" in mem:
dts += """
{name}: can@{can_mem_base:x} {{
compatible = "ctu,ctucanfd";
reg = <0x{can_mem_base:x} 0x{can_mem_size:x}>;
interrupt-parent = <&intc0>;
interrupts = <{can_interrupt}>;
clocks = <&sys_clk>;
status = "okay";
}};
""".format(name=mem,
can_mem_base=d["memories"][mem]["base"],
can_mem_size=d["memories"][mem]["size"],
can_interrupt = int(d["constants"][f"{mem}_interrupt"]),
)
# Framebuffer ----------------------------------------------------------------------------------
if "video_framebuffer" in d["csr_bases"]:
@ -694,14 +747,14 @@ def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_devic
litex,clkout-divide-max = <{clkout_divide_range[1]}>;
litex,vco-margin = <{vco_margin}>;
""".format(
mmcm_lock_timeout = d["constants"]["mmcm_lock_timeout"],
mmcm_drdy_timeout = d["constants"]["mmcm_drdy_timeout"],
sys_clk = d["constants"]["config_clock_frequency"],
mmcm_lock_timeout = d["constants"]["mmcm_lock_timeout"],
mmcm_drdy_timeout = d["constants"]["mmcm_drdy_timeout"],
sys_clk = d["constants"]["config_clock_frequency"],
divclk_divide_range = (d["constants"]["divclk_divide_range_min"], d["constants"]["divclk_divide_range_max"]),
clkfbout_mult_frange = (d["constants"]["clkfbout_mult_frange_min"], d["constants"]["clkfbout_mult_frange_max"]),
vco_freq_range = (d["constants"]["vco_freq_range_min"], d["constants"]["vco_freq_range_max"]),
clkout_divide_range = (d["constants"]["clkout_divide_range_min"], d["constants"]["clkout_divide_range_max"]),
vco_margin = d["constants"]["vco_margin"])
vco_margin = d["constants"]["vco_margin"])
for clkout_nr in range(nclkout):
dts += add_clkout(clkout_nr,
d["constants"]["clkout_def_freq"],

View File

@ -104,6 +104,10 @@ def dts_reg_names(regs):
def disabled_handler(name, parm, csr):
return indent('status = "disabled";\n')
def cpu_handler(name, parm, csr):
return indent("clock-frequency = <{}>;\n".format(
csr['constants']['config_clock_frequency']
))
def ram_handler(name, parm, csr):
mem_reg = {
@ -177,6 +181,51 @@ def i2s_handler(name, parm, csr):
return dtsi
def spimaster_handler(name, parm, csr):
registers = get_registers_of(name, csr)
if len(registers) == 0:
raise KeyError
dtsi = dts_reg(registers)
dtsi += dts_reg_names(registers)
dtsi += indent("clock-frequency = <{}>;\n".format(
csr['constants'][name + '_frequency']))
dtsi += indent("data-width = <{}>;\n".format(
csr['constants'][name + '_data_width']))
dtsi += indent("max-cs = <{}>;\n".format(
csr['constants'][name + '_max_cs']))
return dtsi
def spiflash_handler(name, parm, csr):
registers = get_registers_of(name, csr)
if len(registers) == 0:
raise KeyError
# Add memory mapped region for spiflash, the linker script in zephyr expects this region to be
# the entry with the name flash_mmap in the reg property of the spi controller.
try:
registers.append({
'addr': csr['memories'][name]['base'],
'size': csr['memories'][name]['size'],
'name': 'flash_mmap',
})
except KeyError as e:
print('memory mapped', e, 'not found')
dtsi = dts_reg(registers)
dtsi += dts_reg_names(registers)
dtsi += indent("clock-frequency = <{}>;\n".format(
csr['constants'][name + '_phy_frequency']))
return dtsi
def peripheral_handler(name, parm, csr):
registers = get_registers_of(name, csr)
if len(registers) == 0:
@ -193,71 +242,79 @@ def peripheral_handler(name, parm, csr):
overlay_handlers = {
'cpu': {
'handler': cpu_handler,
'alias': 'cpu0',
},
'ctrl': {
'handler': peripheral_handler,
'alias': 'ctrl0',
},
'uart': {
'handler': peripheral_handler,
'alias': 'uart0',
'config_entry': 'UART_LITEUART'
},
'timer0': {
'handler': peripheral_handler,
'config_entry': 'LITEX_TIMER'
},
'ethmac': {
'handler': ethmac_handler,
'alias': 'eth0',
'config_entry': 'ETH_LITEETH'
},
'spimaster': {
'handler': spimaster_handler,
'alias': 'spi0',
},
'spiflash': {
'handler': peripheral_handler,
'alias': 'spi0',
'config_entry': 'SPI_LITESPI'
'handler': spiflash_handler,
'alias': 'spi1',
},
'sdcard_block2mem': {
'handler': peripheral_handler,
'alias': 'sdcard_block2mem',
'size': 0x18,
'config_entry': 'SD_LITESD'
'disable_handler': False,
},
'sdcard_core': {
'handler': peripheral_handler,
'alias': 'sdcard_core',
'size': 0x2C,
'config_entry': 'SD_LITESD'
'disable_handler': False,
},
'sdcard_irq': {
'handler': peripheral_handler,
'alias': 'sdcard_irq',
'size': 0x0C,
'config_entry': 'SD_LITESD'
'disable_handler': False,
},
'sdcard_mem2block': {
'handler': peripheral_handler,
'alias': 'sdcard_mem2block',
'size': 0x18,
'config_entry': 'SD_LITESD'
'disable_handler': False,
},
'sdcard_phy': {
'handler': peripheral_handler,
'alias': 'sdcard_phy',
'size': 0x10,
'config_entry': 'SD_LITESD'
'disable_handler': False,
},
'i2c0' : {
'handler': i2c_handler,
'config_entry': 'I2C_LITEX'
},
'i2s_rx' : {
'handler': i2s_handler,
'config_entry': 'I2S_LITEX'
},
'i2s_tx' : {
'handler': i2s_handler,
'config_entry': 'I2S_LITEX'
},
'watchdog0': {
'handler': peripheral_handler,
'alias': 'wdt0',
},
'mmcm' : {
'alias': 'clock0',
'handler': peripheral_handler,
'config_entry': 'CLOCK_CONTROL_LITEX'
},
'main_ram': {
'handler': ram_handler,
@ -281,10 +338,14 @@ def generate_dts_config(csr):
try:
dtsi += parm['handler'](name, parm, csr)
except KeyError as e:
print(' dtsi key', e, 'not found, disable', name)
enable = 'n'
dtsi += disabled_handler(name, parm, csr)
if parm.get('disable_handler', True):
print(' dtsi key', e, 'not found, disable', name)
dtsi += disabled_handler(name, parm, csr)
else:
print(' dtsi key', e, 'not found, skip', name)
continue
dtsi += dts_close()
dts += dtsi
if 'config_entry' in parm:

View File

@ -219,23 +219,29 @@ def get_cpu_count(csr):
vexriscv_common_kind = {
'name': 'VexRiscv',
'variants': {
'linux': {
'properties': ['cpuType: "rv32ima"', 'privilegeArchitecture: PrivilegeArchitecture.Priv1_10'],
'minimal': {
'properties': ['cpuType: "rv32i_zicsr_zifencei"'],
},
'i': {
'properties': ['cpuType: "rv32i"'],
'lite': {
'properties': ['cpuType: "rv32im_zicsr_zifencei"'],
},
'im': {
'properties': ['cpuType: "rv32im"'],
},
'ima': {
'properties': ['cpuType: "rv32ima"'],
'standard': {
'properties': ['cpuType: "rv32im_zicsr_zifencei"'],
},
'imac': {
'properties': ['cpuType: "rv32imac"'],
'properties': ['cpuType: "rv32imac_zicsr_zifencei"'],
},
'full': {
'properties': ['cpuType: "rv32im_zicsr_zifencei"'],
},
'linux': {
'properties': ['cpuType: "rv32ima_zicsr_zifencei"'],
},
'secure': {
'properties': ['cpuType: "rv32ima_zicsr_zifencei"'],
},
'others': {
'properties': ['cpuType: "rv32im"'],
'properties': ['cpuType: "rv32im_zicsr_zifencei"'],
}
},
'supports_time_provider': True,
@ -900,6 +906,12 @@ sysbus LoadBinary @{} {}
for cpu_id in range(0, number_of_cores):
result += f"cpu{cpu_id} PC {hex(rom_base)}\n"
if args.bios_elf:
# load LiteX BIOS to ROM base
result += """
sysbus LoadELF @{}
""".format(args.bios_elf)
if args.tftp_ip:
result += """
@ -1057,6 +1069,8 @@ def parse_args():
bios_group = parser.add_mutually_exclusive_group()
bios_group.add_argument('--bios-binary', action='store',
help='Path to the BIOS binary')
bios_group.add_argument('--bios-elf', action='store',
help='Path to the BIOS ELF file')
bios_group.add_argument('--opensbi-binary', action='store',
help='Path to the OpenSBI binary')

Some files were not shown because too many files have changed in this diff Show More