Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
6ccd314b2This file is pinned to the selected release. Download the original to open it in your design tool.
#!/usr/bin/env python3
#
# This file is part of LiteX.
#
# Copyright (c) 2019-2020 Florent Kermarrec <florent@enjoy-digital.fr>
# Copyright (c) 2020 Antmicro <www.antmicro.com>
# SPDX-License-Identifier: BSD-2-Clause
import os
import re
import sys
import json
import argparse
import re
from litex.gen.common import KILOBYTE, MEGABYTE
def generate_dts_interrupt(d, intr, polling):
if polling:
return ""
elif ("aplic_m" in d["memories"]) or ("aplic_s" in d["memories"]):
return "interrupts = <{} 0x4>;".format(intr)
else:
return "interrupts = <{}>;".format(intr)
def generate_dts_intc(d):
if "aplic_s" in d["memories"]:
return "intc_s"
elif "aplic_m" in d["memories"]:
return "intc_m"
else:
return "intc0"
def generate_dts_framebuffer_format(depth):
if (depth == 1):
return "mono1"
elif (depth == 16):
return "r5g6b5"
else:
return "a8b8g8r8"
def generate_dts_framebuffer_stride(width, depth):
return (width*depth + 7)//8
def get_dts_int(value):
return int(value, 0) if isinstance(value, str) else int(value)
def get_dts_constant(d, name, default):
return get_dts_int(d.get("constants", {}).get(name, default))
def get_dts_csr_ordering(d):
constants = d.get("constants", {})
little = "config_csr_ordering_little" in constants
big = "config_csr_ordering_big" in constants
if little and big:
raise ValueError("CSR ordering cannot be both big and little")
return "little" if little else "big"
def get_dts_csr_endian(d):
return "{}-endian;".format(get_dts_csr_ordering(d))
def generate_dts_hwmon_temperature(d, hwmon_index=0):
intel_a10_c10gx = {
"compatibles": [
"litex,hwmon-intel-a10-c10gx",
"litex,hwmon-intel-temp",
"litex,hwmon-temp",
],
"temp_scale": 693000,
"temp_divisor": 1024,
"temp_offset": 265000,
"temp_signed_bits": 0,
}
hwmon_cores = {
"GowinAroraVTemperatureSensor": {
"compatibles": [
"litex,hwmon-gowin-arora-v",
"litex,hwmon-temp",
],
"temp_scale": 250,
"temp_divisor": 1,
"temp_offset": 0,
"temp_signed_bits": 14,
},
"IntelA10C10GXTemperatureSensor": intel_a10_c10gx,
"IntelTemperatureSensor": intel_a10_c10gx,
"IntelLegacyTemperatureSensor": {
"compatibles": [
"litex,hwmon-intel-legacy",
"litex,hwmon-intel-temp",
"litex,hwmon-temp",
],
"temp_scale": 1000,
"temp_divisor": 1,
"temp_offset": 128000,
"temp_signed_bits": 0,
},
"LiteXTemperatureMonitor": {
"compatibles": [
"litex,hwmon-temp",
],
"temp_scale": 1,
"temp_divisor": 1,
"temp_offset": 0,
"temp_signed_bits": 0,
},
}
dts = ""
csr_bases = d.get("csr_bases", {})
csr_registers = d.get("csr_registers", {})
csr_data_width = get_dts_constant(d, "config_csr_data_width", 32)
csr_data_bytes = max(1, csr_data_width // 8)
for name, core in sorted(d.get("cores", {}).items()):
if name not in csr_bases:
continue
if core not in hwmon_cores:
continue
defaults = hwmon_cores[core]
csr_base = get_dts_int(csr_bases[name])
reg = csr_registers.get(name + "_temperature")
if reg is None:
temperature_offset = 0x00
temperature_size = csr_data_bytes
else:
temperature_offset = get_dts_int(reg["addr"]) - csr_base
temperature_size = get_dts_int(reg.get("size", 1)) * csr_data_bytes
csr_size = max(0x04, temperature_offset + temperature_size)
def get_hwmon_constant(field):
return get_dts_constant(d, name + "_hwmon_" + field, defaults[field])
temp_signed_bits = get_hwmon_constant("temp_signed_bits")
temp_signed_dts = ""
if temp_signed_bits:
temp_signed_dts = """
litex,temperature-signed-bits = <{temp_signed_bits}>;""".format(
temp_signed_bits = temp_signed_bits)
compatibles = ", ".join("\"{}\"".format(c) for c in defaults["compatibles"])
dts += """
hwmon{hwmon_index}: hwmon@{csr_base:x} {{
compatible = {compatibles};
reg = <0x{csr_base:x} 0x{csr_size:x}>;
litex,temperature-csr-offset = <0x{temperature_offset:x}>;
litex,temperature-mul = <{temp_scale}>;
litex,temperature-div = <{temp_divisor}>;
litex,temperature-offset = <{temp_offset}>;{temp_signed_dts}
status = "okay";
}};
""".format(
hwmon_index = hwmon_index,
csr_base = csr_base,
csr_size = csr_size,
compatibles = compatibles,
temperature_offset = temperature_offset,
temp_scale = get_hwmon_constant("temp_scale"),
temp_divisor = get_hwmon_constant("temp_divisor"),
temp_offset = get_hwmon_constant("temp_offset"),
temp_signed_dts = temp_signed_dts,
)
hwmon_index += 1
return dts
def generate_dts_xadc(d):
if "xadc" not in d["csr_bases"]:
return ""
xadc_core = d.get("cores", {}).get("xadc", "XADC")
compatibles = {
"USSystemMonitor": "\"litex,hwmon-xadc-us\", \"litex,hwmon-xadc\"",
"USPSystemMonitor": "\"litex,hwmon-xadc-usp\", \"litex,hwmon-xadc\"",
"ZynqUSPSystemMonitor": "\"litex,hwmon-xadc-zynqusp\", \"litex,hwmon-xadc-usp\", \"litex,hwmon-xadc\"",
}.get(xadc_core, "\"litex,hwmon-xadc\"")
defaults = {
"S7SystemMonitor": {
"temp_scale": 503975,
"temp_divisor": 4096,
"temp_offset": 273150,
"voltage_scale": 3000,
"voltage_divisor": 4096,
},
"XADC": {
"temp_scale": 503975,
"temp_divisor": 4096,
"temp_offset": 273150,
"voltage_scale": 3000,
"voltage_divisor": 4096,
},
"USSystemMonitor": {
"temp_scale": 503975,
"temp_divisor": 1024,
"temp_offset": 273150,
"voltage_scale": 3000,
"voltage_divisor": 1024,
},
"USPSystemMonitor": {
"temp_scale": 507592,
"temp_divisor": 1024,
"temp_offset": 279427,
"voltage_scale": 3000,
"voltage_divisor": 1024,
},
"ZynqUSPSystemMonitor": {
"temp_scale": 507592,
"temp_divisor": 1024,
"temp_offset": 279427,
"voltage_scale": 3000,
"voltage_divisor": 1024,
},
}.get(xadc_core, {
"temp_scale": 503975,
"temp_divisor": 4096,
"temp_offset": 273150,
"voltage_scale": 3000,
"voltage_divisor": 4096,
})
xadc_csr_base = get_dts_int(d["csr_bases"]["xadc"])
csr_data_width = get_dts_constant(d, "config_csr_data_width", 32)
csr_data_bytes = max(1, csr_data_width // 8)
def get_csr_offset(name, default):
reg = d.get("csr_registers", {}).get(name)
if reg is None:
return default
return get_dts_int(reg["addr"]) - xadc_csr_base
def get_csr_size(name):
reg = d.get("csr_registers", {}).get(name)
if reg is None:
return 0
return get_dts_int(reg.get("size", 1)) * csr_data_bytes
offsets = {
"temperature": get_csr_offset("xadc_temperature", 0x00),
"vccint": get_csr_offset("xadc_vccint", 0x08),
"vccaux": get_csr_offset("xadc_vccaux", 0x10),
"vccbram": get_csr_offset("xadc_vccbram", 0x18),
}
xadc_csr_size = 0x20
for name, offset in offsets.items():
xadc_csr_size = max(xadc_csr_size, offset + get_csr_size("xadc_" + name))
def get_hwmon_constant(name):
return get_dts_constant(d, "xadc_hwmon_" + name, defaults[name])
return """
hwmon0: xadc@{xadc_csr_base:x} {{
compatible = {compatibles};
reg = <0x{xadc_csr_base:x} 0x{xadc_csr_size:x}>;
litex,temperature-csr-offset = <0x{temperature_offset:x}>;
litex,vccint-csr-offset = <0x{vccint_offset:x}>;
litex,vccaux-csr-offset = <0x{vccaux_offset:x}>;
litex,vccbram-csr-offset = <0x{vccbram_offset:x}>;
litex,temperature-mul = <{temp_scale}>;
litex,temperature-div = <{temp_divisor}>;
litex,temperature-offset = <{temp_offset}>;
litex,voltage-mul = <{voltage_scale}>;
litex,voltage-div = <{voltage_divisor}>;
status = "okay";
}};
""".format(
xadc_csr_base = xadc_csr_base,
xadc_csr_size = xadc_csr_size,
compatibles = compatibles,
temperature_offset = offsets["temperature"],
vccint_offset = offsets["vccint"],
vccaux_offset = offsets["vccaux"],
vccbram_offset = offsets["vccbram"],
temp_scale = get_hwmon_constant("temp_scale"),
temp_divisor = get_hwmon_constant("temp_divisor"),
temp_offset = get_hwmon_constant("temp_offset"),
voltage_scale = get_hwmon_constant("voltage_scale"),
voltage_divisor = get_hwmon_constant("voltage_divisor"),
)
def generate_dts_sdcard(d, polling=False):
if "sdcard" not in d["csr_bases"]:
return ""
csr_bases = d["csr_bases"]
csr_registers = d["csr_registers"]
def get_csr_addr(name):
return get_dts_int(csr_registers[name]["addr"])
phy_base = get_csr_addr("sdcard_phy_card_detect")
core_base = get_csr_addr("sdcard_core_cmd_argument")
block2mem = get_csr_addr("sdcard_block2mem_dma_base")
mem2block = get_csr_addr("sdcard_mem2block_dma_base")
irq_base = get_csr_addr("sdcard_ev_status")
phy_size = core_base - phy_base
core_size = block2mem - core_base
block2mem_size = mem2block - block2mem
mem2block_size = irq_base - mem2block
return """
mmc0: mmc@{mmc_csr_base:x} {{
compatible = "litex,mmc";
reg = <0x{sdcard_phy_csr_base:x} 0x{sdcard_phy_csr_size:x}>,
<0x{sdcard_core_csr_base:x} 0x{sdcard_core_csr_size:x}>,
<0x{sdcard_block2mem:x} 0x{sdcard_block2mem_size:x}>,
<0x{sdcard_mem2block:x} 0x{sdcard_mem2block_size:x}>,
<0x{sdcard_irq:x} 0x100>;
reg-names = "phy", "core", "reader", "writer", "irq";
clocks = <&sys_clk>;
vmmc-supply = <&vreg_mmc>;
bus-width = <0x04>;
{sdcard_irq_interrupt}
{sdcard_csr_endian}
status = "okay";
}};
""".format(
mmc_csr_base = get_dts_int(csr_bases["sdcard"]),
sdcard_phy_csr_base = phy_base,
sdcard_phy_csr_size = phy_size,
sdcard_core_csr_base = core_base,
sdcard_core_csr_size = core_size,
sdcard_block2mem = block2mem,
sdcard_block2mem_size = block2mem_size,
sdcard_mem2block = mem2block,
sdcard_mem2block_size = mem2block_size,
sdcard_irq = irq_base,
sdcard_irq_interrupt = generate_dts_interrupt(d, d["constants"]["sdcard_interrupt"], polling),
sdcard_csr_endian = get_dts_csr_endian(d),
)
def generate_dts(d, initrd_start=None, initrd_size=None, initrd=None, root_device=None, polling=False):
aliases = {}
# CPU Parameters -------------------------------------------------------------------------------
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)
polling = polling or cpu_name == "gowin_ae350"
# 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 * MEGABYTE,
"riscv": 16 * MEGABYTE,
}
default_initrd_size = 8 * MEGABYTE
if initrd_start is None:
initrd_start = default_initrd_start[cpu_family]
if initrd_size is None:
initrd_size = default_initrd_size
if initrd == "enabled" or initrd is None:
initrd_enabled = True
elif initrd == "disabled":
initrd_enabled = False
else:
initrd_enabled = True
initrd_size = os.path.getsize(initrd)
# 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 = get_eth_ip_config())
if initrd_enabled is True:
dts += """
linux,initrd-start = <0x{linux_initrd_start:x}>;
linux,initrd-end = <0x{linux_initrd_end:x}>;""".format(
linux_initrd_start = d["memories"]["main_ram"]["base"] + initrd_start,
linux_initrd_end = d["memories"]["main_ram"]["base"] + initrd_start + initrd_size)
dts += """
};
"""
# Clocks ---------------------------------------------------------------------------------------
for c in [c for c in d["constants"].keys() if c.endswith("config_clock_frequency")]:
name = c[:len(c) - len("config_clock_frequency")] + "sys_clk"
dts += """
{name}: clock-{freq} {{
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <{freq}>;
}};
""".format(
name=name,
freq=d["constants"][c],
)
# CPU ------------------------------------------------------------------------------------------
# RISC-V
# ------
if cpu_family == "riscv":
def get_riscv_cpu_isa_base(cpu_isa):
return cpu_isa[:5]
def get_riscv_cpu_isa_extensions_by_string(isa):
VERSION_PATTERN = re.compile(r"\d+p\d+$")
extensions = set()
length = len(isa)
isa = isa.lower()
if not (isa.startswith("rv64") or isa.startswith("rv32")):
raise ValueError("ISA string must start with rv32|rv64")
slices = isa[4:].lower().split('_')
for slice in slices:
if not slice.isalnum():
raise ValueError(f"Extension ${slice} can not be parsed: invalid character")
if len(slice) == 1 or (slice[0] == 's') or (slice[0] == 'z'):
match = VERSION_PATTERN.search(slice)
if not match is None:
slice = slice[:match.span()[0]]
extensions.add(slice)
continue
match = VERSION_PATTERN.search(slice)
if not match is None:
if len(slice[:match.span()[0]]) > 1:
raise ValueError(f"Invalid extension string ${slice}: version can only be used separately")
slice = slice[:match.span()[0]]
if not slice.isalpha():
raise ValueError(f"Single letter extension string ${slice} should not contain other characters")
for extension in slice:
extensions.add(extension)
return extensions
def get_riscv_cpu_isa_extensions(cpu_isa, cpu_name):
isa_extensions = get_riscv_cpu_isa_extensions_by_string(cpu_isa)
# 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"]:
dcache_sets = int(d["constants"]["config_cpu_dcache_size"] /
d["constants"]["config_cpu_dcache_block_size"] /
d["constants"]["config_cpu_dcache_ways"])
cache_desc += """
d-cache-size = <{d_cache_size}>;
d-cache-sets = <{d_cache_sets}>;
d-cache-block-size = <{d_cache_block_size}>;
""".format(
d_cache_size = d["constants"]["config_cpu_dcache_size"],
d_cache_sets = dcache_sets,
d_cache_block_size = d["constants"]["config_cpu_dcache_block_size"])
if "config_cpu_icache_size" in d["constants"]:
icache_sets = int(d["constants"]["config_cpu_icache_size"] /
d["constants"]["config_cpu_icache_block_size"] /
d["constants"]["config_cpu_icache_ways"])
cache_desc += """
i-cache-size = <{i_cache_size}>;
i-cache-sets = <{i_cache_sets}>;
i-cache-block-size = <{i_cache_block_size}>;
""".format(
i_cache_size = d["constants"]["config_cpu_icache_size"],
i_cache_sets = icache_sets,
i_cache_block_size = d["constants"]["config_cpu_icache_block_size"])
if "config_cpu_l2cache_size" in d["constants"]:
cache_desc += """
next-level-cache = <&cluster0_l2_cache>;
"""
# TLB description.
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(
d_tlb_size = d["constants"]["config_cpu_dtlb_size"],
d_tlb_ways = d["constants"]["config_cpu_dtlb_ways"])
if "config_cpu_itlb_size" in d["constants"]:
tlb_desc += """
i-tlb-size = <{i_tlb_size}>;
i-tlb-sets = <{i_tlb_ways}>;
""".format(
i_tlb_size = d["constants"]["config_cpu_itlb_size"],
i_tlb_ways = d["constants"]["config_cpu_itlb_ways"])
# Rocket specific attributes
if (cpu_name == "rocket"):
extra_attr = """
hardware-exec-breakpoint-count = <1>;
next-level-cache = <&memory>;
riscv,pmpgranularity = <4>;
riscv,pmpregions = <8>;
"""
else:
extra_attr = ""
# CPU(s) Topology.
cpu_map = ""
if cpu_count > 1:
cpu_map += """
cpu-map {
cluster0 {"""
for cpu in range(cpu_count):
cpu_map += """
core{cpu} {{
cpu = <&CPU{cpu}>;
}};""".format(cpu=cpu)
cpu_map += """
};
};"""
l2cache = ""
if "config_cpu_l2cache_size" in d["constants"]:
l2_size=d["constants"]["config_cpu_l2cache_size"]
l2_ways=d["constants"]["config_cpu_l2cache_ways"]
l2_block_size = d["constants"]["config_cpu_l2cache_block_size"]
l2_sets = int(l2_size / l2_block_size / l2_ways)
l2cache += """
cluster0_l2_cache: l2-cache0 {{
compatible = "cache";
cache-block-size = <{l2block}>;
cache-level = <2>;
cache-size = <{l2size}>;
cache-sets = <{l2sets}>;
cache-unified;
}};""".format(l2size=l2_size, l2block=l2_block_size, l2sets=l2_sets)
# Prefer CPU_TIMEBASE_FREQUENCY when present.
dts += """
cpus {{
#address-cells = <1>;
#size-cells = <0>;
timebase-frequency = <{timebase_freq}>;
""".format(timebase_freq=d["constants"].get("config_cpu_timebase_frequency",
d["constants"]["config_clock_frequency"]))
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}>;
status = "okay";
{cache_desc}
{tlb_desc}
{extra_attr}
L{irq}: interrupt-controller {{
#address-cells = <0>;
#interrupt-cells = <0x00000001>;
interrupt-controller;
compatible = "riscv,cpu-intc";
}};
}};
""".format(cpu=cpu, irq=cpu,
sys_clk_freq = d["constants"].get("config_cpu_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}
{l2cache}
}};
""".format(cpu_map=cpu_map, l2cache=l2cache)
# Or1k
# ----
elif cpu_family == "or1k":
dts += """
cpus {{
#address-cells = <1>;
#size-cells = <0>;
cpu@0 {{
compatible = "opencores,or1200-rtlsvn481";
reg = <0>;
clock-frequency = <{sys_clk_freq}>;
}};
}};
""".format(sys_clk_freq=d["constants"]["config_clock_frequency"])
# Memory ---------------------------------------------------------------------------------------
dts += """
memory: memory@{main_ram_base:x} {{
device_type = "memory";
reg = <0x{main_ram_base:x} 0x{main_ram_size:x}>;
}};
""".format(
main_ram_base = d["memories"]["main_ram"]["base"],
main_ram_size = d["memories"]["main_ram"]["size"])
if (("opensbi" in d["memories"]) or ("video_framebuffer" in d["csr_bases"])):
dts += """
reserved-memory {
#address-cells = <1>;
#size-cells = <1>;
ranges;
"""
if "opensbi" in d["memories"]:
dts += """
opensbi@{opensbi_base:x} {{
reg = <0x{opensbi_base:x} 0x{opensbi_size:x}>;
}};
""".format(
opensbi_base = d["memories"]["opensbi"]["base"],
opensbi_size = d["memories"]["opensbi"]["size"])
if "video_framebuffer" in d["csr_bases"]:
framebuffer_width = d["constants"]["video_framebuffer_hres"]
framebuffer_height = d["constants"]["video_framebuffer_vres"]
framebuffer_depth = d["constants"]["video_framebuffer_depth"]
framebuffer_stride = generate_dts_framebuffer_stride(framebuffer_width, framebuffer_depth)
dts += """
framebuffer@{framebuffer_base:x} {{
reg = <0x{framebuffer_base:x} 0x{framebuffer_size:x}>;
}};
""".format(
framebuffer_base = d["constants"]["video_framebuffer_base"],
framebuffer_size = framebuffer_stride * framebuffer_height)
dts += """
};
"""
# Voltage Regulator for LiteSDCard (if applicable) --------------------------------------------
if "sdcard" in d["csr_bases"]:
dts += """
vreg_mmc: vreg_mmc {{
compatible = "regulator-fixed";
regulator-name = "vreg_mmc";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
}};
""".format()
# SoC ------------------------------------------------------------------------------------------
dts += """
soc {{
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
interrupt-parent = <&{intc}>;
ranges;
""".format(intc=generate_dts_intc(d))
# SoC Controller -------------------------------------------------------------------------------
dts += """
soc_ctrl0: soc_controller@{soc_ctrl_csr_base:x} {{
compatible = "litex,soc-controller";
reg = <0x{soc_ctrl_csr_base:x} 0xc>;
status = "okay";
}};
""".format(soc_ctrl_csr_base=d["csr_bases"]["ctrl"])
# Interrupt Controller -------------------------------------------------------------------------
if (cpu_family == "riscv") and "clint" in d["memories"]:
# FIXME : L4 definition?
# CHECKME: interrupts-extended.
dts += """
lintc0: clint@{clint_base:x} {{
compatible = "riscv,clint0";
interrupts-extended = <
{cpu_mapping}>;
reg = <0x{clint_base:x} 0x10000>;
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(cpu_count)]))
if "plmt" in d["memories"]:
dts += """
timer@{plmt_base:x} {{
compatible = "andestech,plmt0";
reg = <0x{plmt_base:x} 0x{plmt_size:x}>;
interrupts-extended = <
{cpu_mapping}>;
}};
""".format(
plmt_base = d["memories"]["plmt"]["base"],
plmt_size = d["memories"]["plmt"]["size"],
cpu_mapping = ("\n" + " "*20).join(["&L{} 7".format(cpu) for cpu in range(cpu_count)]))
if cpu_family == "riscv":
if "aplic_m" in d["memories"]:
extra_attr_m = ""
if "imsic_m" in d["memories"]:
extra_attr_m += """
msi-parent = <&imsic_m>;
"""
else:
extra_attr_m += """
interrupts-extended = <
{cpu_mapping}>;
""".format(cpu_mapping = ("\n" + " "*20).join(["&L{} 11".format(cpu) for cpu in range(cpu_count)]))
if "aplic_s" in d["memories"]:
extra_attr_m += """
riscv,children = <&intc_s>;
riscv,delegation = <&intc_s 1 31>;
"""
dts += """
intc_m: interrupt-controller@{aplic_base:x} {{
compatible = "riscv,aplic";
reg = <0x{aplic_base:x} 0x200000>;
#interrupt-cells = <2>;
interrupt-controller;
{extra_attr}
riscv,num-sources = <31>;
}};
""".format(
aplic_base = d["memories"]["aplic_m"]["base"],
extra_attr = extra_attr_m)
if "aplic_s" in d["memories"]:
extra_attr_s = ""
if "imsic_s" in d["memories"]:
extra_attr_s += """
msi-parent = <&imsic_s>;
"""
else:
extra_attr_s += """
interrupts-extended = <
{cpu_mapping}>;
""".format(cpu_mapping = ("\n" + " "*20).join(["&L{} 9".format(cpu) for cpu in range(cpu_count)]))
dts += """
intc_s: interrupt-controller@{aplic_base:x} {{
compatible = "riscv,aplic";
reg = <0x{aplic_base:x} 0x200000>;
#interrupt-cells = <2>;
interrupt-controller;
{extra_attr}
riscv,num-sources = <31>;
}};
""".format(
aplic_base = d["memories"]["aplic_s"]["base"],
extra_attr = extra_attr_s)
elif "plic" in d["memories"]:
if cpu_name == "rocket":
extra_attr = """
reg-names = "control";
riscv,max-priority = <7>;
"""
else:
extra_attr = ""
dts += """
intc0: interrupt-controller@{plic_base:x} {{
compatible = "sifive,fu540-c000-plic", "sifive,plic-1.0.0";
reg = <0x{plic_base:x} 0x400000>;
#address-cells = <0>;
#interrupt-cells = <1>;
interrupt-controller;
interrupts-extended = <
{cpu_mapping}>;
riscv,ndev = <{plic_ndev}>;
{extra_attr}
}};
""".format(
plic_base = d["memories"]["plic"]["base"],
plic_ndev = get_dts_constant(d, "config_cpu_plic_ndev", 32),
cpu_mapping = ("\n" + " "*20).join(["&L{} 11 &L{} 9".format(cpu, cpu) for cpu in range(cpu_count)]),
extra_attr = extra_attr)
else:
raise NotImplementedError("No interrupt controller found")
if "imsic_m" in d["memories"]:
dts += """
imsic_m: interrupt-controller@{imsic_base:x} {{
compatible = "riscv,imsics";
reg = <0x{imsic_base:x} 0x{imsic_size:x}>;
#interrupt-cells = <0>;
#msi-cells = <0>;
interrupt-controller;
interrupts-extended = <
{cpu_mapping}>;
msi-controller;
riscv,num-ids = <63>;
}};
""".format(
imsic_base = d["memories"]["imsic_m"]["base"],
imsic_size = d["memories"]["imsic_m"]["size"],
cpu_mapping = ("\n" + " "*20).join(["&L{} 11".format(cpu) for cpu in range(cpu_count)]))
if "imsic_s" in d["memories"]:
dts += """
imsic_s: interrupt-controller@{imsic_base:x} {{
compatible = "riscv,imsics";
reg = <0x{imsic_base:x} 0x{imsic_size:x}>;
#interrupt-cells = <0>;
#msi-cells = <0>;
interrupt-controller;
interrupts-extended = <
{cpu_mapping}>;
msi-controller;
riscv,num-ids = <63>;
}};
""".format(
imsic_base = d["memories"]["imsic_s"]["base"],
imsic_size = d["memories"]["imsic_s"]["size"],
cpu_mapping = ("\n" + " "*20).join(["&L{} 9".format(cpu) for cpu in range(cpu_count)]))
elif cpu_family == "or1k":
dts += """
intc0: interrupt-controller {
interrupt-controller;
#interrupt-cells = <1>;
compatible = "opencores,or1k-pic";
status = "okay";
};
"""
if (cpu_family == "riscv") and (cpu_name == "rocket"):
dts += """
dbg_ctl: debug-controller@0 {{
compatible = "sifive,debug-013", "riscv,debug-013";
interrupts-extended = <
{cpu_mapping}>;
reg = <0x0 0x1000>;
reg-names = "control";
}};
err_dev: error-device@3000 {{
compatible = "sifive,error0";
reg = <0x3000 0x1000>;
}};
ext_it: external-interrupts {{
interrupts = <1 2 3 4 5 6 7 8>;
}};
rom: rom@10000 {{
compatible = "sifive,rom0";
reg = <0x10000 0x10000>;
reg-names = "mem";
}};
""".format(
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";
reg = <0x{uart_csr_base:x} 0x100>;
{uart_interrupt}
status = "okay";
}};
""".format(
uart_csr_base = d["csr_bases"]["uart"],
uart_interrupt = "" if polling else generate_dts_interrupt(d, int(d["constants"]["uart_interrupt"]) + it_incr, polling))
# Ethernet -------------------------------------------------------------------------------------
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>,
<0x{ethmac_mem_base:x} 0x{ethmac_mem_size:x}>;
reg-names = "mac", "mdio", "buffer";
litex,rx-slots = <{ethmac_rx_slots}>;
litex,tx-slots = <{ethmac_tx_slots}>;
litex,slot-size = <{ethmac_slot_size}>;
{ethmac_interrupt}
{local_mac_addr}
status = "okay";
}};
""".format(
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 generate_dts_interrupt(d, int(d["constants"][ethmac_name + "_interrupt"]) + it_incr, polling),
local_mac_addr = "" if not "macaddr1" in d["constants"] else "local-mac-address = [{mac_addr}];".format(
mac_addr = "{a1:02X} {a2:02X} {a3:02X} {a4:02X} {a5:02X} {a6:02X}".format(
a1 = d["constants"]["macaddr1"],
a2 = d["constants"]["macaddr2"],
a3 = d["constants"]["macaddr3"],
a4 = d["constants"]["macaddr4"],
a5 = d["constants"]["macaddr5"],
a6 = d["constants"]["macaddr6"])))
# USB OHCI -------------------------------------------------------------------------------------
if "usb_ohci_ctrl" in d["memories"]:
dts += """
usb0: usb@{usb_ohci_mem_base:x} {{
compatible = "generic-ohci";
reg = <0x{usb_ohci_mem_base:x} 0x1000>;
{usb_ohci_interrupt}
status = "okay";
}};
""".format(
usb_ohci_mem_base = d["memories"]["usb_ohci_ctrl"]["base"],
usb_ohci_interrupt = generate_dts_interrupt(d, 16, polling)) # FIXME
# SPI Flash ------------------------------------------------------------------------------------
if "spiflash" in d["csr_bases"]:
aliases["spiflash"] = "litespiflash"
dts += """
litespiflash: spiflash@{spiflash_csr_base:x} {{
#address-cells = <1>;
#size-cells = <1>;
compatible = "litex,spiflash";
reg = <0x{spiflash_csr_base:x} 0x100>;
flash: flash@0 {{
compatible = "jedec,spi-nor";
reg = <0x0 0x{spiflash_size:x}>;
}};
}};
""".format(spiflash_csr_base=d["csr_bases"]["spiflash"], spiflash_size=d["memories"]["spiflash"]["size"])
# SPI-SDCard -----------------------------------------------------------------------------------
if "spisdcard" in d["csr_bases"]:
aliases["sdcard0"] = "litespisdcard0"
dts += """
litespisdcard0: spi@{spisdcard_csr_base:x} {{
compatible = "litex,litespi";
reg = <0x{spisdcard_csr_base:x} 0x100>;
status = "okay";
litespi,max-bpw = <8>;
litespi,sck-frequency = <1500000>;
litespi,num-cs = <1>;
#address-cells = <1>;
#size-cells = <0>;
mmc-slot@0 {{
compatible = "mmc-spi-slot";
reg = <0>;
voltage-ranges = <3300 3300>;
spi-max-frequency = <1500000>;
status = "okay";
}};
}};
""".format(spisdcard_csr_base=d["csr_bases"]["spisdcard"])
# SDCard ---------------------------------------------------------------------------------------
dts += generate_dts_sdcard(d, polling=polling)
# Leds -----------------------------------------------------------------------------------------
if "leds" in d["csr_bases"]:
dts += """
leds: gpio@{leds_csr_base:x} {{
compatible = "litex,gpio";
reg = <0x{leds_csr_base:x} 0x4>;
gpio-controller;
#gpio-cells = <2>;
litex,direction = "out";
status = "disabled";
}};
""".format(leds_csr_base=d["csr_bases"]["leds"])
# RGB Leds -------------------------------------------------------------------------------------
for name in ["rgb_led_r0", "rgb_led_g0", "rgb_led_b0"]:
if name in d["csr_bases"]:
dts += """
{pwm_name}: pwm@{pwm_csr_base:x} {{
compatible = "litex,pwm";
reg = <0x{pwm_csr_base:x} 0x24>;
clock = <100000000>;
#pwm-cells = <3>;
status = "okay";
}};
""".format(pwm_name=name, pwm_csr_base=d["csr_bases"][name])
# Switches -------------------------------------------------------------------------------------
if "switches" in d["csr_bases"]:
dts += """
switches: gpio@{switches_csr_base:x} {{
compatible = "litex,gpio";
reg = <0x{switches_csr_base:x} 0x4>;
gpio-controller;
#gpio-cells = <2>;
litex,direction = "in";
{switches_interrupt}
status = "disabled";
}};
""".format(
switches_csr_base = d["csr_bases"]["switches"],
switches_interrupt = generate_dts_interrupt(d, d["constants"]["switches_interrupt"], polling))
# SPI ------------------------------------------------------------------------------------------
if "spi" in d["csr_bases"]:
aliases["spi0"] = "litespi0"
dts += """
litespi0: spi@{spi_csr_base:x} {{
compatible = "litex,litespi";
reg = <0x{spi_csr_base:x} 0x100>;
status = "okay";
litespi,max-bpw = <8>;
litespi,sck-frequency = <1000000>;
litespi,num-cs = <1>;
#address-cells = <1>;
#size-cells = <0>;
spidev0: spidev@0 {{
compatible = "linux,spidev";
reg = <0>;
spi-max-frequency = <1000000>;
status = "okay";
}};
}};
""".format(spi_csr_base=d["csr_bases"]["spi"])
# I2C ------------------------------------------------------------------------------------------
# One node per i2c<N> CSR base: a SoC may instantiate several I2C masters, and only the first
# was emitted before. The label matches the CSR name so overlays can reference &i2cN.
for name in sorted(
(n for n in d["csr_bases"] if re.fullmatch(r"i2c\d+", n)),
key=lambda n: int(n[3:]),
):
dts += """
{i2c_name}: i2c@{i2c_csr_base:x} {{
compatible = "litex,i2c";
reg = <0x{i2c_csr_base:x} 0x5>;
#address-cells = <1>;
#size-cells = <0>;
status = "okay";
}};
""".format(i2c_name=name, i2c_csr_base=d["csr_bases"][name])
# Hardware Monitors ----------------------------------------------------------------------------
xadc_dts = generate_dts_xadc(d)
dts += xadc_dts
dts += generate_dts_hwmon_temperature(d, hwmon_index=1 if xadc_dts else 0)
# CAN ------------------------------------------------------------------------------------------
for mem in d["memories"]:
if "can" in mem:
can_interrupt = int(d["constants"][f"{mem}_interrupt"])
dts += """
{name}: can@{can_mem_base:x} {{
compatible = "ctu,ctucanfd";
reg = <0x{can_mem_base:x} 0x{can_mem_size:x}>;
interrupt-parent = <&{interrupt_parent}>;
{interrupt}
clocks = <&sys_clk>;
status = "okay";
}};
""".format(
name = mem,
can_mem_base = d["memories"][mem]["base"],
can_mem_size = d["memories"][mem]["size"],
interrupt_parent = generate_dts_intc(d),
interrupt = generate_dts_interrupt(d, can_interrupt, polling),
)
# Framebuffer ----------------------------------------------------------------------------------
if "video_framebuffer" in d["csr_bases"]:
framebuffer_base = d["constants"]["video_framebuffer_base"]
framebuffer_width = d["constants"]["video_framebuffer_hres"]
framebuffer_height = d["constants"]["video_framebuffer_vres"]
framebuffer_depth = d["constants"]["video_framebuffer_depth"]
framebuffer_stride = generate_dts_framebuffer_stride(framebuffer_width, framebuffer_depth)
framebuffer_size = framebuffer_stride * framebuffer_height
framebuffer_format = generate_dts_framebuffer_format(framebuffer_depth)
dts += """
framebuffer0: framebuffer@{framebuffer_base:x} {{
compatible = "simple-framebuffer";
reg = <0x{framebuffer_base:x} 0x{framebuffer_size:x}>;
width = <{framebuffer_width}>;
height = <{framebuffer_height}>;
stride = <{framebuffer_stride}>;
format = "{framebuffer_format}";
}};
""".format(
framebuffer_base = framebuffer_base,
framebuffer_width = framebuffer_width,
framebuffer_height = framebuffer_height,
framebuffer_size = framebuffer_size,
framebuffer_stride = framebuffer_stride,
framebuffer_format = framebuffer_format)
# ICAP Bitstream -------------------------------------------------------------------------------
if "icap_bit" in d["csr_bases"]:
dts += """
fpga0: icap@{icap_csr_base:x} {{
compatible = "litex,fpga-icap";
reg = <0x{icap_csr_base:x} 0x14>;
status = "okay";
}};
""".format(icap_csr_base=d["csr_bases"]["icap_bit"])
# Clocking ------------------------------------------------------------------------------------
def add_clkout(clkout_nr, clk_f, clk_p, clk_dn, clk_dd, clk_margin, clk_margin_exp):
return """
CLKOUT{clkout_nr}: CLKOUT{clkout_nr} {{
compatible = "litex,clk";
#clock-cells = <0>;
clock-output-names = "CLKOUT{clkout_nr}";
reg = <{clkout_nr}>;
litex,clock-frequency = <{clk_f}>;
litex,clock-phase = <{clk_p}>;
litex,clock-duty-num = <{clk_dn}>;
litex,clock-duty-den = <{clk_dd}>;
litex,clock-margin = <{clk_margin}>;
litex,clock-margin-exp = <{clk_margin_exp}>;
}};
""".format(
clkout_nr = clkout_nr,
clk_f = clk_f,
clk_p = clk_p,
clk_dn = clk_dn,
clk_dd = clk_dd,
clk_margin = clk_margin,
clk_margin_exp = clk_margin_exp)
if "mmcm" in d["csr_bases"]:
nclkout = d["constants"]["nclkout"]
dts += """
clk0: clk@{mmcm_csr_base:x} {{
compatible = "litex,clk";
reg = <0x{mmcm_csr_base:x} 0x100>;
#clock-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;
clock-output-names =
""".format(mmcm_csr_base=d["csr_bases"]["mmcm"])
for clkout_nr in range(nclkout - 1):
dts += """
"CLKOUT{clkout_nr}",
""".format(clkout_nr=clkout_nr)
dts += """
"CLKOUT{nclkout}";
""".format(nclkout=(nclkout - 1))
dts += """
litex,lock-timeout = <{mmcm_lock_timeout}>;
litex,drdy-timeout = <{mmcm_drdy_timeout}>;
litex,sys-clock-frequency = <{sys_clk}>;
litex,divclk-divide-min = <{divclk_divide_range[0]}>;
litex,divclk-divide-max = <{divclk_divide_range[1]}>;
litex,clkfbout-mult-min = <{clkfbout_mult_frange[0]}>;
litex,clkfbout-mult-max = <{clkfbout_mult_frange[1]}>;
litex,vco-freq-min = <{vco_freq_range[0]}>;
litex,vco-freq-max = <{vco_freq_range[1]}>;
litex,clkout-divide-min = <{clkout_divide_range[0]}>;
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"],
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"])
for clkout_nr in range(nclkout):
dts += add_clkout(clkout_nr,
d["constants"]["clkout_def_freq"],
d["constants"]["clkout_def_phase"],
d["constants"]["clkout_def_duty_num"],
d["constants"]["clkout_def_duty_den"],
d["constants"]["clkout_margin"],
d["constants"]["clkout_margin_exp"])
dts += """
};"""
dts += """
};
"""
# Aliases --------------------------------------------------------------------------------------
if aliases:
dts += """
aliases {
"""
for alias in aliases:
dts += """
{} = &{};
""".format(alias, aliases[alias])
dts += """
};
"""
dts += """
};
"""
# Leds & switches ------------------------------------------------------------------------------
if "leds" in d["csr_bases"]:
dts += """
&leds {{
litex,ngpio = <{ngpio}>;
status = "okay";
}};
""".format(ngpio=d["constants"].get('leds_ngpio', 4))
if "switches" in d["csr_bases"]:
dts += """
&switches {{
litex,ngpio = <{ngpio}>;
status = "okay";
}};
""".format(ngpio=d["constants"].get('switches_ngpio', 4))
return dts
def main():
parser = argparse.ArgumentParser(description="LiteX's CSR JSON to Linux DTS generator")
parser.add_argument("csr_json", help="CSR JSON file")
parser.add_argument("--initrd-start", type=int, help="Location of initrd in RAM (relative, default depends on CPU).")
parser.add_argument("--initrd-size", type=int, help="Size of initrd (default=8MB).")
parser.add_argument("--initrd", type=str, help="Supports arguments 'enabled', 'disabled' or a file name. Set to 'disabled' if you use a kernel built in rootfs or have your rootfs on an SD card partition. If a file name is provided the size of the file will be used instead of --initrd-size. (default=enabled).")
parser.add_argument("--root-device", type=str, help="Device that has our rootfs, if using initrd use the default. For SD card's use something like mmcblk0p3. (default=ram0).")
parser.add_argument("--polling", action="store_true", help="Force polling mode on peripherals.")
args = parser.parse_args()
d = json.load(open(args.csr_json))
r = generate_dts(d,
initrd_start = args.initrd_start,
initrd_size = args.initrd_size,
initrd = args.initrd,
root_device = args.root_device,
polling = args.polling,
)
print(r)
if __name__ == "__main__":
main()