Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
#
# This file is part of LiteX.
#
# Copyright (c) 2026 Florent Kermarrec <florent@enjoy-digital.fr>
# SPDX-License-Identifier: BSD-2-Clause
import unittest
import xml.etree.ElementTree as ET
from types import SimpleNamespace
from litex.soc.integration.export import (
get_csr_header,
get_csr_svd,
get_linker_regions,
get_mem_header,
get_soc_header,
)
from litex.soc.integration.soc import SoCCSRRegion, SoCRegion
from litex.soc.interconnect.csr import CSRField, CSRStatus, CSRStorage
def _get_csr_header(csr):
return get_csr_header(
regions = {
"ctrl": SoCCSRRegion(origin=0xf0000000, busword=32, obj=[csr]),
},
constants = {},
csr_base = 0xf0000000,
with_fields_access_functions = True,
)
def _get_csr_svd(csr, ordering="big"):
csr.finalize(32, ordering)
soc = SimpleNamespace(
irq = SimpleNamespace(locs={}),
csr = SimpleNamespace(
data_width = 32,
ordering = ordering,
regions = {
"ctrl": SoCCSRRegion(origin=0xf0000000, busword=32, obj=[csr]),
},
),
mem_regions = {},
constants = {},
)
return get_csr_svd(soc, description="test")
def _get_svd_registers(svd):
registers = {}
root = ET.fromstring(svd)
for register in root.findall("./peripherals/peripheral/registers/register"):
fields = {}
for field in register.findall("./fields/field"):
fields[field.findtext("name")] = {
"msb": field.findtext("msb"),
"lsb": field.findtext("lsb"),
"bitRange": field.findtext("bitRange"),
}
registers[register.findtext("name")] = fields
return registers
def _get_svd_field_enums(svd, register_name, field_name):
root = ET.fromstring(svd)
for register in root.findall("./peripherals/peripheral/registers/register"):
if register.findtext("name") != register_name:
continue
for field in register.findall("./fields/field"):
if field.findtext("name") != field_name:
continue
return [
{
"name": enum.findtext("name"),
"description": enum.findtext("description"),
"value": enum.findtext("value"),
}
for enum in field.findall("./enumeratedValues/enumeratedValue")
]
return []
class TestCSRExport(unittest.TestCase):
def test_soc_header_formats_int_constants(self):
header = get_soc_header({
"CONFIG_SMALL": 42,
"CONFIG_CLOCK_FREQUENCY": 100000000,
"CONFIG_RESET_ADDR": 0x80000000,
"CONFIG_MASK": 0xffff,
"CONFIG_64BIT": 0x100000000,
"CONFIG_NEGATIVE_64BIT": -0x80000001,
})
self.assertIn("#define CONFIG_SMALL 42\n", header)
self.assertIn("#define CONFIG_CLOCK_FREQUENCY 100000000\n", header)
self.assertIn("#define CONFIG_RESET_ADDR 0x80000000\n", header)
self.assertIn("#define CONFIG_MASK 0xffff\n", header)
self.assertIn("#define CONFIG_64BIT 0x100000000ULL\n", header)
self.assertIn("#define CONFIG_NEGATIVE_64BIT -0x80000001LL\n", header)
self.assertIn("static inline uint64_t config_64bit_read(void)", header)
self.assertIn("static inline int64_t config_negative_64bit_read(void)", header)
def test_csr_field_accessors_use_uint64_for_wide_csr(self):
csr = CSRStorage(name="wide", fields=[
CSRField("low", size=32, offset=0),
CSRField("high", size=32, offset=32),
])
header = _get_csr_header(csr)
self.assertIn("static inline uint64_t ctrl_wide_high_extract(uint64_t oldword)", header)
self.assertIn("uint64_t word = ctrl_wide_read();", header)
self.assertIn("static inline uint64_t ctrl_wide_high_replace(uint64_t oldword, uint64_t plain_value)", header)
self.assertIn("uint64_t oldword = ctrl_wide_read();", header)
self.assertIn("uint64_t newword = ctrl_wide_high_replace(oldword, plain_value);", header)
self.assertNotIn("static inline uint32_t ctrl_wide_high_extract(uint32_t oldword)", header)
def test_csr_field_accessors_keep_uint32_for_32_bit_csr(self):
csr = CSRStorage(name="narrow", fields=[
CSRField("field", size=8, offset=8),
])
header = _get_csr_header(csr)
self.assertIn("static inline uint32_t ctrl_narrow_field_extract(uint32_t oldword)", header)
self.assertIn("uint32_t word = ctrl_narrow_read();", header)
self.assertIn("static inline void ctrl_narrow_field_write(uint32_t plain_value)", header)
def test_csr_field_accessors_skip_csr_wider_than_uint64(self):
csr = CSRStorage(name="too_wide", fields=[
CSRField("field", size=8, offset=64),
])
header = _get_csr_header(csr)
self.assertNotIn("ctrl_too_wide_field_extract", header)
self.assertNotIn("ctrl_too_wide_field_read", header)
def test_cpu_bios_map_exports_virtual_memory_aliases(self):
cpu = SimpleNamespace(
bios_map = lambda addr, cached: addr + (0x80000000 if cached else 0xa0000000),
)
regions = {
"main_ram": SoCRegion(origin=0x00000000, size=0x1000, cached=True),
"csr": SoCRegion(origin=0x18000000, size=0x1000, cached=False),
}
linker = get_linker_regions(regions, cpu=cpu)
header = get_mem_header(regions, cpu=cpu)
self.assertIn("main_ram : ORIGIN = 0x80000000", linker)
self.assertIn("csr : ORIGIN = 0xb8000000", linker)
self.assertIn("#define MAIN_RAM_BASE 0x00000000L", header)
self.assertIn("#define MAIN_RAM_BASE_VA 0x80000000L", header)
self.assertIn("#define CSR_BASE 0x18000000L", header)
self.assertIn("#define CSR_BASE_VA 0xb8000000L", header)
def test_cpu_bios_map_exports_virtual_csr_accessors(self):
cpu = SimpleNamespace(
bios_map = lambda addr, cached: addr + (0x80000000 if cached else 0xa0000000),
)
csr = CSRStatus(name="scratch", size=32)
header = get_csr_header(
regions = {
"ctrl": SoCCSRRegion(origin=0x18000000, busword=32, obj=[csr]),
},
constants = {},
csr_base = 0x18000000,
cpu = cpu,
)
self.assertIn("#define CSR_BASE 0x18000000L", header)
self.assertIn("#define CSR_BASE_VA 0xb8000000L", header)
self.assertIn("#define CSR_CTRL_BASE (CSR_BASE + 0x0L)", header)
self.assertIn("#define CSR_CTRL_BASE_VA (CSR_BASE_VA + 0x0L)", header)
self.assertIn("return csr_read_simple((CSR_BASE_VA + 0x0L));", header)
def test_csr_header_omits_virtual_aliases_for_identity_map(self):
cpu = SimpleNamespace(
bios_map = lambda addr, cached: addr,
)
csr = CSRStatus(name="scratch", size=32)
header = get_csr_header(
regions = {
"ctrl": SoCCSRRegion(origin=0x18000000, busword=32, obj=[csr]),
},
constants = {},
csr_base = 0x18000000,
cpu = cpu,
)
self.assertIn("#define CSR_BASE 0x18000000L", header)
self.assertIn("#define CSR_CTRL_BASE (CSR_BASE + 0x0L)", header)
self.assertIn("#define CSR_CTRL_SCRATCH_ADDR (CSR_BASE + 0x0L)", header)
self.assertIn("return csr_read_simple((CSR_BASE + 0x0L));", header)
self.assertNotIn("CSR_BASE_VA", header)
self.assertNotIn("CSR_CTRL_BASE_VA", header)
self.assertNotIn("CSR_CTRL_SCRATCH_ADDR_VA", header)
def test_relative_csr_header_omits_virtual_aliases_for_identity_map(self):
cpu = SimpleNamespace(
bios_map = lambda addr, cached: addr,
)
csr = CSRStatus(name="scratch", size=32)
header = get_csr_header(
regions = {
"ctrl": SoCCSRRegion(origin=0x0, busword=32, obj=[csr]),
},
constants = {},
csr_base = 0x0,
with_csr_base_define = False,
cpu = cpu,
)
self.assertIn("#define CSR_CTRL_BASE 0x0L", header)
self.assertIn("#define CSR_CTRL_SCRATCH_ADDR 0x0L", header)
self.assertIn("return csr_read_simple(0x0L);", header)
self.assertNotIn("CSR_CTRL_BASE_VA", header)
self.assertNotIn("CSR_CTRL_SCRATCH_ADDR_VA", header)
def test_svd_splits_wide_csr_fields_per_bus_word(self):
csr = CSRStatus(name="switch", description="TMU Switch Status", fields=[
CSRField("source", size=32, offset=0, description="Source Thread ID"),
CSRField("dest", size=32, offset=32, description="Destination Thread ID"),
])
svd = _get_csr_svd(csr)
registers = _get_svd_registers(svd)
self.assertNotIn("<msb>-", svd)
self.assertNotIn("<bitRange>[-", svd)
self.assertEqual(registers["SWITCH1"], {
"dest": {"msb": "31", "lsb": "0", "bitRange": "[31:0]"},
})
self.assertEqual(registers["SWITCH0"], {
"source": {"msb": "31", "lsb": "0", "bitRange": "[31:0]"},
})
def test_svd_clips_field_crossing_split_csr_boundary(self):
csr = CSRStatus(name="wide", fields=[
CSRField("middle", size=32, offset=16),
])
svd = _get_csr_svd(csr)
registers = _get_svd_registers(svd)
self.assertNotIn("<msb>32</msb>", svd)
self.assertEqual(registers["WIDE1"], {
"middle": {"msb": "15", "lsb": "0", "bitRange": "[15:0]"},
})
self.assertEqual(registers["WIDE0"], {
"middle": {"msb": "31", "lsb": "16", "bitRange": "[31:16]"},
})
def test_svd_exports_csr_field_enumerated_values(self):
csr = CSRStorage(name="mode", fields=[
CSRField("select", size=1, offset=0, values=[
("``0b0``", "Software (CPU) control."),
("``0b1``", "Hardware control (default)."),
]),
])
svd = _get_csr_svd(csr)
enums = _get_svd_field_enums(svd, "MODE", "select")
self.assertEqual(enums, [
{
"name": "Software_CPU_control",
"description": "Software (CPU) control.",
"value": "0b0",
},
{
"name": "Hardware_control_default",
"description": "Hardware control (default).",
"value": "0b1",
},
])
def test_svd_sanitizes_and_filters_csr_field_enums(self):
csr = CSRStatus(name="state", fields=[
CSRField("fsm", size=2, offset=0, description="FSM state.", values=[
("``0b00``", "Idle State", "Waiting for work."),
("``0b01``", "Active/State", "Running ]]> work."),
("``0b0..1``", "Unsupported", "Non-SVD wildcard."),
("``0b10``", "Active/State", "Duplicate name."),
("``0xxxxx``", "UnsupportedRaw", "Missing binary prefix."),
"invalid",
]),
])
svd = _get_csr_svd(csr)
enums = _get_svd_field_enums(svd, "STATE", "fsm")
self.assertEqual(enums, [
{
"name": "Idle_State",
"description": "Waiting for work.",
"value": "0b00",
},
{
"name": "Active_State",
"description": "Running ]]> work.",
"value": "0b01",
},
{
"name": "Active_State_0b10",
"description": "Duplicate name.",
"value": "0b10",
},
])
if __name__ == "__main__":
unittest.main()
58322a1d2This file is pinned to the selected release. Download the original to open it in your design tool.