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]
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):
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`."""
@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>)
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.
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.