1327 lines
48 KiB
Python
Executable File
1327 lines
48 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import sys
|
|
import xmltodict
|
|
import functools
|
|
import re
|
|
import json
|
|
import os
|
|
from glob import glob
|
|
from pathlib import Path
|
|
|
|
from stm32data import yaml, header, interrupts, memory
|
|
from stm32data.util import *
|
|
|
|
|
|
def corename(d):
|
|
# print("CHECKING CORENAME", d)
|
|
if m := re.match(r'.*Cortex-M(\d+)(\+?)\s*(.*)', d):
|
|
name = "cm" + str(m.group(1))
|
|
if m.group(2) == "+":
|
|
name += "p"
|
|
if m.group(3) == "secure":
|
|
name += "s"
|
|
return name
|
|
|
|
|
|
def children(x, key):
|
|
r = x.get(key)
|
|
if r is None:
|
|
return []
|
|
if type(r) is list:
|
|
return r
|
|
return [r]
|
|
|
|
|
|
def expand_name(name):
|
|
if '(' not in name:
|
|
return [name]
|
|
prefix, suffix = name.split('(')
|
|
letters, suffix = suffix.split(')')
|
|
return [prefix + x + suffix for x in letters.split('-')]
|
|
|
|
|
|
# ========================================
|
|
# ========================================
|
|
|
|
FAKE_PERIPHERALS = [
|
|
# These are real peripherals but with special handling
|
|
'NVIC',
|
|
'GPIO',
|
|
'DMA',
|
|
|
|
# IRTIM is just TIM16+TIM17
|
|
'IRTIM',
|
|
|
|
# I2S is just SPI on disguise
|
|
'I2S1',
|
|
'I2S2',
|
|
'I2S3',
|
|
'I2S4',
|
|
'I2S5',
|
|
'I2S6',
|
|
'I2S7',
|
|
'I2S8',
|
|
|
|
# We add this as ghost peri
|
|
'SYS',
|
|
|
|
# These are software libraries
|
|
'FREERTOS',
|
|
'PDM2PCM',
|
|
'FATFS',
|
|
'LIBJPEG',
|
|
'MBEDTLS',
|
|
'LWIP',
|
|
'USB_HOST',
|
|
'USB_DEVICE',
|
|
'GUI_INTERFACE',
|
|
'TRACER_EMB',
|
|
'TOUCHSENSING',
|
|
]
|
|
|
|
perimap = [
|
|
('.*:USART:sci2_v1_1', ('usart', 'v1', 'USART')),
|
|
('.*:USART:sci2_v1_2_F1', ('usart', 'v1', 'USART')),
|
|
('.*:USART:sci2_v1_2', ('usart', 'v1', 'USART')),
|
|
('.*:USART:sci2_v2_0', ('usart', 'v2', 'USART')),
|
|
('.*:USART:sci2_v2_1', ('usart', 'v2', 'USART')),
|
|
('.*:USART:sci2_v2_2', ('usart', 'v2', 'USART')),
|
|
('.*:USART:sci3_v1_0', ('usart', 'v2', 'USART')),
|
|
('.*:USART:sci3_v1_1', ('usart', 'v2', 'USART')),
|
|
('.*:USART:sci3_v1_2', ('usart', 'v2', 'USART')),
|
|
('.*:USART:sci3_v2_0', ('usart', 'v2', 'USART')),
|
|
('.*:USART:sci3_v2_1', ('usart', 'v2', 'USART')),
|
|
('.*:UART:sci2_v1_2_F4', ('usart', 'v1', 'USART')),
|
|
('.*:UART:sci2_v2_1', ('usart', 'v2', 'USART')),
|
|
('.*:UART:sci2_v3_0', ('usart', 'v2', 'USART')),
|
|
('.*:UART:sci2_v3_1', ('usart', 'v2', 'USART')),
|
|
('.*:LPUART:sci3_v1_1', ('lpuart', 'v1', 'LPUART')),
|
|
('.*:LPUART:sci3_v1_2', ('lpuart', 'v2', 'LPUART')),
|
|
('.*:LPUART:sci3_v1_3', ('lpuart', 'v2', 'LPUART')),
|
|
('.*:LPUART:sci3_v1_4', ('lpuart', 'v2', 'LPUART')),
|
|
('.*:RNG:rng1_v1_1', ('rng', 'v1', 'RNG')),
|
|
('.*:RNG:rng1_v2_0', ('rng', 'v1', 'RNG')),
|
|
('.*:RNG:rng1_v2_1', ('rng', 'v1', 'RNG')),
|
|
('.*:RNG:rng1_v3_1', ('rng', 'v1', 'RNG')),
|
|
('.*:SPI:spi2_v1_4', ('spi', 'f1', 'SPI')),
|
|
('.*:SPI:spi2s1_v2_1', ('spi', 'v1', 'SPI')),
|
|
('.*:SPI:spi2s1_v2_2', ('spi', 'v1', 'SPI')),
|
|
('.*:SPI:spi2s1_v3_2', ('spi', 'v2', 'SPI')),
|
|
('.*:SPI:spi2s1_v3_3', ('spi', 'v2', 'SPI')),
|
|
('.*:SPI:spi2s1_v3_5', ('spi', 'v2', 'SPI')),
|
|
('.*:SUBGHZSPI:.*', ('spi', 'v2', 'SPI')),
|
|
('.*:SPI:spi2s1_v3_1', ('spi', 'v2', 'SPI')),
|
|
('.*:SPI:spi2s2_v1_1', ('spi', 'v3', 'SPI')),
|
|
('.*:SPI:spi2s2_v1_0', ('spi', 'v3', 'SPI')),
|
|
('.*:SPI:spi2s3_v1_1', ('spi', 'v4', 'SPI')),
|
|
('.*:I2C:i2c1_v1_5', ('i2c', 'v1', 'I2C')),
|
|
('.*:I2C:i2c2_v1_1', ('i2c', 'v2', 'I2C')),
|
|
('.*:I2C:i2c2_v1_1F7', ('i2c', 'v2', 'I2C')),
|
|
('.*:I2C:i2c2_v1_1U5', ('i2c', 'v2', 'I2C')),
|
|
|
|
('.*:DAC:dacif_v1_1', ('dac', 'v1', 'DAC')),
|
|
('.*:DAC:dacif_v2_0', ('dac', 'v2', 'DAC')),
|
|
('.*:DAC:dacif_v3_0', ('dac', 'v2', 'DAC')),
|
|
|
|
('.*:ADC:aditf_v2_5F1', ('adc', 'f1', 'ADC')),
|
|
('.*:ADC:aditf4_v1_1', ('adc', 'v1', 'ADC')),
|
|
('.*:ADC:aditf2_v1_1', ('adc', 'v2', 'ADC')),
|
|
('.*:ADC:aditf5_v2_0', ('adc', 'v3', 'ADC')),
|
|
('.*:ADC:aditf5_v3_0', ('adc', 'v4', 'ADC')),
|
|
('STM32G0.*:ADC:.*', ('adc', 'g0', 'ADC')),
|
|
('STM32G0.*:ADC_COMMON:.*', ('adccommon', 'v3', 'ADC_COMMON')),
|
|
('.*:ADC_COMMON:aditf2_v1_1', ('adccommon', 'v2', 'ADC_COMMON')),
|
|
('.*:ADC_COMMON:aditf5_v2_0', ('adccommon', 'v3', 'ADC_COMMON')),
|
|
('.*:ADC_COMMON:aditf4_v3_0_WL', ('adccommon', 'v3', 'ADC_COMMON')),
|
|
('STM32H7.*:ADC_COMMON:.*', ('adccommon', 'v4', 'ADC_COMMON')),
|
|
('STM32H7.*:ADC3_COMMON:.*', ('adccommon', 'v4', 'ADC_COMMON')),
|
|
|
|
('.*:DCMI:.*', ('dcmi', 'v1', 'DCMI')),
|
|
('STM32F0.*:SYSCFG:.*', ('syscfg', 'f0', 'SYSCFG')),
|
|
('STM32F2.*:SYSCFG:.*', ('syscfg', 'f2', 'SYSCFG')),
|
|
('STM32F3.*:SYSCFG:.*', ('syscfg', 'f3', 'SYSCFG')),
|
|
('STM32F4.*:SYSCFG:.*', ('syscfg', 'f4', 'SYSCFG')),
|
|
('STM32F7.*:SYSCFG:.*', ('syscfg', 'f7', 'SYSCFG')),
|
|
('STM32L0.*:SYSCFG:.*', ('syscfg', 'l0', 'SYSCFG')),
|
|
('STM32L1.*:SYSCFG:.*', ('syscfg', 'l1', 'SYSCFG')),
|
|
('STM32L4.*:SYSCFG:.*', ('syscfg', 'l4', 'SYSCFG')),
|
|
('STM32L5.*:SYSCFG:.*', ('syscfg', 'l5', 'SYSCFG')),
|
|
('STM32G0.*:SYSCFG:.*', ('syscfg', 'g0', 'SYSCFG')),
|
|
('STM32G4.*:SYSCFG:.*', ('syscfg', 'g4', 'SYSCFG')),
|
|
('STM32H7.*:SYSCFG:.*', ('syscfg', 'h7', 'SYSCFG')),
|
|
('STM32U5.*:SYSCFG:.*', ('syscfg', 'u5', 'SYSCFG')),
|
|
('STM32WB.*:SYSCFG:.*', ('syscfg', 'wb', 'SYSCFG')),
|
|
('STM32WL5.*:SYSCFG:.*', ('syscfg', 'wl5', 'SYSCFG')),
|
|
('STM32WLE.*:SYSCFG:.*', ('syscfg', 'wle', 'SYSCFG')),
|
|
|
|
('.*:IWDG:iwdg1_v2_0', ('iwdg', 'v2', 'IWDG')),
|
|
('.*:WWDG:wwdg1_v1_0', ('wwdg', 'v1', 'WWDG')),
|
|
('.*:JPEG:jpeg1_v1_0', ('jpeg', 'v1', 'JPEG')),
|
|
('.*:LPTIM:F7_lptimer1_v1_1', ('lptim', 'v1', 'LPTIM')),
|
|
('.*:LTDC:lcdtft1_v1_1', ('ltdc', 'v1', 'LTDC')),
|
|
('.*:MDIOS:mdios1_v1_0', ('mdios', 'v1', 'MDIOS')),
|
|
('.*:QUADSPI:quadspi1_v1_0', ('quadspi', 'v1', 'QUADSPI')),
|
|
('STM32F1.*:BKP.*', ('bkp', 'v1', 'BKP')),
|
|
('.*:RTC:rtc1_v1_1', ('rtc', 'v1', 'RTC')),
|
|
('STM32F0.*:RTC:rtc2_.*', ('rtc', 'v2f0', 'RTC')),
|
|
('STM32F2.*:RTC:rtc2_.*', ('rtc', 'v2f2', 'RTC')),
|
|
('STM32F3.*:RTC:rtc2_.*', ('rtc', 'v2f3', 'RTC')),
|
|
('STM32F4.*:RTC:rtc2_.*', ('rtc', 'v2f4', 'RTC')),
|
|
('STM32F7.*:RTC:rtc2_.*', ('rtc', 'v2f7', 'RTC')),
|
|
('STM32H7.*:RTC:rtc2_.*', ('rtc', 'v2h7', 'RTC')),
|
|
('STM32L0.*:RTC:rtc2_.*', ('rtc', 'v2l0', 'RTC')),
|
|
('STM32L1.*:RTC:rtc2_.*', ('rtc', 'v2l1', 'RTC')),
|
|
('STM32L4.*:RTC:rtc2_.*', ('rtc', 'v2l4', 'RTC')),
|
|
('STM32WB.*:RTC:rtc2_.*', ('rtc', 'v2wb', 'RTC')),
|
|
('STM32U5.*:RTC:rtc2_.*', ('rtc', 'v3u5', 'RTC')), # Cube says v2, but it's v3 with security stuff
|
|
('.*:RTC:rtc3_v1_0', ('rtc', 'v3', 'RTC')),
|
|
('.*:RTC:rtc3_v1_1', ('rtc', 'v3', 'RTC')),
|
|
('.*:RTC:rtc3_v2_0', ('rtc', 'v3', 'RTC')),
|
|
('.*:RTC:rtc3_v3_0', ('rtc', 'v3', 'RTC')),
|
|
('.*:SAI:sai1_v1_1', ('sai', 'v1', 'SAI')),
|
|
('.*:SDIO:sdmmc_v1_2', ('sdmmc', 'v1', 'SDMMC')),
|
|
('.*:SDMMC:sdmmc_v1_3', ('sdmmc', 'v1', 'SDMMC')),
|
|
('.*:SPDIFRX:spdifrx1_v1_0', ('spdifrx', 'v1', 'SPDIFRX')),
|
|
|
|
# USB
|
|
('STM32F302.[BC].*:USB:.*', ('usb', 'v1_x1', 'USB')),
|
|
('STM32F302.[68DE].*:USB:.*', ('usb', 'v2', 'USB')),
|
|
('STM32F303.[BC].*:USB:.*', ('usb', 'v1_x1', 'USB')),
|
|
('STM32F303.[DE].*:USB:.*', ('usb', 'v2', 'USB')),
|
|
('STM32F373.*:USB:.*', ('usb', 'v1_x2', 'USB')),
|
|
('STM32(F1|L1).*:USB:.*', ('usb', 'v1_x1', 'USB')),
|
|
('.*:USB:.*', ('usb', 'v3', 'USB')),
|
|
|
|
# USB OTG
|
|
('.*:USB_OTG_FS:otgfs1_v1_.*', ('otgfs', 'v1', 'OTG_FS')),
|
|
('.*:USB_OTG_FS:otgfs1_v3_.*', ('otgfs', 'v1', 'OTG_FS')),
|
|
('.*:USB_OTG_HS:otghs1_v1_.*', ('otghs', 'v1', 'OTG_HS')),
|
|
|
|
('STM32F0.*:RCC:.*', ('rcc', 'f0', 'RCC')),
|
|
('STM32F100.*:RCC:.*', ('rcc', 'f100', 'RCC')),
|
|
('STM32F10[123].*:RCC:.*', ('rcc', 'f1', 'RCC')),
|
|
('STM32F10[57].*:RCC:.*', ('rcc', 'f1cl', 'RCC')),
|
|
('STM32F2.*:RCC:.*', ('rcc', 'f2', 'RCC')),
|
|
('STM32F3.*:RCC:.*', ('rcc', 'f3', 'RCC')),
|
|
('STM32F410.*:RCC:.*', ('rcc', 'f410', 'RCC')),
|
|
('STM32F4.*:RCC:.*', ('rcc', 'f4', 'RCC')),
|
|
('STM32F7.*:RCC:.*', ('rcc', 'f7', 'RCC')),
|
|
('STM32G0.*:RCC:.*', ('rcc', 'g0', 'RCC')),
|
|
('STM32G4.*:RCC:.*', ('rcc', 'g4', 'RCC')),
|
|
('STM32H7[AB].*:RCC:.*', ('rcc', 'h7ab', 'RCC')),
|
|
('STM32H7.*:RCC:.*', ('rcc', 'h7', 'RCC')),
|
|
('STM32L0.*:RCC:.*', ('rcc', 'l0', 'RCC')),
|
|
('STM32L1.*:RCC:.*', ('rcc', 'l1', 'RCC')),
|
|
('STM32L4.*:RCC:.*', ('rcc', 'l4', 'RCC')),
|
|
('STM32L5.*:RCC:.*', ('rcc', 'l5', 'RCC')),
|
|
('STM32U5.*:RCC:.*', ('rcc', 'u5', 'RCC')),
|
|
('STM32WB.*:RCC:.*', ('rcc', 'wb', 'RCC')),
|
|
('STM32WL5.*:RCC:.*', ('rcc', 'wl5', 'RCC')),
|
|
('STM32WLE.*:RCC:.*', ('rcc', 'wle', 'RCC')),
|
|
|
|
('STM32F1.*:SPI[1234]:.*', ('spi', 'f1', 'SPI')),
|
|
('STM32F3.*:SPI[1234]:.*', ('spi', 'v2', 'SPI')),
|
|
|
|
('STM32F1.*:AFIO:.*', ('afio', 'f1', 'AFIO')),
|
|
|
|
('STM32L5.*:EXTI:.*', ('exti', 'l5', 'EXTI')),
|
|
('STM32G0.*:EXTI:.*', ('exti', 'g0', 'EXTI')),
|
|
('STM32H7.*:EXTI:.*', ('exti', 'h7', 'EXTI')),
|
|
('STM32U5.*:EXTI:.*', ('exti', 'u5', 'EXTI')),
|
|
('STM32WB.*:EXTI:.*', ('exti', 'w', 'EXTI')),
|
|
('STM32WL5.*:EXTI:.*', ('exti', 'w', 'EXTI')),
|
|
('STM32WLE.*:EXTI:.*', ('exti', 'wle', 'EXTI')),
|
|
('.*:EXTI:.*', ('exti', 'v1', 'EXTI')),
|
|
|
|
('STM32L0.*:CRS:.*', ('crs', 'l0', 'CRS')),
|
|
('.*SDMMC:sdmmc2_v1_0', ('sdmmc', 'v2', 'SDMMC')),
|
|
('STM32G0.*:PWR:.*', ('pwr', 'g0', 'PWR')),
|
|
('STM32G4.*:PWR:.*', ('pwr', 'g4', 'PWR')),
|
|
('STM32H7(42|43|53|50).*:PWR:.*', ('pwr', 'h7', 'PWR')),
|
|
('STM32H7.*:PWR:.*', ('pwr', 'h7smps', 'PWR')),
|
|
('STM32F2.*:PWR:.*', ('pwr', 'f2', 'PWR')),
|
|
('STM32F3.*:PWR:.*', ('pwr', 'f3', 'PWR')),
|
|
('STM32F4.*:PWR:.*', ('pwr', 'f4', 'PWR')),
|
|
('STM32F7.*:PWR:.*', ('pwr', 'f7', 'PWR')),
|
|
('STM32L1.*:PWR:.*', ('pwr', 'l1', 'PWR')),
|
|
('STM32L4.*:PWR:.*', ('pwr', 'l4', 'PWR')),
|
|
('STM32L5.*:PWR:.*', ('pwr', 'l5', 'PWR')),
|
|
('STM32U5.*:PWR:.*', ('pwr', 'u5', 'PWR')),
|
|
('STM32WL.*:PWR:.*', ('pwr', 'wl5', 'PWR')),
|
|
('STM32WB.*:PWR:.*', ('pwr', 'wb55', 'PWR')),
|
|
('STM32H7.*:FLASH:.*', ('flash', 'h7', 'FLASH')),
|
|
('STM32F0.*:FLASH:.*', ('flash', 'f0', 'FLASH')),
|
|
('STM32F1.*:FLASH:.*', ('flash', 'f1', 'FLASH')),
|
|
('STM32F2.*:FLASH:.*', ('flash', 'f2', 'FLASH')),
|
|
('STM32F3.*:FLASH:.*', ('flash', 'f3', 'FLASH')),
|
|
('STM32F4.*:FLASH:.*', ('flash', 'f4', 'FLASH')),
|
|
('STM32F7.*:FLASH:.*', ('flash', 'f7', 'FLASH')),
|
|
('STM32L0[0-9]2.*:FLASH:.*', ('flash', 'l0', 'FLASH')),
|
|
('STM32L1.*:FLASH:.*', ('flash', 'l1', 'FLASH')),
|
|
('STM32L4.*:FLASH:.*', ('flash', 'l4', 'FLASH')),
|
|
('STM32L5.*:FLASH:.*', ('flash', 'l5', 'FLASH')),
|
|
('STM32U5.*:FLASH:.*', ('flash', 'u5', 'FLASH')),
|
|
('STM32WB.*:FLASH:.*', ('flash', 'wb', 'FLASH')),
|
|
('STM32WL.*:FLASH:.*', ('flash', 'wl', 'FLASH')),
|
|
('STM32G0.*:FLASH:.*', ('flash', 'g0', 'FLASH')),
|
|
('STM32F107.*:ETH:.*', ('eth', 'v1a', 'ETH')),
|
|
('STM32F[24].*:ETH:.*', ('eth', 'v1b', 'ETH')),
|
|
('STM32F7.*:ETH:ETH:ethermac110_v2_0', ('eth', 'v1c', 'ETH')),
|
|
('.*ETH:ethermac110_v3_0', ('eth', 'v2', 'ETH')),
|
|
|
|
('.*:FSMC:.*', ('fsmc', 'v1', 'FSMC')),
|
|
('STM32H7.*:FMC:.*', ('fmc', 'h7', 'FMC')),
|
|
|
|
(r'.*LPTIM\d.*:G0xx_lptimer1_v1_4', ('lptim', 'g0', 'LPTIM')),
|
|
|
|
('STM32H7.*:TIM1:.*', ('timer', 'v1', 'TIM_ADV')),
|
|
('STM32H7.*:TIM2:.*', ('timer', 'v1', 'TIM_GP32')),
|
|
('STM32H7.*:TIM5:.*', ('timer', 'v1', 'TIM_GP32')),
|
|
('STM32H7.*:TIM6:.*', ('timer', 'v1', 'TIM_BASIC')),
|
|
('STM32H7.*:TIM7:.*', ('timer', 'v1', 'TIM_BASIC')),
|
|
('STM32H7.*:TIM8:.*', ('timer', 'v1', 'TIM_ADV')),
|
|
|
|
('STM32F3.*:TIM(6|7){1}:.*', ('timer', 'v1', 'TIM_BASIC')),
|
|
('STM32F3.*:TIM(3|4|15|16|17){1}:.*', ('timer', 'v1', 'TIM_GP16')),
|
|
('STM32F3.*:TIM2:.*', ('timer', 'v1', 'TIM_GP32')),
|
|
('STM32F3.*:TIM(1|8|20){1}:.*', ('timer', 'v1', 'TIM_ADV')),
|
|
|
|
('STM32F7.*:TIM1:.*', ('timer', 'v1', 'TIM_ADV')),
|
|
('STM32F7.*:TIM8:.*', ('timer', 'v1', 'TIM_ADV')),
|
|
(r'.*TIM\d.*:gptimer.*', ('timer', 'v1', 'TIM_GP16')),
|
|
|
|
('STM32F0.*:DBGMCU:.*', ('dbgmcu', 'f0', 'DBGMCU')),
|
|
('STM32F1.*:DBGMCU:.*', ('dbgmcu', 'f1', 'DBGMCU')),
|
|
('STM32F2.*:DBGMCU:.*', ('dbgmcu', 'f2', 'DBGMCU')),
|
|
('STM32F3.*:DBGMCU:.*', ('dbgmcu', 'f3', 'DBGMCU')),
|
|
('STM32F4.*:DBGMCU:.*', ('dbgmcu', 'f4', 'DBGMCU')),
|
|
('STM32F7.*:DBGMCU:.*', ('dbgmcu', 'f7', 'DBGMCU')),
|
|
('STM32G0.*:DBGMCU:.*', ('dbgmcu', 'g0', 'DBGMCU')),
|
|
('STM32G4.*:DBGMCU:.*', ('dbgmcu', 'g4', 'DBGMCU')),
|
|
('STM32H7.*:DBGMCU:.*', ('dbgmcu', 'h7', 'DBGMCU')),
|
|
('STM32L0.*:DBGMCU:.*', ('dbgmcu', 'l0', 'DBGMCU')),
|
|
('STM32L1.*:DBGMCU:.*', ('dbgmcu', 'l1', 'DBGMCU')),
|
|
('STM32L4.*:DBGMCU:.*', ('dbgmcu', 'l4', 'DBGMCU')),
|
|
('STM32U5.*:DBGMCU:.*', ('dbgmcu', 'u5', 'DBGMCU')),
|
|
('STM32WB.*:DBGMCU:.*', ('dbgmcu', 'wb', 'DBGMCU')),
|
|
('STM32WL.*:DBGMCU:.*', ('dbgmcu', 'wl', 'DBGMCU')),
|
|
|
|
('STM32F1.*:GPIO.*', ('gpio', 'v1', 'GPIO')),
|
|
('.*:GPIO.*', ('gpio', 'v2', 'GPIO')),
|
|
|
|
('.*:IPCC:v1_0', ('ipcc', 'v1', 'IPCC')),
|
|
('.*:DMAMUX.*', ('dmamux', 'v1', 'DMAMUX')),
|
|
|
|
(r'.*:GPDMA\d?:.*', ('gpdma', 'v1', 'GPDMA')),
|
|
(r'.*:BDMA\d?:.*', ('bdma', 'v1', 'DMA')),
|
|
('STM32H7.*:DMA2D:DMA2D:dma2d1_v1_0', ('dma2d', 'v2', 'DMA2D')),
|
|
('.*:DMA2D:dma2d1_v1_0', ('dma2d', 'v1', 'DMA2D')),
|
|
('STM32L4[PQRS].*:DMA.*', ('bdma', 'v1', 'DMA')), # L4+
|
|
('STM32L[04].*:DMA.*', ('bdma', 'v2', 'DMA')), # L0, L4 non-plus (since plus is handled above)
|
|
('STM32F030.C.*:DMA.*', ('bdma', 'v2', 'DMA')), # Weird F0
|
|
('STM32F09.*:DMA.*', ('bdma', 'v2', 'DMA')), # Weird F0
|
|
('STM32F[247].*:DMA.*', ('dma', 'v2', 'DMA')),
|
|
('STM32H7.*:DMA.*', ('dma', 'v1', 'DMA')),
|
|
('.*:DMA.*', ('bdma', 'v1', 'DMA')),
|
|
|
|
('.*:CAN:bxcan1_v1_1.*', ('can', 'bxcan', 'CAN')),
|
|
# stm32F4 CRC peripheral
|
|
# ("STM32F4*:CRC:CRC:crc_f4")
|
|
# v1: F1, F2, F4, L1
|
|
# v2, adds INIT reg: F0
|
|
# v3, adds POL reg: F3, F7, G0, G4, H7, L0, L4, L5, WB, WL
|
|
('.*:CRC:integtest1_v1_0', ('crc', 'v1', 'CRC')),
|
|
('STM32L[04].*:CRC:integtest1_v2_0', ('crc', 'v3', 'CRC')),
|
|
('.*:CRC:integtest1_v2_0', ('crc', 'v2', 'CRC')),
|
|
('.*:CRC:integtest1_v2_2', ('crc', 'v3', 'CRC')),
|
|
|
|
('.*:LCD:lcdc1_v1.0.*', ('lcd', 'v1', 'LCD')),
|
|
('.*:LCD:lcdc1_v1.2.*', ('lcd', 'v2', 'LCD')),
|
|
('.*:LCD:lcdc1_v1.3.*', ('lcd', 'v2', 'LCD')),
|
|
]
|
|
|
|
peri_rename = {
|
|
'HDMI_CEC': 'CEC',
|
|
'SUBGHZ': 'SUBGHZSPI',
|
|
}
|
|
|
|
ghost_peris = [
|
|
'GPIOA', 'GPIOB', 'GPIOC', 'GPIOD', 'GPIOE', 'GPIOF', 'GPIOG', 'GPIOH', 'GPIOI', 'GPIOJ', 'GPIOK', 'GPIOL', 'GPIOM', 'GPION', 'GPIOO', 'GPIOP', 'GPIOQ', 'GPIOR', 'GPIOS', 'GPIOT',
|
|
'DMA1', 'DMA2', 'BDMA', 'DMAMUX', 'DMAMUX1', 'DMAMUX2',
|
|
'SYSCFG', 'EXTI', 'FLASH', 'DBGMCU', 'CRS', 'PWR', 'AFIO', 'BKP',
|
|
]
|
|
|
|
alt_peri_defines = {
|
|
'DBGMCU': ['DBGMCU_BASE', 'DBG_BASE'],
|
|
'FLASH': ['FLASH_R_BASE', 'FLASH_REG_BASE'],
|
|
'ADC_COMMON': ['ADC_COMMON', 'ADC1_COMMON', 'ADC12_COMMON', 'ADC123_COMMON'],
|
|
'CAN': ['CAN_BASE', 'CAN1_BASE'],
|
|
'FMC': ['FMC_BASE', 'FMC_R_BASE'],
|
|
'FSMC': ['FSMC_R_BASE']
|
|
}
|
|
|
|
# Device address overrides, in case of missing from headers
|
|
address_overrides = {
|
|
'STM32F412VE:GPIOF_BASE': 0x40021400,
|
|
'STM32F412VE:GPIOG_BASE': 0x40021800,
|
|
'STM32F412VG:GPIOF_BASE': 0x40021400,
|
|
'STM32F412VG:GPIOG_BASE': 0x40021800,
|
|
'STM32L151CB-A:GPIOF_BASE': 0x40021800,
|
|
'STM32L151CB-A:GPIOG_BASE': 0x40021C00,
|
|
'STM32L432KB:GPIOD_BASE': 0x48000C00,
|
|
'STM32L432KB:GPIOE_BASE': 0x48001000,
|
|
'STM32L432KB:GPIOF_BASE': 0x48001400,
|
|
'STM32L432KB:GPIOG_BASE': 0x48001800,
|
|
}
|
|
|
|
|
|
def lookup_address(defines, name, d):
|
|
if addr := defines.get(d):
|
|
return addr
|
|
elif addr := address_overrides.get(name + ':' + d):
|
|
return addr
|
|
|
|
|
|
@functools.cache
|
|
def match_peri(peri):
|
|
for r, block in perimap:
|
|
if re.match('^' + r + '$', peri):
|
|
if block == '':
|
|
return None
|
|
return block
|
|
return None
|
|
|
|
|
|
all_mcu_files = {}
|
|
per_mcu_files = {}
|
|
|
|
|
|
def parse_documentations():
|
|
print("linking files and documents")
|
|
with open('sources/mcufinder/files.json', 'r', encoding='utf-8') as j:
|
|
files = json.load(j)
|
|
for file in files['Files']:
|
|
file_id = file['id_file']
|
|
if file_id not in all_mcu_files:
|
|
all_mcu_files[file_id] = {
|
|
'name': file['name'],
|
|
'title': file['title'],
|
|
'url': file['URL'],
|
|
'type': file['type'],
|
|
}
|
|
|
|
with open('sources/mcufinder/mcus.json', 'r', encoding='utf-8') as j:
|
|
mcus = json.load(j)
|
|
for mcu in mcus['MCUs']:
|
|
rpn = mcu['RPN']
|
|
if rpn not in per_mcu_files:
|
|
per_mcu_files[rpn] = []
|
|
for file in mcu['files']:
|
|
per_mcu_files[rpn].append(file['file_id'])
|
|
|
|
|
|
def parse_document_type(t):
|
|
if t == 'Reference manual':
|
|
return 0, 'reference_manual'
|
|
if t == 'Programming manual':
|
|
return 1, 'programming_manual'
|
|
if t == 'Datasheet':
|
|
return 2, 'datahseet'
|
|
if t == 'Errata sheet':
|
|
return 3, 'errata_sheet'
|
|
if t == 'Application note':
|
|
return 4, 'application_note'
|
|
raise Exception(f'Unknown doc type {t}')
|
|
|
|
|
|
def documents_for(chip_name):
|
|
docs = []
|
|
if ids := per_mcu_files.get(chip_name):
|
|
for id in ids:
|
|
if file := all_mcu_files.get(id):
|
|
file = all_mcu_files[id]
|
|
order, doc_type = parse_document_type(file['type'])
|
|
docs.append({
|
|
'order': order,
|
|
'type': doc_type,
|
|
'title': file['title'],
|
|
'name': file['name'],
|
|
'url': file['url'],
|
|
})
|
|
docs.sort(key=lambda x: (x['order'], x['name']))
|
|
for doc in docs:
|
|
del doc['order']
|
|
return docs
|
|
|
|
|
|
def chip_name_from_package_name(x):
|
|
name_map = [
|
|
('(STM32L1....).x([AX])', '\\1-\\2'),
|
|
('(STM32G0....).xN', '\\1'),
|
|
('(STM32F412..).xP', '\\1'),
|
|
('(STM32L4....).xP', '\\1'),
|
|
('(STM32WB....).x[AE]', '\\1'),
|
|
('(STM32G0....).xN', '\\1'),
|
|
('(STM32L5....).x[PQ]', '\\1'),
|
|
('(STM32L0....).xS', '\\1'),
|
|
('(STM32H7....).xQ', '\\1'),
|
|
('(STM32U5....).xQ', '\\1'),
|
|
('(STM32......).x', '\\1'),
|
|
]
|
|
|
|
for a, b in name_map:
|
|
r, n = re.subn('^' + a + '$', b, x)
|
|
if n != 0:
|
|
return r
|
|
raise Exception("bad name: {}".format(x))
|
|
|
|
|
|
memories_map = {
|
|
'flash': [
|
|
'FLASH', 'FLASH_BANK1', 'FLASH_BANK2',
|
|
'D1_AXIFLASH', 'D1_AXIICP',
|
|
],
|
|
'ram': [
|
|
'SRAM', 'SRAM1', 'SRAM2',
|
|
'D1_AXISRAM',
|
|
'D1_ITCMRAM',
|
|
'D1_DTCMRAM',
|
|
'D1_AHBSRAM',
|
|
'D2_AXISRAM',
|
|
'D3_BKPSRAM',
|
|
'D3_SRAM'
|
|
],
|
|
}
|
|
|
|
|
|
def cleanup_pin_name(pin_name):
|
|
if p := parse_pin_name(pin_name):
|
|
return f'P{p[0]}{p[1]}'
|
|
|
|
|
|
def parse_signal_name(signal_name):
|
|
if signal_name.startswith('USB_OTG_FS') or signal_name.startswith('USB_OTG_HS'):
|
|
parts = [signal_name[:10], signal_name[11:]]
|
|
else:
|
|
parts = signal_name.split('_', 1)
|
|
|
|
if len(parts) == 1:
|
|
return None
|
|
peri_name = parts[0]
|
|
signal_name = parts[1]
|
|
if signal_name.startswith("EXTI"):
|
|
return None
|
|
if peri_name.startswith("DEBUG") and signal_name.startswith("SUBGHZSPI"):
|
|
parts = signal_name.split('-', 1)
|
|
if len(parts) == 2:
|
|
peri_name = parts[0]
|
|
signal_name = removesuffix(parts[1], "OUT")
|
|
|
|
return peri_name, signal_name
|
|
|
|
|
|
def parse_pin_name(pin_name):
|
|
if len(pin_name) < 3:
|
|
return None
|
|
if pin_name[0] != 'P':
|
|
return None
|
|
port = pin_name[1]
|
|
if not port.isalpha():
|
|
return None
|
|
|
|
pin = pin_name[2:]
|
|
i = 0
|
|
while i < len(pin) and pin[i].isnumeric():
|
|
i += 1
|
|
|
|
if i == 0:
|
|
return None
|
|
|
|
pin = int(pin[:i])
|
|
|
|
return port, pin
|
|
|
|
|
|
def get_peri_addr(defines, pname):
|
|
possible_defines = alt_peri_defines.get(pname) or [f'{pname}_BASE', pname]
|
|
for d in possible_defines:
|
|
if addr := defines.get(d):
|
|
return addr
|
|
return None
|
|
|
|
|
|
def parse_chips():
|
|
os.makedirs('data/chips', exist_ok=True)
|
|
|
|
# XMLs group together chips that are identical except flash/ram size.
|
|
# For example STM32L471Z(E-G)Jx.xml is STM32L471ZEJx, STM32L471ZGJx.
|
|
# However they do NOT group together identical chips with different package.
|
|
#
|
|
# We want exactly the opposite: group all packages of a chip together, but
|
|
# NOT group equal-except-memory-size chips together. Yay.
|
|
#
|
|
# We first read all XMLs, and fold together all packages. We don't expand
|
|
# flash/ram sizes yet, we want to do it as late as possible to avoid duplicate
|
|
# work so that generation is faster.
|
|
|
|
chips = {}
|
|
chip_groups = []
|
|
|
|
for f in sorted(glob('sources/cubedb/mcu/STM32*.xml')):
|
|
f = f.replace(os.path.sep, '/')
|
|
if 'STM32MP' in f:
|
|
continue
|
|
if 'STM32GBK' in f:
|
|
continue
|
|
|
|
print(f)
|
|
|
|
r = xmltodict.parse(open(f, 'rb'), force_list=['Signal'])['Mcu']
|
|
|
|
package_names = expand_name(r['@RefName'])
|
|
package_rams = r['Ram']
|
|
package_flashs = r['Flash']
|
|
if type(package_rams) != list:
|
|
package_rams = [package_rams] * len(package_names)
|
|
if type(package_flashs) != list:
|
|
package_flashs = [package_flashs] * len(package_names)
|
|
|
|
group_idx = None
|
|
for package_name in package_names:
|
|
chip_name = chip_name_from_package_name(package_name)
|
|
if chip := chips.get(chip_name):
|
|
group_idx = chip['group_idx']
|
|
break
|
|
|
|
if group_idx is None:
|
|
group_idx = len(chip_groups)
|
|
chip_groups.append({
|
|
'chip_names': [],
|
|
'xml': r,
|
|
'ips': {},
|
|
'pins': {},
|
|
})
|
|
|
|
for package_i, package_name in enumerate(package_names):
|
|
chip_name = chip_name_from_package_name(package_name)
|
|
if chip_name not in chips:
|
|
chips[chip_name] = {
|
|
'name': chip_name,
|
|
'flash': package_flashs[package_i],
|
|
'ram': package_rams[package_i],
|
|
'group_idx': group_idx,
|
|
'packages': [],
|
|
}
|
|
chips[chip_name]['packages'].append({
|
|
'name': package_name,
|
|
'package': r['@Package'],
|
|
})
|
|
|
|
# Some packages have some peripehrals removed because the package had to
|
|
# remove GPIOs useful for that peripheral. So we merge all peripherals from all packages.
|
|
group = chip_groups[group_idx]
|
|
for ip in r['IP']:
|
|
group['ips'][ip['@InstanceName']] = ip
|
|
for pin in r['Pin']:
|
|
if pin_name := cleanup_pin_name(pin['@Name']):
|
|
group['pins'][pin_name] = pin
|
|
|
|
for chip_name, chip in chips.items():
|
|
chip_groups[chip['group_idx']]['chip_names'].append(chip_name)
|
|
|
|
for chip in chip_groups:
|
|
chip_name = chip["chip_names"][0]
|
|
print(f'* processing chip group {chip["chip_names"]}')
|
|
|
|
chip['family'] = chip['xml']['@Family']
|
|
chip['line'] = chip['xml']['@Line']
|
|
chip['die'] = chip['xml']['Die']
|
|
|
|
rcc_kind = next(filter(lambda x: x['@Name'] == 'RCC', chip['ips'].values()))['@Version']
|
|
assert rcc_kind is not None
|
|
rcc_block = match_peri(f'{chip_name}:RCC:{rcc_kind}')
|
|
assert rcc_block is not None
|
|
|
|
h = header.get_for_chip(chip_name)
|
|
if h is None:
|
|
raise Exception("missing header for {}".format(chip_name))
|
|
|
|
chip_af = next(filter(lambda x: x['@Name'] == 'GPIO', chip['ips'].values()))['@Version']
|
|
chip_af = removesuffix(chip_af, '_gpio_v1_0')
|
|
chip_af = af[chip_af]
|
|
|
|
cores = []
|
|
for core_xml in children(chip['xml'], 'Core'):
|
|
core_name = corename(core_xml)
|
|
core = {
|
|
'name': core_name,
|
|
'peripherals': [],
|
|
}
|
|
cores.append(core)
|
|
|
|
if not core_name in h['interrupts'] or not core_name in h['defines']:
|
|
core_name = 'all'
|
|
# print("Defining for core", core_name)
|
|
|
|
# C header defines for this core.
|
|
defines = h['defines'][core_name]
|
|
|
|
# Interrupts!
|
|
# Most chips have a single NVIC, named "NVIC"
|
|
want_nvic_name = 'NVIC'
|
|
|
|
# Exception 1: Multicore: NVIC1 is the first core, NVIC2 is the second. We have to pick the right one.
|
|
if chip_name[5:9] in ('H745', 'H747', 'H755', 'H757', 'WL54', 'WL55'):
|
|
if core_name == 'cm7':
|
|
want_nvic_name = 'NVIC1'
|
|
else:
|
|
want_nvic_name = 'NVIC2'
|
|
if chip_name[5:8] == 'WL5':
|
|
if core_name == 'cm4':
|
|
want_nvic_name = 'NVIC1'
|
|
else:
|
|
want_nvic_name = 'NVIC2'
|
|
|
|
# Exception 2: TrustZone: NVIC1 is Secure mode, NVIC2 is NonSecure mode. For now, we pick the NonSecure one.
|
|
if chip_name[5:7] in ('L5', 'U5'):
|
|
want_nvic_name = 'NVIC2'
|
|
|
|
chip_nvic = next(filter(lambda x: x['@Name'] == want_nvic_name, chip['ips'].values()))
|
|
header_irqs = h['interrupts'][core_name]
|
|
chip_irqs = interrupts.get(chip_nvic['@Name'], chip_nvic['@Version'], core_name)
|
|
|
|
# F100xE MISC_REMAP remaps some DMA IRQs, so ST decided to give two names
|
|
# to the same IRQ number.
|
|
if chip_name.startswith('STM32F100') and 'DMA2_Channel4_5' in header_irqs:
|
|
del header_irqs['DMA2_Channel4_5']
|
|
|
|
core['interrupts'] = [
|
|
{
|
|
'name': k,
|
|
'number': v,
|
|
}
|
|
for k, v in header_irqs.items()
|
|
]
|
|
|
|
peri_kinds = {}
|
|
|
|
for ip in chip['ips'].values():
|
|
pname = ip['@InstanceName']
|
|
pkind = ip['@Name'] + ':' + ip['@Version']
|
|
pkind = removesuffix(pkind, '_Cube')
|
|
|
|
if pname in FAKE_PERIPHERALS:
|
|
continue
|
|
|
|
if rename := peri_rename.get(pname):
|
|
pname = rename
|
|
|
|
if pname.startswith('ADC'):
|
|
if not 'ADC_COMMON' in peri_kinds:
|
|
peri_kinds['ADC_COMMON'] = 'ADC_COMMON:' + removesuffix(ip['@Version'], '_Cube')
|
|
if pname.startswith('ADC3'):
|
|
if chip_name.startswith("STM32H7") and not 'ADC_COMMON3' in peri_kinds:
|
|
peri_kinds['ADC3_COMMON'] = 'ADC3_COMMON:' + removesuffix(ip['@Version'], '_Cube')
|
|
|
|
peri_kinds[pname] = pkind
|
|
|
|
for pname in ghost_peris:
|
|
if pname not in peri_kinds and (addr := get_peri_addr(defines, pname)):
|
|
peri_kinds[pname] = 'unknown'
|
|
|
|
if 'BDMA1' in peri_kinds and 'BDMA' in peri_kinds:
|
|
del peri_kinds['BDMA']
|
|
|
|
# get possible used GPIOs for each peripheral from the chip xml
|
|
# it's not the full info we would want (stuff like AFIO info which comes from GPIO xml),
|
|
# but we actually need to use it because of F1 line
|
|
# which doesn't include non-remappable peripherals in GPIO xml
|
|
# and some weird edge cases like STM32F030C6 (see merge_periph_pins_info)
|
|
periph_pins = {}
|
|
for pin_name, pin in chip['pins'].items():
|
|
for signal in pin['Signal']:
|
|
signal = signal['@Name']
|
|
if signal.startswith('DEBUG_SUBGHZSPI-'):
|
|
signal = 'SUBGHZSPI_' + signal[16:-3]
|
|
# TODO: What are those signals (well, GPIO is clear) Which peripheral do they belong to?
|
|
if signal not in {'GPIO', 'CEC', 'AUDIOCLK', 'VDDTCXO'} and 'EXTI' not in signal:
|
|
# both peripherals and signals can have underscores in their names so there is no easy way to split
|
|
# check if signal name starts with one of the peripheral names
|
|
for periph in peri_kinds.keys():
|
|
if signal.startswith(periph + '_'):
|
|
signal = removeprefix(signal, periph + '_')
|
|
pins = periph_pins.setdefault(periph, [])
|
|
pins.append({
|
|
'pin': pin_name,
|
|
'signal': signal,
|
|
})
|
|
break
|
|
for periph, pins in periph_pins.items():
|
|
pins = remove_duplicates(pins)
|
|
sort_pins(pins)
|
|
periph_pins[periph] = pins
|
|
|
|
peris = []
|
|
for pname, pkind in peri_kinds.items():
|
|
addr = get_peri_addr(defines, pname)
|
|
if addr is None:
|
|
continue
|
|
|
|
p = {
|
|
'name': pname,
|
|
'address': addr,
|
|
}
|
|
|
|
if block := match_peri(chip_name + ':' + pname + ':' + pkind):
|
|
p['registers'] = {
|
|
'kind': block[0],
|
|
'version': block[1],
|
|
'block': block[2],
|
|
}
|
|
|
|
if rcc_info := match_peri_clock(rcc_block, pname):
|
|
p['rcc'] = rcc_info
|
|
|
|
if pins := periph_pins.get(pname):
|
|
# merge the core xml info with GPIO xml info to hopefully get the full picture
|
|
# if the peripheral does not exist in the GPIO xml (one of the notable one is ADC)
|
|
# it probably doesn't need any AFIO writes to work
|
|
if af_pins := chip_af.get(pname):
|
|
pins = merge_periph_pins_info('STM32F1' in chip_name, pname, pins, af_pins)
|
|
p['pins'] = pins
|
|
|
|
if pname in chip_irqs:
|
|
# filter by available, because some are conditioned on <Die>
|
|
irqs = interrupts.filter_interrupts(chip_irqs[pname], header_irqs)
|
|
irqs = sorted(irqs, key=lambda x: (x['signal'], x['interrupt']))
|
|
p['interrupts'] = irqs
|
|
|
|
peris.append(p)
|
|
|
|
family_extra = "data/extra/family/" + chip['family'] + ".yaml"
|
|
if os.path.exists(family_extra):
|
|
with open(family_extra) as extra_f:
|
|
extra = yaml.load(extra_f)
|
|
for p in extra['peripherals']:
|
|
peris.append(p)
|
|
|
|
peris.sort(key=lambda x: x['name'])
|
|
have_peris = set((p['name'] for p in peris))
|
|
core['peripherals'] = peris
|
|
|
|
# Collect DMA versions in the chip
|
|
chip_dmas = []
|
|
for ip in chip['ips'].values():
|
|
pkind = ip['@Name']
|
|
version = ip['@Version']
|
|
if pkind in ('DMA', 'BDMA', 'BDMA1', 'BDMA2', 'GPDMA') and version in dma_channels and version not in chip_dmas:
|
|
chip_dmas.append(version)
|
|
|
|
# Process DMA channels
|
|
chs = []
|
|
for dma in chip_dmas:
|
|
chs.extend(dma_channels[dma]['channels'])
|
|
|
|
# The dma_channels[xx] is generic for multiple chips. The current chip may have less DMAs,
|
|
# so we have to filter it.
|
|
chs = [ch for ch in chs if ch['dma'] in have_peris]
|
|
core['dma_channels'] = chs
|
|
|
|
have_chs = set((ch['name'] for ch in chs))
|
|
|
|
# Process peripheral - DMA channel associations
|
|
for p in peris:
|
|
chs = []
|
|
for dma in chip_dmas:
|
|
if peri_chs := dma_channels[dma]['peripherals'].get(p['name']):
|
|
chs.extend([
|
|
ch
|
|
for ch in peri_chs
|
|
if 'channel' not in ch or ch['channel'] in have_chs
|
|
])
|
|
if chs:
|
|
p['dma_channels'] = chs
|
|
|
|
# Now that we've processed everything common to the entire group,
|
|
# process each chip in the group.
|
|
|
|
group = chip
|
|
|
|
for chip_name in group['chip_names']:
|
|
chip = chips[chip_name]
|
|
|
|
flash_total = int(chip['flash']) * 1024
|
|
ram_total = int(chip['ram']) * 1024
|
|
|
|
memory_regions = []
|
|
|
|
found = set()
|
|
for each in memories_map['flash']:
|
|
if each + '_BASE' in h['defines']['all']:
|
|
if each == 'FLASH':
|
|
key = 'BANK_1'
|
|
elif each == 'FLASH_BANK1':
|
|
key = 'BANK_1'
|
|
elif each == 'FLASH_BANK2':
|
|
key = 'BANK_2'
|
|
else:
|
|
key = each
|
|
|
|
if key in found:
|
|
continue
|
|
found.add(key)
|
|
|
|
size = 0
|
|
if key == 'BANK_1' or key == 'BANK_2':
|
|
if size2 := memory.determine_flash_size(chip_name):
|
|
size = min(size2, flash_total)
|
|
|
|
memory_regions.append({
|
|
'name': key,
|
|
'kind': 'flash',
|
|
'address': h['defines']['all'][each + '_BASE'],
|
|
'size': size,
|
|
'settings': memory.determine_flash_settings(chip_name),
|
|
})
|
|
|
|
found = set()
|
|
for each in memories_map['ram']:
|
|
if each + '_BASE' in h['defines']['all']:
|
|
if each == 'D1_AXISRAM':
|
|
key = 'SRAM'
|
|
elif each == 'SRAM1':
|
|
key = 'SRAM'
|
|
else:
|
|
key = each
|
|
|
|
if key in found:
|
|
continue
|
|
found.add(key)
|
|
|
|
size = 0
|
|
if key == 'SRAM':
|
|
if size2 := memory.determine_ram_size(chip_name):
|
|
size = min(size2, ram_total)
|
|
|
|
memory_regions.append({
|
|
'name': key,
|
|
'kind': 'ram',
|
|
'address': h['defines']['all'][each + '_BASE'],
|
|
'size': size,
|
|
})
|
|
|
|
docs = documents_for(chip_name)
|
|
|
|
device_id = memory.determine_device_id(chip_name)
|
|
|
|
chip = {
|
|
'name': chip_name,
|
|
'family': group['family'],
|
|
'line': group['line'],
|
|
'die': group['die'],
|
|
'device_id': device_id,
|
|
'packages': chip['packages'],
|
|
'memory': memory_regions,
|
|
'docs': docs,
|
|
'cores': cores,
|
|
}
|
|
|
|
with open('data/chips/' + chip_name + '.json', 'w') as f:
|
|
json.dump(chip, f, indent=4)
|
|
|
|
|
|
SIGNAL_REMAP = {
|
|
# for some godforsaken reason UART4's and UART5's CTS are called CTS_NSS in the GPIO xml
|
|
# so try to match with these
|
|
'CTS': 'CTS_NSS'
|
|
|
|
|
|
}
|
|
|
|
|
|
def merge_periph_pins_info(is_f1, periph_name, core_pins, af_pins):
|
|
if is_f1:
|
|
# TODO: actually handle the F1 AFIO information when it will be extracted
|
|
return core_pins
|
|
|
|
# covert to dict
|
|
af_pins = {(v['pin'], v['signal']): v for v in af_pins}
|
|
for pin in core_pins:
|
|
af = af_pins.get((pin['pin'], pin['signal']), {'af': None})['af']
|
|
|
|
# try to look for a signal with another name
|
|
if af is None and (remap := SIGNAL_REMAP.get(pin['signal'])):
|
|
af = af_pins.get((pin['pin'], remap), {'af': None})['af']
|
|
|
|
# it appears that for __some__ STM32 MCUs there is no AFIO specified in GPIO file
|
|
# (notably - STM32F030C6 with it's I2C1 on PF6 and PF7)
|
|
# but the peripheral can actually be mapped to different pins
|
|
# this breaks embassy's model, so we pretend that it's AF 0
|
|
# Reference Manual states that there's no GPIOF_AFR register
|
|
# but according to Cube-generated core it's OK to write to AFIO reg, it seems to be ignored
|
|
# TODO: are there any more signals that have this "feature"
|
|
if af is None and periph_name == 'I2C1':
|
|
af = 0
|
|
|
|
if af is not None:
|
|
# Kinda not nice to modify this dict, but doesn't seem to be a problem
|
|
pin['af'] = af
|
|
return core_pins
|
|
pass
|
|
|
|
|
|
af = {}
|
|
|
|
|
|
def sort_pins(pins):
|
|
pins.sort(key=lambda p: (parse_pin_name(p['pin']), p['signal']))
|
|
|
|
|
|
def remove_duplicates(item_list):
|
|
''' Removes duplicate items from a list '''
|
|
singles_list = []
|
|
for element in item_list:
|
|
if element not in singles_list:
|
|
singles_list.append(element)
|
|
return singles_list
|
|
|
|
|
|
def parse_gpio_af():
|
|
# os.makedirs('data/gpio_af', exist_ok=True)
|
|
for f in glob('sources/cubedb/mcu/IP/GPIO-*_gpio_v1_0_Modes.xml'):
|
|
f = f.replace(os.path.sep, '/')
|
|
|
|
ff = removeprefix(f, 'sources/cubedb/mcu/IP/GPIO-')
|
|
ff = removesuffix(ff, '_gpio_v1_0_Modes.xml')
|
|
r = xmltodict.parse(open(f, 'rb'))
|
|
|
|
if 'STM32F1' in f:
|
|
peris = parse_gpio_af_f1(r)
|
|
else:
|
|
peris = parse_gpio_af_nonf1(r)
|
|
af[ff] = peris
|
|
|
|
|
|
def parse_gpio_af_f1(xml):
|
|
peris = {}
|
|
for pin in xml['IP']['GPIO_Pin']:
|
|
pin_name = pin['@Name']
|
|
|
|
# Cleanup pin name
|
|
pin_name = cleanup_pin_name(pin_name)
|
|
if pin_name is None:
|
|
continue
|
|
|
|
# Extract AFs
|
|
for signal in children(pin, 'PinSignal'):
|
|
p = parse_signal_name(signal['@Name'])
|
|
if p is None:
|
|
continue
|
|
peri_name, signal_name = p
|
|
|
|
if peri_name not in peris:
|
|
peris[peri_name] = []
|
|
peris[peri_name].append({
|
|
'pin': pin_name,
|
|
'signal': signal_name,
|
|
})
|
|
|
|
for pname, p in peris.items():
|
|
p = remove_duplicates(p)
|
|
sort_pins(p)
|
|
peris[pname] = p
|
|
return peris
|
|
|
|
|
|
def parse_gpio_af_nonf1(xml):
|
|
peris = {}
|
|
|
|
for pin in xml['IP']['GPIO_Pin']:
|
|
pin_name = pin['@Name']
|
|
|
|
# Cleanup pin name
|
|
pin_name = cleanup_pin_name(pin_name)
|
|
if pin_name is None:
|
|
continue
|
|
|
|
# Extract AFs
|
|
for signal in children(pin, 'PinSignal'):
|
|
p = parse_signal_name(signal['@Name'])
|
|
if p is None:
|
|
continue
|
|
peri_name, signal_name = p
|
|
|
|
afn = signal['SpecificParameter']['PossibleValue'].split('_')[1]
|
|
afn = int(removeprefix(afn, 'AF'))
|
|
|
|
if peri_name not in peris:
|
|
peris[peri_name] = []
|
|
peris[peri_name].append({
|
|
'pin': pin_name,
|
|
'signal': signal_name,
|
|
'af': afn,
|
|
})
|
|
|
|
for pname, p in peris.items():
|
|
p = remove_duplicates(p)
|
|
sort_pins(p)
|
|
peris[pname] = p
|
|
return peris
|
|
|
|
|
|
dma_channels = {}
|
|
|
|
|
|
def parse_dma():
|
|
for f in glob('sources/cubedb/mcu/IP/DMA*Modes.xml') + glob('sources/cubedb/mcu/IP/BDMA*Modes.xml'):
|
|
f = f.replace(os.path.sep, '/')
|
|
is_explicitly_bdma = False
|
|
ff = removeprefix(f, 'sources/cubedb/mcu/IP/')
|
|
if not (ff.startswith('B') or ff.startswith('D')):
|
|
continue
|
|
if ff.startswith("BDMA"):
|
|
is_explicitly_bdma = True
|
|
ff = removeprefix(ff, 'DMA-')
|
|
ff = removeprefix(ff, 'BDMA-')
|
|
ff = removeprefix(ff, 'BDMA1-')
|
|
ff = removeprefix(ff, 'BDMA2-')
|
|
ff = removesuffix(ff, '_Modes.xml')
|
|
print(ff)
|
|
|
|
r = xmltodict.parse(open(f, 'rb'), force_list={'Mode', 'RefMode'})
|
|
|
|
chip_dma = {
|
|
'channels': [],
|
|
'peripherals': {},
|
|
}
|
|
|
|
for dma in r['IP']['ModeLogicOperator']['Mode']:
|
|
dma_peri_name = dma['@Name']
|
|
if ' Context' in dma_peri_name:
|
|
continue
|
|
channels = dma['ModeLogicOperator']['Mode']
|
|
if len(channels) == 1:
|
|
# ========== CHIP WITH DMAMUX
|
|
|
|
dmamux_file = ff[5:7]
|
|
if ff.startswith('STM32L4P'):
|
|
dmamux_file = 'L4PQ'
|
|
if ff.startswith('STM32L4S'):
|
|
dmamux_file = 'L4RS'
|
|
|
|
dmamux = 'DMAMUX1'
|
|
if is_explicitly_bdma:
|
|
dmamux = 'DMAMUX2'
|
|
|
|
for mf in sorted(glob('data/dmamux/{}_*.yaml'.format(dmamux_file))):
|
|
mf = mf.replace(os.path.sep, '/')
|
|
with open(mf, 'r') as yaml_file:
|
|
y = yaml.load(yaml_file)
|
|
mf = removesuffix(mf, '.yaml')
|
|
req_dmamux = mf[mf.index('_') + 1:] # DMAMUX1 or DMAMUX2
|
|
|
|
if req_dmamux == dmamux:
|
|
for (request_name, request_num) in y.items():
|
|
parts = request_name.split('_')
|
|
target_peri_name = parts[0]
|
|
if len(parts) < 2:
|
|
request = target_peri_name
|
|
else:
|
|
request = parts[1]
|
|
chip_dma['peripherals'].setdefault(target_peri_name, []).append({
|
|
'signal': request,
|
|
"dmamux": req_dmamux,
|
|
"request": request_num,
|
|
})
|
|
|
|
dmamux_channel = 0
|
|
for n in dma_peri_name.split(","):
|
|
n = n.strip()
|
|
if result := re.match('.*' + n + r'_(Channel|Stream)\[(\d+)-(\d+)\]', channels[0]['@Name']):
|
|
low = int(result.group(2))
|
|
high = int(result.group(3))
|
|
for i in range(low, high + 1):
|
|
chip_dma['channels'].append({
|
|
'name': n + '_CH' + str(i),
|
|
'dma': n,
|
|
# Make sure all channels numbers start at 0
|
|
'channel': i - low,
|
|
'dmamux': dmamux,
|
|
'dmamux_channel': dmamux_channel,
|
|
})
|
|
dmamux_channel += 1
|
|
|
|
else:
|
|
# ========== CHIP WITHOUT DMAMUX
|
|
|
|
# see if we can scrape out requests
|
|
requests = {}
|
|
|
|
request_blocks = filter(lambda x: x['@BaseMode'] == 'DMA_Request', r['IP']['RefMode'])
|
|
for block in request_blocks:
|
|
name = block['@Name']
|
|
# Depending on the chip, the naming is "Channel" or "Request"...
|
|
request_num = next(filter(lambda x: x['@Name'] in ('Channel', 'Request'), block['Parameter']), None)
|
|
if request_num is not None:
|
|
request_num = request_num['PossibleValue']
|
|
if request_num.startswith('BDMA1_REQUEST_'):
|
|
continue
|
|
request_num = removeprefix(request_num, "DMA_CHANNEL_")
|
|
request_num = removeprefix(request_num, "DMA_REQUEST_")
|
|
requests[name] = int(request_num)
|
|
|
|
channel_names = []
|
|
for channel in channels:
|
|
channel_name = channel['@Name']
|
|
channel_name = removeprefix(channel_name, dma_peri_name + '_')
|
|
channel_name = removeprefix(channel_name, "Channel")
|
|
channel_name = removeprefix(channel_name, "Stream")
|
|
|
|
channel_names.append(channel_name)
|
|
chip_dma['channels'].append({
|
|
'name': dma_peri_name + '_CH' + channel_name,
|
|
'dma': dma_peri_name,
|
|
'channel': int(channel_name),
|
|
})
|
|
for target in channel['ModeLogicOperator']['Mode']:
|
|
target_name = target['@Name']
|
|
original_target_name = target_name
|
|
parts = target_name.split(':')
|
|
target_name = parts[0]
|
|
parts = target_name.split('_')
|
|
target_peri_name = parts[0]
|
|
if len(parts) < 2:
|
|
target_requests = [target_peri_name]
|
|
else:
|
|
target_requests = target_name.split('_')[1].split('/')
|
|
if target_name != 'MEMTOMEM':
|
|
if target_peri_name == "LPUART":
|
|
target_peri_name = "LPUART1"
|
|
for request in target_requests:
|
|
if ':' in request:
|
|
request = request.split(':')[0]
|
|
entry = {
|
|
'signal': request,
|
|
'channel': dma_peri_name + '_CH' + channel_name,
|
|
}
|
|
if original_target_name in requests:
|
|
entry['request'] = requests[original_target_name]
|
|
chip_dma['peripherals'].setdefault(target_peri_name, []).append(entry)
|
|
|
|
# Make sure all channels numbers start at 0
|
|
if min(map(int, channel_names)) != 0:
|
|
for ch in chip_dma['channels']:
|
|
if ch['dma'] == dma_peri_name:
|
|
ch['channel'] -= 1
|
|
|
|
Path('tmp/dmas').mkdir(parents=True, exist_ok=True)
|
|
with open('tmp/dmas/' + ff + '.json', 'w') as f:
|
|
json.dump(chip_dma, f, indent=4)
|
|
|
|
dma_channels[ff] = chip_dma
|
|
|
|
# STM32U5
|
|
|
|
chip_dma = {
|
|
'channels': [],
|
|
'peripherals': {},
|
|
}
|
|
|
|
with open('data/dmamux/U5_GPDMA1.yaml', 'r') as yaml_file:
|
|
y = yaml.load(yaml_file)
|
|
|
|
for (request_name, request_num) in y.items():
|
|
parts = request_name.split('_')
|
|
target_peri_name = parts[0]
|
|
if len(parts) < 2:
|
|
request = target_peri_name
|
|
else:
|
|
request = parts[1]
|
|
chip_dma['peripherals'].setdefault(target_peri_name, []).append({
|
|
'signal': request,
|
|
"dma": 'GPDMA1',
|
|
"request": request_num,
|
|
})
|
|
|
|
for i in range(16):
|
|
chip_dma['channels'].append({
|
|
'name': 'GPDMA1_CH' + str(i),
|
|
'dma': 'GPDMA1',
|
|
'channel': i,
|
|
'supports_2d': i >= 12,
|
|
})
|
|
|
|
ff = 'STM32U5_dma3_Cube'
|
|
with open('tmp/dmas/' + ff + '.json', 'w') as f:
|
|
json.dump(chip_dma, f, indent=4)
|
|
dma_channels[ff] = chip_dma
|
|
|
|
|
|
peripheral_to_clock = {}
|
|
|
|
|
|
clock_renames = {
|
|
'AHB': 'AHB1',
|
|
'APB': 'APB1',
|
|
}
|
|
|
|
|
|
def parse_rcc_regs():
|
|
print("parsing RCC registers")
|
|
for f in glob('data/registers/rcc_*'):
|
|
f = f.replace(os.path.sep, '/')
|
|
ff = removeprefix(f, 'data/registers/rcc_')
|
|
ff = removesuffix(ff, '.yaml')
|
|
family_clocks = {}
|
|
with open(f, 'r') as yaml_file:
|
|
y = yaml.load(yaml_file)
|
|
|
|
for (key, body) in y.items():
|
|
# Some chip families have a separate bus for GPIO so it's not attached to the AHB/APB
|
|
# bus but an GPIO bus. Use the GPIO as the clock for these chips.
|
|
if m := re.match(r'^fieldset/((A[PH]B\d?)|GPIO)[LH]?ENR\d?$', key):
|
|
reg = removeprefix(key, 'fieldset/')
|
|
clock = m.group(1)
|
|
clock = clock_renames.get(clock, clock)
|
|
for field in body['fields']:
|
|
if field['name'].endswith('EN'):
|
|
peri = removesuffix(field['name'], 'EN')
|
|
|
|
# Timers are a bit special, they may have a x2 freq
|
|
peri_clock = f'{clock}_TIM' if re.match(r'^TIM\d+$', peri) else clock
|
|
res = {
|
|
'clock': peri_clock,
|
|
'enable': {
|
|
'register': reg,
|
|
'field': field['name'],
|
|
}
|
|
}
|
|
if rstr := y.get(key.replace('ENR', 'RSTR')):
|
|
if field := next(filter(lambda f: f['name'] == f'{peri}RST', rstr['fields']), None):
|
|
res['reset'] = {
|
|
'register': reg.replace('ENR', 'RSTR'),
|
|
'field': f'{peri}RST',
|
|
}
|
|
family_clocks[peri] = res
|
|
|
|
peripheral_to_clock[('rcc', ff, 'RCC')] = family_clocks
|
|
|
|
|
|
def match_peri_clock(rcc_block, peri_name):
|
|
if rcc_block in peripheral_to_clock:
|
|
if res := peripheral_to_clock[rcc_block].get(peri_name):
|
|
return res
|
|
if peri_name.endswith("1"):
|
|
return match_peri_clock(rcc_block, removesuffix(peri_name, "1"))
|
|
return None
|
|
|
|
|
|
memory.parse()
|
|
interrupts.parse()
|
|
parse_rcc_regs()
|
|
parse_documentations()
|
|
parse_dma()
|
|
parse_gpio_af()
|
|
parse_chips()
|