Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
260 changes: 246 additions & 14 deletions lopper/assists/gen_domain_dts.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

sys.path.append(os.path.dirname(__file__))

from baremetalconfig_xlnx import compat_list, get_cpu_node, get_mapped_nodes, get_label
from baremetalconfig_xlnx import compat_list, get_cpu_node, get_mapped_nodes, get_label, scan_reg_size
from common_utils import to_cmakelist
import common_utils as utils
from domain_access import update_mem_node
Expand Down Expand Up @@ -101,6 +101,81 @@ def filter_ipi_nodes_for_cpu(sdt, machine):
return


def check_console_uart_accessibility(sdt, options):
"""
Check if the console UART (from /chosen/stdout-path) is accessible to MicroBlaze RISC-V.

Logic:
1. Read /chosen/stdout-path to get configured console UART alias (e.g., serial0)
2. Resolve alias to get actual UART device node
3. Check if UART is accessible using accessible_by() API
4. If accessible: enable PS UART (set status="okay")
5. If NOT accessible: find PL UART that is accessible and update /chosen/stdout-path

This ensures Vitis BSP and Zephyr use the same console UART.

Args:
sdt: System device tree
options: User options with processor name
"""
try:
# Only process for MicroBlaze RISC-V
match_cpunode = get_cpu_node(sdt, options)
cpu_ip = match_cpunode.propval('xlnx,ip-name', list)
if not cpu_ip or cpu_ip[0] not in ('microblaze', 'microblaze_riscv'):
return

chosen_node = sdt.tree['/chosen']
if chosen_node.propval('stdout-path') == ['']:
return

stdout_path = chosen_node.propval('stdout-path', list)[0]
alias_name = stdout_path.split(':')[0]

alias_node = sdt.tree['/aliases']
if alias_node.propval(alias_name) == ['']:
return

uart_path = alias_node.propval(alias_name, list)[0]

try:
uart_node = sdt.tree[uart_path]
except KeyError:
return

# Check if UART is accessible by any CPU cluster using accessible_by() API
is_accessible = bool(sdt.tree.accessible_by(uart_node))

if is_accessible:
current_status = uart_node.propval('status', list)
if current_status and current_status[0] == 'disabled':
uart_node['status'] = 'okay'
else:
pl_uart_compatibles = [
'xlnx,axi-uart16550', 'xlnx,xps-uart16550',
'xlnx,xps-uartlite', 'xlnx,mdm-riscv',
'ns16550'
]

accessible_pl_uart = None
for node in sdt.tree.nodes('/.*'):
if node.propval('compatible') != ['']:
node_compatibles = node.propval('compatible', list)
if any(compat in node_compatibles for compat in pl_uart_compatibles):
# Check if this PL UART is accessible using accessible_by() API
if sdt.tree.accessible_by(node):
accessible_pl_uart = node
break

if accessible_pl_uart:
pl_uart_label = accessible_pl_uart.label if accessible_pl_uart.label else "serial_pl"
alias_node[pl_uart_label] = accessible_pl_uart.abs_path
baud_rate = ":" + stdout_path.split(':')[1] if ':' in stdout_path else ""
chosen_node['stdout-path'] = pl_uart_label + baud_rate
except Exception:
pass


# tgt_node: is the top level domain node
# sdt: is the system device-tree
# options: User provided options (processor name)
Expand Down Expand Up @@ -199,6 +274,11 @@ def xlnx_generate_domain_dts(tgt_node, sdt, options):

filter_ipi_nodes_for_cpu(sdt, machine)

# Check console UART accessibility for MicroBlaze Zephyr DTS
# This ensures Vitis BSP and Zephyr use the same accessible console UART
if zephyr_dt:
check_console_uart_accessibility(sdt, options)

node_list = []
cpu_ip = match_cpunode.propval('xlnx,ip-name', list)
for node in root_sub_nodes:
Expand Down Expand Up @@ -1063,7 +1143,16 @@ def xlnx_generate_zephyr_domain_dts(tgt_node, sdt, options):
root_sub_nodes = root_node.subnodes()
symbol_node = sdt.tree['/__symbols__']
valid_alias_proplist = []

stdout_baud = None
try:
chosen_node = sdt.tree['/chosen']
if chosen_node.propval('stdout-path') != ['']:
stdout_path = chosen_node.propval('stdout-path', list)[0]
match = re.search(r':(\d+)', str(stdout_path))
if match:
stdout_baud = int(match.group(1))
except Exception:
stdout_baud = None
"""
DRC Checks
1) Interrupt controller is present or not
Expand Down Expand Up @@ -1110,6 +1199,136 @@ def xlnx_generate_zephyr_domain_dts(tgt_node, sdt, options):
print(err_timer_nointr)
sys.exit(1)

memnode_list = sdt.tree.nodes('/memory@.*')
for mem_node in memnode_list:
if mem_node.propval('ranges') != ['']:
mem_node.delete('ranges')

# MicroBlaze Zephyr DTS should only keep PL peripherals plus core nodes
# when the design includes both PS and PL (MicroBlaze) domains.
has_pl_mb = False
try:
match_cpunode = get_cpu_node(sdt, options)
cpu_ip = match_cpunode.propval('xlnx,ip-name', list)
has_pl_mb = bool(cpu_ip and cpu_ip[0] == "microblaze_riscv")
except Exception:
has_pl_mb = any(
node.propval('xlnx,ip-name') != [''] and node.propval('xlnx,ip-name', list)[0] == "microblaze_riscv"
for node in root_sub_nodes
)

has_ps_axi = False
ps_serial_data_to_recreate = []

try:
axi_node = sdt.tree['/axi']
has_ps_axi = True

if has_pl_mb:
ps_uart_compatibles = ['arm,pl011', 'arm,sbsa-uart', 'arm,primecell']
for node in list(axi_node.subnodes()):
if node.propval('compatible') != ['']:
node_compatibles = node.propval('compatible', list)
if any(compat in node_compatibles for compat in ps_uart_compatibles):
if 'serial' in node.name.lower() and node.propval('status', list) == ['okay']:
node_data = {
'name': node.name,
'label': node.label,
'properties': {}
}
for prop_name, prop_obj in node.__props__.items():
node_data['properties'][prop_name] = prop_obj.value
ps_serial_data_to_recreate.append(node_data)
except Exception:
pass

if has_pl_mb and has_ps_axi:
allowed_top_nodes = {
"amba_pl",
"soc",
"chosen",
"aliases",
"clocks",
"__symbols__",
}
for node in list(root_sub_nodes):
if node.depth != 1:
continue
if node.name in allowed_top_nodes:
continue
if node.name.startswith("memory@"):
continue
if node.name.startswith("cpus"):
continue
sdt.tree.delete(node)
root_sub_nodes = root_node.subnodes()

if ps_serial_data_to_recreate:
target_bus = None
try:
target_bus = sdt.tree['/soc']
except KeyError:
try:
target_bus = sdt.tree['/amba_pl']
except KeyError:
pass

if target_bus:
for idx, serial_data in enumerate(ps_serial_data_to_recreate):
try:
new_serial_path = f"{target_bus.abs_path}/{serial_data['name']}"
serial_label = f"serial{idx}"

new_serial = LopperNode(-1, new_serial_path)
new_serial.tree = sdt.tree
new_serial.label = serial_label

new_serial + LopperProp(name="compatible", value=["arm,sbsa-uart"])
new_serial + LopperProp(name="status", value=["okay"])

if 'reg' in serial_data['properties']:
new_serial + LopperProp(name="reg", value=serial_data['properties']['reg'])

baudrate = serial_data['properties']['xlnx,baudrate'][0] if 'xlnx,baudrate' in serial_data['properties'] else 115200
new_serial + LopperProp(name="current-speed", value=[baudrate])

sdt.tree.add(new_serial)

alias_path = new_serial.abs_path.replace("/amba_pl/", "/soc/")
alias_node = sdt.tree['/aliases']
alias_node + LopperProp(name=serial_label, value=alias_path)

valid_alias_proplist.append(serial_data['name'])
except Exception:
pass

try:
chosen_node = sdt.tree['/chosen']
memnode_list = sdt.tree.nodes('/memory@.*')
ddr_memory = None
bram_memory = None
ocm_memory = None
for mem_node in memnode_list:
mem_path = mem_node.abs_path.lower()
mem_name = mem_node.name.lower()

if 'ddr' in mem_path or 'ddr' in mem_name or any(addr in mem_name for addr in ['@40000000', '@80000000', '@8000', '@4000', 'axi_noc', 'noc']):
if not ddr_memory:
ddr_memory = mem_node
elif any(keyword in mem_path or keyword in mem_name for keyword in ['bram', 'axi_bram', 'axi-bram', 'blockram', 'dlmb', 'ilmb', 'lmb']):
if not bram_memory:
bram_memory = mem_node
elif any(keyword in mem_path or keyword in mem_name for keyword in ['ocm', 'tcm']) or (mem_name.startswith('memory@') and 'sram' in mem_path):
if not ocm_memory:
ocm_memory = mem_node

selected_sram = ddr_memory or bram_memory or ocm_memory

if selected_sram:
chosen_node['zephyr,sram'] = LopperProp(name="zephyr,sram", value=selected_sram.abs_path)
except Exception:
pass

license_content = '''#
# Copyright (c) 2024 - 2025 Advanced Micro Devices, Inc.
#
Expand Down Expand Up @@ -1145,16 +1364,25 @@ def xlnx_generate_zephyr_domain_dts(tgt_node, sdt, options):
if val == "microblaze_riscv":
compatlist = ['amd,mbv32', 'riscv']
node['compatible'] = compatlist
new_node = LopperNode()
new_node.name = "interrupt-controller"
new_node['compatible'] = "riscv,cpu-intc"
new_prop = LopperProp( "interrupt-controller" )
new_node + new_prop
new_node['#interrupt-cells'] = 1
new_node.label_set("cpu_intc")
node.add(new_node)
phandle_val = new_node.phandle_or_create()
new_node + LopperProp(name="phandle", value=phandle_val)

# Find existing interrupt-controller child or create a new one
intc_node = None
for child in node.child_nodes.values():
if child.name == "interrupt-controller":
intc_node = child
break
if not intc_node:
intc_node = LopperNode()
intc_node.name = "interrupt-controller"
node.add(intc_node)

intc_node.label_set("cpu_intc")
intc_node['compatible'] = "riscv,cpu-intc"
intc_node['#interrupt-cells'] = 1
if 'interrupt-controller' not in intc_node.__props__:
intc_node + LopperProp("interrupt-controller")
phandle_val = intc_node.phandle_or_create()
intc_node + LopperProp(name="phandle", value=phandle_val)

zephyr_supported_schema_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "zephyr_supported_comp.yaml")
if utils.is_file(zephyr_supported_schema_file):
Expand Down Expand Up @@ -1216,9 +1444,13 @@ def xlnx_generate_zephyr_domain_dts(tgt_node, sdt, options):
node["reg-shift"] = LopperProp("reg-shift")
node["reg-shift"].value = 2
if node.propval('current-speed') == ['']:
# Using default IP baud-rate of 9600, but change according to uart-setup for prints
node["current-speed"] = LopperProp("current-speed")
node["current-speed"].value = 9600
if node.propval('xlnx,baudrate') != ['']:
node["current-speed"].value = node["xlnx,baudrate"].value
elif stdout_baud:
node["current-speed"].value = stdout_baud
else:
node["current-speed"].value = 9600
# MDM RISCV DEBUG UARTLITE
if "xlnx,mdm-riscv-1.0" in node["compatible"].value:
node["compatible"].value = ["xlnx,xps-uartlite-1.00a"]
Expand Down