modm_data.cubehal

STMicro STM32CubeHAL Source Code

The STM32CubeHAL source code provides useful information:

  • Determine canonical names of conflicting data items.
  • Determine the map of register bit field values to names.
 1# Copyright 2022, Niklas Hauser
 2# SPDX-License-Identifier: MPL-2.0
 3
 4"""
 5# STMicro STM32CubeHAL Source Code
 6
 7The STM32CubeHAL source code provides useful information:
 8
 9- Determine canonical names of conflicting data items.
10- Determine the map of register bit field values to names.
11"""
12
13from .dmamux_requests import read_request_map, read_bdma_request_map
14from .header import read_header
15from .registers import RegisterAccess, LLFunction, register_accesses, ll_functions, ll_descriptions
16
17__all__ = [
18    "read_request_map",
19    "read_bdma_request_map",
20    "read_header",
21    "RegisterAccess",
22    "LLFunction",
23    "register_accesses",
24    "ll_functions",
25    "ll_descriptions",
26]
def read_request_map(did: modm_data.kg.DeviceIdentifier) -> dict[str, int]:
17def read_request_map(did: DeviceIdentifier) -> dict[str, int]:
18    """
19    Reads the DMA requests mapping from the Low-Level (LL) CubeHAL header files.
20
21    :param did: Device to query for.
22    :return: A dictionary of DMA trigger name to trigger position.
23    """
24    dma_header = _get_hal_dma_header_path(did)
25    dmamux_header = _get_ll_dmamux_header_path(did)
26    request_map = None
27    if did.family in ["c0", "g4", "h7", "l5"]:
28        request_map = _read_requests(dma_header, _REQUEST_PATTERN)
29    elif did.family in ["g0", "u0", "wb", "wl"]:
30        request_map = _read_requests_from_ll_dmamux(dma_header, dmamux_header)
31    elif did.family == "l4" and did.name[0] in ["p", "q", "r", "s"]:
32        request_map = _read_requests_l4(dma_header, dmamux_header, did)
33    else:
34        raise RuntimeError("No DMAMUX request data available for {}".format(did))
35    _fix_request_data(request_map, "DMA")
36    return request_map

Reads the DMA requests mapping from the Low-Level (LL) CubeHAL header files.

Parameters
  • did: Device to query for.
Returns

A dictionary of DMA trigger name to trigger position.

def read_bdma_request_map(did):
39def read_bdma_request_map(did):
40    dma_header = _get_hal_dma_header_path(did)
41    request_map = _read_requests(dma_header, _BDMA_REQUEST_PATTERN)
42    _fix_request_data(request_map, "BDMA")
43    return request_map
def read_header(did):
253def read_header(did):
254    """
255    Finds all register and bit names in the CMSIS header file.
256
257    :returns: a RegisterMap object that allows regex-ing for register names.
258    """
259    family_folder = f"stm32{did.family}xx"
260    if did.string[5:8] in ["h7r", "h7s"]:
261        family_folder = "stm32h7rsxx"
262    elif did.string[5:8] == "wba":
263        family_folder = "stm32wbaxx"
264    family_header = f"{family_folder}.h"
265    if did.string[5:8] == "wb0":
266        family_folder = "stm32wb0xx"
267        family_header = "stm32wb0x.h"
268    elif did.string[5:8] == "wl3":
269        family_folder = "stm32wl3xx"
270        family_header = "stm32wl3x.h"
271    family_header = (_HEADER_PATH / family_folder / "Include" / family_header).read_text(
272        encoding="utf-8", errors="replace"
273    )
274    match = re.findall(r"if +defined\( *(STM32[A-Z][\w\d]+) *\)", family_header)
275    assert match, f"No CPP define match found for '{did.string}'!"
276    device_define = _get_define_for_device(did, match)
277    assert device_define, f"No device define found for '{did.string}'!"
278
279    if (pkl := _CACHE_PATH / family_folder / f"{device_define}.pkl").exists():
280        return pickle.loads(pkl.read_bytes())
281
282    values = _read_header(device_define)
283    pkl.write_bytes(pickle.dumps(values, protocol=pickle.HIGHEST_PROTOCOL))
284    return values

Finds all register and bit names in the CMSIS header file.

:returns: a RegisterMap object that allows regex-ing for register names.

@dataclass
class RegisterAccess:
35@dataclass
36class RegisterAccess:
37    kind: str
38    """The access macro, e.g. `MODIFY_REG`."""
39    member: str
40    """The register member of the structure, e.g. `CR1`."""
41    types: set[str]
42    """The possible structure types of the accessed variable."""
43    variable: str
44    """The name of the accessed variable or instance, e.g. `USARTx` or `RCC`."""
45    masks: set[str]
46    """The identifiers in the mask argument, e.g. `USART_CR1_UE`."""
RegisterAccess( kind: str, member: str, types: set[str], variable: str, masks: set[str])
kind: str

The access macro, e.g. MODIFY_REG.

member: str

The register member of the structure, e.g. CR1.

types: set[str]

The possible structure types of the accessed variable.

variable: str

The name of the accessed variable or instance, e.g. USARTx or RCC.

masks: set[str]

The identifiers in the mask argument, e.g. USART_CR1_UE.

@dataclass
class LLFunction:
49@dataclass
50class LLFunction:
51    name: str
52    """The name of the function, e.g. `LL_USART_SetParity`."""
53    file: str
54    """The name of the header file."""
55    typedef: str | None
56    """The structure type of the instance parameter."""
57    rmtoll: list[tuple[str, str]] = field(default_factory=list)
58    """The register and bit field names in the reference manual."""
59    values: list[str] = field(default_factory=list)
60    """The enumerated values of the parameter or return value."""
61    instance_macros: set[str] = field(default_factory=set)
62    """The `IS_*_INSTANCE` macros that check for instance support."""
63    accesses: list[RegisterAccess] = field(default_factory=list)
64    """The register accesses in the function body."""
LLFunction( name: str, file: str, typedef: str | None, rmtoll: list[tuple[str, str]] = <factory>, values: list[str] = <factory>, instance_macros: set[str] = <factory>, accesses: list[RegisterAccess] = <factory>)
name: str

The name of the function, e.g. LL_USART_SetParity.

file: str

The name of the header file.

typedef: str | None

The structure type of the instance parameter.

rmtoll: list[tuple[str, str]]

The register and bit field names in the reference manual.

values: list[str]

The enumerated values of the parameter or return value.

instance_macros: set[str]

The IS_*_INSTANCE macros that check for instance support.

accesses: list[RegisterAccess]

The register accesses in the function body.

@cache
def register_accesses(path: pathlib.Path) -> list[RegisterAccess]:
118@cache
119def register_accesses(path: Path) -> list[RegisterAccess]:
120    """
121    :param path: the CubeHAL folder of a family.
122    :return: all register accesses with a structure member in the HAL and LL source code.
123    """
124    texts = {file: _read(file) for file in sorted(path.glob("*/*.[ch]"))}
125    # Variables and handle members are declared per module, e.g. UART_HandleTypeDef::Instance in hal_uart.h
126    modules = defaultdict(lambda: defaultdict(set))
127    for file, text in texts.items():
128        module = re.sub(r"(_ex)?\.[ch]$", "", file.name)
129        for typedef, variable in re.findall(r"\b(\w+_TypeDef)\s*\*\s*(?:const\s+)?(\w+)", text):
130            modules[module][variable].add(typedef)
131    accesses = []
132    for file, text in texts.items():
133        accesses += _accesses(text, modules[re.sub(r"(_ex)?\.[ch]$", "", file.name)])
134    return accesses
Parameters
  • path: the CubeHAL folder of a family.
Returns

all register accesses with a structure member in the HAL and LL source code.

@cache
def ll_functions(path: pathlib.Path) -> list[LLFunction]:
137@cache
138def ll_functions(path: Path) -> list[LLFunction]:
139    """
140    :param path: the CubeHAL folder of a family.
141    :return: all documented LL inline functions.
142    """
143    functions = []
144    for file in sorted((path / "Inc").glob("*_ll_*.h")):
145        text = file.read_text(encoding="utf-8", errors="replace")
146        for match in _FUNCTION.finditer(text):
147            doc, name, parameters = match.groups()
148            body = text[match.end() : text.find("\n}", match.end())]
149            typedef = re.search(r"(\w+_TypeDef)\s*\*\s*(\w+)", parameters)
150            function = LLFunction(name, file.name, typedef.group(1) if typedef else None)
151            for rmtoll in re.finditer(r"@rmtoll\s+(.*?)(?=@param|@retval|@note|@brief|$)", doc, flags=re.S):
152                for line in rmtoll.group(1).split("\\n"):
153                    if len(parts := line.replace("*", " ").split()) >= 2:
154                        function.rmtoll.append((parts[0], parts[1]))
155            function.values = re.findall(r"@arg\s+@ref\s+(LL_\w+)", doc)
156            function.instance_macros = set(re.findall(r"\b(IS_\w+_INSTANCE)\b", doc))
157            types = {typedef.group(2): {typedef.group(1)}} if typedef else {}
158            function.accesses = _accesses(body, types)
159            functions.append(function)
160    return functions
Parameters
  • path: the CubeHAL folder of a family.
Returns

all documented LL inline functions.

@cache
def ll_descriptions(path: pathlib.Path) -> dict[str, str]:
163@cache
164def ll_descriptions(path: Path) -> dict[str, str]:
165    """:return: the descriptions of all LL macros, e.g. `LL_USART_PARITY_EVEN`."""
166    descriptions = {}
167    for file in sorted((path / "Inc").glob("*_ll_*.h")):
168        text = file.read_text(encoding="utf-8", errors="replace")
169        for name, description in re.findall(r"#define\s+(LL_\w+)\s+[^\n]*?/\*!<\s*(.*?)\s*\*/", text):
170            descriptions.setdefault(name, " ".join(description.split()))
171    return descriptions
Returns

the descriptions of all LL macros, e.g. LL_USART_PARITY_EVEN.