stm32-data/parse.py
2021-06-30 13:34:43 -04:00

973 lines
34 KiB
Python
Executable File

#!/usr/bin/env python3
import xmltodict
import yaml
try:
from yaml import CSafeLoader as SafeLoader
except ImportError:
from yaml import SafeLoader
import re
import json
import os
from collections import OrderedDict
from glob import glob
def removeprefix(value: str, prefix: str, /) -> str:
if value.startswith(prefix):
return value[len(prefix):]
else:
return value[:]
def corename(d):
if m := re.match('.*Cortex-M(\d+)(\+?)', d):
name = "cm" + str(m.group(1))
if m.group(2) == "+":
name += "p"
return name
def removesuffix(value: str, suffix: str, /) -> str:
if value.endswith(suffix):
return value[:-len(suffix)]
else:
return value[:]
def represent_ordereddict(dumper, data):
value = []
for item_key, item_value in data.items():
node_key = dumper.represent_data(item_key)
node_value = dumper.represent_data(item_value)
value.append((node_key, node_value))
return yaml.nodes.MappingNode(u'tag:yaml.org,2002:map', value)
yaml.add_representer(OrderedDict, represent_ordereddict)
def hexint_presenter(dumper, data):
if data > 0x10000:
return dumper.represent_int(hex(data))
else:
return dumper.represent_int(data)
yaml.add_representer(int, hexint_presenter)
def children(x, key):
r = x.get(key)
if r is None:
return []
if type(r) is list:
return r
return [r]
headers_parsed = {}
header_map = {}
with open('header_map.yaml', 'r') as f:
y = yaml.load(f, Loader=SafeLoader)
for header, chips in y.items():
for chip in chips.split(','):
header_map[chip.strip().lower()] = header.lower()
def find_header(model):
# for a, b in header_map:
# model = re.sub(a, b, model, flags=re.IGNORECASE)
model = model.lower()
# if it's in the map, just go
if r := header_map.get(model):
return r
# if not, find it by regex, taking `x` meaning `anything`
res = []
for h in headers_parsed.keys():
if re.match('^' + h.replace('x', '.') + '$', model):
res.append(h)
if len(res) == 0:
return None
assert len(res) == 1
return res[0]
def paren_ok(val):
n = 0
for c in val:
if c == '(':
n += 1
if c == ')':
n -= 1
if n < 0:
return False
return n == 0
# warning: horrible abomination ahead
def parse_value(val, defines):
val = val.strip()
if val == '':
return 0
if m := re.match('((0x[0-9a-fA-F]+|\\d+))(|u|ul|U|UL)$', val):
return int(m.group(1), 0)
if m := re.match('([0-9A-Za-z_]+)$', val):
return defines.get(m.group(1), 0)
if m := re.match('\\((.*)\\)$', val):
if paren_ok(m.group(1)):
return parse_value(m.group(1), defines)
if m := re.match('\\*?\\([0-9A-Za-z_]+ *\\*?\\)(.*)$', val):
return parse_value(m.group(1), defines)
# if m := re.match('\\*?\\(u?int(8|16|32|64)_t\\ *)(.*)$', val):
# return parse_value(m.group(1), defines)
if m := re.match('(.*)<<(.*)$', val):
return (parse_value(m.group(1), defines) << parse_value(m.group(2), defines)) & 0xFFFFFFFF
if m := re.match('(.*)>>(.*)$', val):
return parse_value(m.group(1), defines) >> parse_value(m.group(2), defines)
if m := re.match('(.*)\\|(.*)$', val):
return parse_value(m.group(1), defines) | parse_value(m.group(2), defines)
if m := re.match('(.*)&(.*)$', val):
return parse_value(m.group(1), defines) | parse_value(m.group(2), defines)
if m := re.match('~(.*)$', val):
return (~parse_value(m.group(1), defines)) & 0xFFFFFFFF
if m := re.match('(.*)\\+(.*)$', val):
return parse_value(m.group(1), defines) + parse_value(m.group(2), defines)
if m := re.match('(.*)-(.*)$', val):
return parse_value(m.group(1), defines) - parse_value(m.group(2), defines)
raise Exception("can't parse: " + val)
def parse_header(f):
irqs = {}
defines = {}
cores = []
cur_core = 'all'
accum = ''
for l in open(f, 'r', encoding='utf-8', errors='ignore'):
l = l.strip()
l = accum + l
if l.endswith('\\'):
accum = l[:-1]
continue
accum = ''
# Scoped by a single core
if m := re.match('.*if defined.*CORE_CM(\\d+)(PLUS)?.*', l):
cur_core = "cm" + str(m.group(1))
if m.group(2) != None:
cur_core += "p"
#print("Cur core is ", cur_core, "matched", l)
found = False
for core in cores:
if core == cur_core:
found = True
if not found:
cores.append(cur_core)
#print("Switching to core", cur_core, "for", f)
elif m := re.match('.*else.*', l):
cur_core = "all"
if m := re.match('.*else.*CORE_CM(\\d+)(PLUS)?.*', l):
cur_core = "cm" + str(m.group(1))
if m.group(2) != None:
cur_core += "p"
#print("Cur core is ", cur_core, "matched", l)
elif len(cores) > 1:
# Pick the second core assuming we've already parsed one
cur_core = cores[1]
found = False
for core in cores:
if core == cur_core:
found = True
if not found:
cores.append(cur_core)
#print("Switching to core", cur_core, "for", f)
elif m := re.match('.*endif.*', l):
#print("Switching to common core for", f)
cur_core = "all"
if cur_core not in irqs:
#print("Registering new core", cur_core)
irqs[cur_core] = {}
if cur_core not in defines:
defines[cur_core] = {}
if m := re.match('([a-zA-Z0-9_]+)_IRQn += (\\d+),? +/\\*!< (.*) \\*/', l):
#print("Found irq for", cur_core)
irqs[cur_core][m.group(1)] = int(m.group(2))
if m := re.match('#define +([0-9A-Za-z_]+)\\(', l):
defines[cur_core][m.group(1)] = -1
if m := re.match('#define +([0-9A-Za-z_]+) +(.*)', l):
name = m.group(1)
val = m.group(2)
name = name.strip()
if name == 'FLASH_SIZE':
continue
val = val.split('/*')[0].strip()
val = parse_value(val, defines[cur_core])
#print("Found define for", cur_core)
defines[cur_core][name] = val
#print("Found", len(cores), "cores for", f)
#print("Found", len(irqs['all']), "shared interrupts for", f)
if len(cores) == 0:
cores.append("all")
for core in cores:
if core != "all":
irqs[core].update(irqs['all'])
defines[core].update(defines['all'])
return {
'cores': cores,
'interrupts': irqs,
'defines': defines,
}
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',
# I2S is just SPI on disguise
'I2S1',
'I2S2',
'I2S3',
'I2S4',
'I2S5',
'I2S6',
'I2S7',
'I2S8',
# These are software libraries
'FREERTOS',
'PDM2PCM',
'FATFS',
'CRC',
'LIBJPEG',
'MBEDTLS',
'LWIP',
'USB_HOST',
'USB_DEVICE',
'GUI_INTERFACE',
'TRACER_EMB',
]
perimap = [
('UART:sci2_v1_1', 'usart_v1/UART'),
('UART:sci2_v1_2', 'usart_v1/UART'),
('UART:sci2_v1_2_F1', 'usart_v1/UART'),
('UART:sci2_v2_1', 'usart_v2/UART'),
# ('UART:sci2_v3_0', 'usart_v3/UART'),
# ('UART:sci2_v3_1', 'usart_v3/UART'),
('.*: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_v3/USART'),
('.*:USART:sci3_v2_0', 'usart_v3/USART'),
('.*:USART:sci3_v2_1', 'usart_v3/USART'),
('.*: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:spi2s1_v2_2', 'spi_v1/SPI'),
('.*:SPI:spi2s1_v3_3', 'spi_v2/SPI'),
('.*:SPI:spi2s1_v3_1', 'spi_v2/SPI'),
('.*:SPI:spi2s2_v1_1', 'spi_v3/SPI'),
('.*:SPI:spi2s2_v1_0', 'spi_v3/SPI'),
('.*:I2C:i2c1_v1_5', 'i2c_v1/I2C'),
('.*:I2C:i2c2_v1_1', 'i2c_v2/I2C'),
('.*:I2C:i2c2_v1_1F7', 'i2c_v2/I2C'),
('.*:DAC:dacif_v2_0', 'dac_v2/DAC'),
('.*:DAC:dacif_v3_0', 'dac_v2/DAC'),
('.*:ADC:aditf5_v2_0', 'adc_v3/ADC'),
('.*:ADC_COMMON:aditf5_v2_0', 'adccommon_v3/ADC_COMMON'),
('.*:ADC_COMMON:aditf4_v3_0_WL', 'adccommon_v3/ADC_COMMON'),
('STM32F0.*:SYS:.*', 'syscfg_f0/SYSCFG'),
('STM32F4.*:SYS:.*', 'syscfg_f4/SYSCFG'),
('STM32L4.*:SYS:.*', 'syscfg_l4/SYSCFG'),
('STM32L0.*:SYS:.*', 'syscfg_l0/SYSCFG'),
('STM32H7.*:SYS:.*', 'syscfg_h7/SYSCFG'),
('STM32WB55.*:SYS:.*', 'syscfg_wb55/SYSCFG'),
('STM32WL.*:SYS:.*', 'syscfg_wl5x/SYSCFG'),
('STM32L0.*:RCC:.*', 'rcc_l0/RCC'),
('STM32L4.*:RCC:.*', 'rcc_l4/RCC'),
('STM32F4.*:RCC:.*', 'rcc_f4/RCC'),
('STM32WL.*:RCC:.*', 'rcc_wl5x/RCC'),
('STM32F0.0.*:RCC:.*', 'rcc_f0x0/RCC'),
('STM32F0.*:RCC:.*', 'rcc_f0/RCC'),
('.*:STM32H7AB_rcc_v1_0', ''), # rcc_h7ab/RCC
('.*:STM32H7_rcc_v1_0', 'rcc_h7/RCC'),
('.*:STM32W_rcc_v1_0', 'rcc_wb55/RCC'),
('.*:STM32L0_crs_v1_0', 'crs_l0/CRS'),
('.*SDMMC:sdmmc2_v1_0', 'sdmmc_v2/SDMMC'),
('.*:STM32H7_pwr_v1_0', 'pwr_h7/PWR'),
('.*:STM32H7_flash_v1_0', 'flash_h7/FLASH'),
('.*:STM32F0_flash_v1_0', 'flash_f0/FLASH'),
('.*TIM\d.*:gptimer.*', 'timer_v1/TIM_GP16'),
('.*ETH:ethermac110_v3_0', 'eth_v2/ETH'),
('.*:STM32F0_dbgmcu_v1_0', 'dbgmcu_f0/DBGMCU'),
('.*:STM32F1_dbgmcu_v1_0', 'dbgmcu_f1/DBGMCU'),
('.*:STM32F2_dbgmcu_v1_0', 'dbgmcu_f2/DBGMCU'),
('.*:STM32F3_dbgmcu_v1_0', 'dbgmcu_f3/DBGMCU'),
('.*:STM32F4_dbgmcu_v1_0', 'dbgmcu_f4/DBGMCU'),
('.*:STM32F7_dbgmcu_v1_0', 'dbgmcu_f7/DBGMCU'),
('.*:STM32G0_dbgmcu_v1_0', 'dbgmcu_g0/DBGMCU'),
('.*:STM32G4_dbgmcu_v1_0', 'dbgmcu_g4/DBGMCU'),
('.*:STM32H7_dbgmcu_v1_0', 'dbgmcu_h7/DBGMCU'),
('.*:STM32L0_dbgmcu_v1_0', 'dbgmcu_l0/DBGMCU'),
('.*:STM32L4_dbgmcu_v1_0', 'dbgmcu_l4/DBGMCU'),
('.*:STM32WB_dbgmcu_v1_0', 'dbgmcu_wb/DBGMCU'),
('.*:STM32WL_dbgmcu_v1_0', 'dbgmcu_wl/DBGMCU'),
('.*:IPCC:v1_0', 'ipcc_v1/IPCC'),
('.*:DMAMUX:v1', 'dmamux_v1/DMAMUX'),
('.*:BDMA:DMA', 'bdma_v1/DMA'),
('STM32L4[PQRS].*:.*:DMA', 'bdma_v1/DMA'), # L4+
('STM32L[04].*:.*:DMA', 'bdma_v2/DMA'), # L0, L4 non-plus (since plus is handled above)
('STM32F[247].*:.*:DMA', 'dma_v2/DMA'),
('STM32H7.*:.*:DMA', 'dma_v1/DMA'),
('.*:DMA', 'bdma_v1/DMA'),
]
rng_clock_map = [
('STM32L0.*:RNG:.*', 'AHB'),
('STM32L4.*:RNG:.*', 'AHB2'),
('STM32F4.*:RNG:.*', 'AHB2'),
('STM32H7.*:RNG:.*', 'AHB2'),
('STM32WB55.*:RNG:.*', 'AHB3'),
('STM32WL5.*:RNG:.*', 'AHB3')
]
def match_peri(peri):
for r, block in perimap:
if re.match('^'+r+'$', peri):
if block == '':
return None
return block
return None
def find_af(gpio_af, peri_name, pin_name, signal_name):
if gpio_af in af:
if pin_name in af[gpio_af]:
if peri_name + '_' + signal_name in af[gpio_af][pin_name]:
return af[gpio_af][pin_name][peri_name + '_' + signal_name]
return None
def match_rng_clock(rcc):
for r, clock in rng_clock_map:
if re.match(r, rcc):
return clock
return None
all_mcu_files = {}
per_mcu_files = {}
def parse_documentations():
print("linking files and documents")
with open('sources/mcufinder/files.json', 'r') 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] = OrderedDict( {
'name': file['name'],
'title': file['title'],
'url': file['URL'],
'type': file['type'],
} )
with open('sources/mcufinder/mcus.json', 'r') 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 documents_for(chip_name, type):
docs = []
for id in per_mcu_files[chip_name]:
if id in all_mcu_files:
file = all_mcu_files[id]
if file['type'] == type:
docs.append( OrderedDict({
'title': file['title'],
'name': file['name'],
'url': file['url'],
}))
return docs
def parse_headers():
os.makedirs('sources/headers_parsed', exist_ok=True)
print('loading headers...')
for f in glob('sources/headers/*.h'):
# if 'stm32f4' not in f: continue
ff = removeprefix(f, 'sources/headers/')
ff = removesuffix(ff, '.h')
try:
with open('sources/headers_parsed/{}.json'.format(ff), 'r') as j:
res = json.load(j)
except:
print(f)
res = parse_header(f)
with open('sources/headers_parsed/{}.json'.format(ff), 'w') as j:
json.dump(res, j)
headers_parsed[ff] = res
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'),
('(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))
def parse_chips():
os.makedirs('data/chips', exist_ok=True)
chips = {}
for f in sorted(glob('sources/cubedb/mcu/STM32*.xml')):
if 'STM32MP' in f:
continue
print(f)
r = xmltodict.parse(open(f, 'rb'))['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)
for package_i, package_name in enumerate(package_names):
chip_name = chip_name_from_package_name(package_name)
flash = int(package_flashs[package_i])
ram = int(package_rams[package_i])
gpio_af = next(filter(lambda x: x['@Name'] == 'GPIO', r['IP']))['@Version']
gpio_af = removesuffix(gpio_af, '_gpio_v1_0')
dma = next(filter(lambda x: x['@Name'] == 'DMA', r['IP']))['@Version']
rcc = next(filter(lambda x: x['@Name'] == 'RCC', r['IP']))['@Version']
rcc = removesuffix(rcc, '-rcc_v1_0')
rcc = removesuffix(rcc, '_rcc_v1_0')
core = r['Core']
family = r['@Family']
cores = []
if isinstance(core, list):
for core in core:
cores.append(OrderedDict(
{
'name': corename(core),
'peripherals': {},
}))
else:
cores.append(OrderedDict(
{
'name': corename(core),
'peripherals': {},
}))
if chip_name not in chips:
chips[chip_name] = OrderedDict({
'name': chip_name,
'family': family,
'line': r['@Line'],
'packages': [],
'datasheet': None,
'reference-manual': None,
'flash': flash,
'ram': ram,
'cores': cores,
'peripherals': {},
'pins': {},
'application-notes': [],
'rcc': rcc, # temporarily stashing it here
'dma': dma, # temporarily stashing it here
})
chips[chip_name]['packages'].append(OrderedDict({
'name': package_name,
'package': r['@Package'],
}))
if chip_name in per_mcu_files:
if len(ds := documents_for(chip_name, 'Datasheet')) >= 1:
chips[chip_name]['datasheet'] = ds[0]
if len(rm := documents_for(chip_name, 'Reference manual')) >= 1:
chips[chip_name]['reference-manual'] = rm[0]
chips[chip_name]['application-notes'] = documents_for(chip_name, 'Application note')
# 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.
peris = chips[chip_name]['peripherals']
pins = chips[chip_name]['pins']
for ip in r['IP']:
pname = ip['@InstanceName']
pkind = ip['@Name']+':'+ip['@Version']
pkind = removesuffix(pkind, '_Cube')
if pname == 'SYS':
pname = 'SYSCFG'
if pname in FAKE_PERIPHERALS:
continue
if pname.startswith('ADC'):
if not 'ADC_COMMON' in peris:
peris['ADC_COMMON'] = 'ADC_COMMON:'+removesuffix(ip['@Version'], '_Cube')
peris[pname] = pkind
pins[pname] = []
for pin in r['Pin']:
pin_name = pin['@Name']
pin_name = pin_name.split(' ', 1)[0]
pin_name = pin_name.split('-', 1)[0]
if 'Signal' in pin:
signals = []
if not type(pin['Signal']) is list:
signals.append(pin['Signal'])
else:
signals = pin['Signal']
for signal in signals:
signal_name = signal['@Name']
parts = signal_name.split('_', 1)
if len(parts) == 1:
continue
peri_name = parts[0]
signal_name = parts[1]
if signal_name.startswith("EXTI"):
continue
if not peri_name in pins:
pins[peri_name] = []
entry = OrderedDict({
'pin': pin_name,
'signal': signal_name,
})
af_num = find_af(gpio_af, peri_name, pin_name, signal_name)
if af_num is not None:
entry['af'] = af_num
pins[peri_name].append(entry)
for chip_name, chip in chips.items():
print(f'* processing chip {chip_name}')
rcc = chip['rcc']
del chip['rcc']
chip_dma = chip['dma']
del chip['dma']
h = find_header(chip_name)
if h is None:
raise Exception("missing header for {}".format(chip_name))
h = headers_parsed[h]
# print("Got", len(chip['cores']), "cores")
for core in chip['cores']:
core_name = core['name']
if not core_name in h['interrupts'] or not core_name in h['defines']:
core_name = 'all'
#print("Defining for core", core_name)
# Gather all interrupts and defines for this core
interrupts = h['interrupts'][core_name]
defines = h['defines'][core_name]
core['interrupts'] = interrupts
if chip_dma in dma_channels:
core['dma_channels'] = dma_channels[chip_dma]['channels']
# print("INterrupts for", core, ":", interrupts)
#print("Defines for", core, ":", defines)
peris = {}
for pname, pkind in chip['peripherals'].items():
addr = defines.get(pname)
if addr is None:
if pname == 'ADC_COMMON':
addr = defines.get('ADC_COMMON')
if addr is None:
addr = defines.get('ADC1_COMMON')
if addr is None:
addr = defines.get('ADC12_COMMON')
if addr is None:
addr = defines.get('ADC123_COMMON')
if addr is None:
continue
p = OrderedDict({
'address': addr,
'kind': pkind,
})
if pname in clocks[rcc]:
p['clock'] = clocks[rcc][pname]
elif chip['family'] == 'STM32H7' and pname == "SPI6":
p['clock'] = "APB4"
if block := match_peri(chip_name+':'+pname+':'+pkind):
p['block'] = block
if pname in chip['pins']:
if len(chip['pins'][pname]) > 0:
p['pins'] = chip['pins'][pname]
# RNG Clock definitions are not easily determined, so lookup in mapping
if block is not None and block.startswith("rng_"):
if (clock := match_rng_clock(chip_name+':'+pname+':'+pkind)) != None:
p['clock'] = clock
if pname in dma_channels[chip_dma]['peripherals']:
if 'channels' in dma_channels[chip_dma]['peripherals'][pname]:
p['dma_channels'] = dma_channels[chip_dma]['peripherals'][pname]['channels']
if 'requests' in dma_channels[chip_dma]['peripherals'][pname]:
p['dma_requests'] = dma_channels[chip_dma]['peripherals'][pname]['requests']
peris[pname] = 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, Loader=SafeLoader)
for (extra_name, extra_p) in extra['peripherals'].items():
peris[extra_name] = extra_p
# Handle GPIO specially.
for p in range(20):
port = 'GPIO' + chr(ord('A')+p)
if addr := defines.get(port + '_BASE'):
block = 'gpio_v2/GPIO'
if chip['family'] == 'STM32F1':
block = 'gpio_v1/GPIO'
p = OrderedDict({
'address': addr,
'block': block,
})
peris[port] = p
# Handle DMA specially.
for dma in ('DMA1', 'DMA2', 'BDMA'):
if addr := defines.get(dma + '_BASE'):
p = OrderedDict({
'address': addr,
})
if block := match_peri(chip_name+':'+dma+':DMA'):
p['block'] = block
peris[dma] = p
# DMAMUX is not in the cubedb XMLs
for dma in ('DMAMUX', 'DMAMUX1', "DMAMUX2"):
if addr := defines.get(dma+'_BASE'):
kind = 'DMAMUX:v1'
dbg_peri = OrderedDict({
'address': addr,
'kind': kind,
})
if block := match_peri(chip_name+':'+dma+':'+kind):
dbg_peri['block'] = block
peris[dma] = dbg_peri
# EXTI is not in the cubedb XMLs
if addr := defines.get('EXTI_BASE'):
if chip_name.startswith("STM32WB55"):
block = 'exti_wb55/EXTI'
else:
block = 'exti_v1/EXTI'
peris['EXTI'] = OrderedDict({
'address': addr,
'kind': 'EXTI',
'block': block,
})
# FLASH is not in the cubedb XMLs
if addr := defines.get('FLASH_R_BASE'):
kind = 'FLASH:' + chip_name[:7] + '_flash_v1_0'
flash_peri = OrderedDict({
'address': addr,
'kind': kind,
})
if block := match_peri(kind):
flash_peri['block'] = block
peris['FLASH'] = flash_peri
# DBGMCU is not in the cubedb XMLs
if addr := defines.get('DBGMCU_BASE') or defines.get('DBG_BASE'):
kind = 'DBGMCU:' + chip_name[:7] + '_dbgmcu_v1_0'
dbg_peri = OrderedDict({
'address': addr,
'kind': kind,
})
if block := match_peri(kind):
dbg_peri['block'] = block
peris['DBGMCU'] = dbg_peri
# CRS is not in the cubedb XMLs
if addr := defines.get('CRS_BASE'):
kind = 'CRS:' + chip_name[:7] + '_crs_v1_0'
crs_peri = OrderedDict({
'address': addr,
'kind': kind,
})
if block := match_peri(kind):
crs_peri['block'] = block
peris['CRS'] = crs_peri
core['peripherals'] = peris
# remove all pins from the root of the chip before emitting.
del chip['pins']
del chip['peripherals']
with open('data/chips/'+chip_name+'.yaml', 'w') as f:
f.write(yaml.dump(chip, width=500))
af = {}
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'):
if 'STM32F1' in f:
continue
ff = removeprefix(f, 'sources/cubedb/mcu/IP/GPIO-')
ff = removesuffix(ff, '_gpio_v1_0_Modes.xml')
pins = {}
r = xmltodict.parse(open(f, 'rb'))
for pin in r['IP']['GPIO_Pin']:
pin_name = pin['@Name']
# Blacklist non-pins
if pin_name == 'PDR_ON':
continue
# Cleanup pin name
pin_name = pin_name.split('/')[0]
pin_name = pin_name.split('-')[0]
pin_name = pin_name.split(' ')[0]
pin_name = pin_name.split('_')[0]
pin_name = pin_name.split('(')[0]
pin_name = removesuffix(pin_name, 'OSC32')
pin_name = removesuffix(pin_name, 'BOOT0')
# Extract AFs
afs = {}
for signal in children(pin, 'PinSignal'):
func = signal['@Name']
afn = signal['SpecificParameter']['PossibleValue'].split('_')[1]
afn = int(removeprefix(afn, 'AF'))
afs[func] = afn
pins[pin_name] = afs
# with open('data/gpio_af/'+ff+'.yaml', 'w') as f:
# f.write(yaml.dump(pins))
af[ff] = pins
dma_channels = {}
def parse_dma():
for f in glob('sources/cubedb/mcu/IP/DMA-*Modes.xml'):
ff = removeprefix(f, 'sources/cubedb/mcu/IP/DMA-')
ff = removesuffix(ff, '_Modes.xml')
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:
requests = next(filter(lambda x: x['@Name'] == 'Request', r['IP']['RefParameter']))
request_num = 0
for request in requests['PossibleValue']:
target_name = request['@Comment']
parts = target_name.split('_')
target_peri_name = parts[0]
if len(parts) < 2:
event = target_peri_name
else:
event = target_name.split('_')[1]
if target_name != 'MEMTOMEM':
if target_peri_name not in chip_dma['peripherals']:
chip_dma['peripherals'][target_peri_name] = {}
peri_dma = chip_dma['peripherals'][target_peri_name]
if 'requests' not in peri_dma:
peri_dma['requests'] = {}
if event not in peri_dma['requests']:
peri_dma['requests'][event] = request_num
#event_dma = peri_dma['requests'][event]
# event_dma.append( OrderedDict( {
# 'channel': 'DMAMUX',
# 'request': request_num
# } ))
request_num += 1
for n in dma_peri_name.split(","):
n = n.strip()
if result := re.match('.*' + n + '_Channel\[(\d+)-(\d+)\]', channels[0]['@Name']):
low = int(result.group(1))
high = int(result.group(2))
for i in range(low, high+1):
chip_dma['channels'][n+'_'+str(i)] = OrderedDict({
'dma': n,
'channel': i,
})
else:
# 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']
request_num = next(filter(lambda x: x['@Name'] == 'Channel', block['Parameter']), None)
if request_num is not None:
request_num = request_num['PossibleValue']
request_num = removeprefix(request_num, "DMA_CHANNEL_")
requests[name] = int(request_num)
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")
chip_dma['channels'][dma_peri_name + '_' + channel_name] = OrderedDict({
'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_events = [target_peri_name]
else:
target_events = target_name.split('_')[1].split('/')
if target_name != 'MEMTOMEM':
if target_peri_name not in chip_dma['peripherals']:
chip_dma['peripherals'][target_peri_name] = {}
peri_dma = chip_dma['peripherals'][target_peri_name]
for event in target_events:
if ':' in event:
event = event.split(':')[0]
if 'channels' not in peri_dma:
peri_dma['channels'] = {}
if event not in peri_dma['channels']:
peri_dma['channels'][event] = []
event_dma = peri_dma['channels'][event]
entry = OrderedDict({
'channel': dma_peri_name + '_' + channel_name,
} )
if original_target_name in requests:
entry['request'] = requests[original_target_name]
event_dma.append( entry )
dma_channels[ff] = chip_dma
clocks = {}
def parse_clocks():
for f in glob('sources/cubedb/mcu/IP/RCC-*rcc_v1_0_Modes.xml'):
ff = removeprefix(f, 'sources/cubedb/mcu/IP/RCC-')
ff = removesuffix(ff, '_rcc_v1_0_Modes.xml')
ff = removesuffix(ff, '-rcc_v1_0_Modes.xml')
chip_clocks = {}
r = xmltodict.parse(open(f, 'rb'))
for ref in r['IP']['RefParameter']:
name = ref['@Name']
if name.startswith("APB") and name.endswith("Freq_Value") and not name.endswith("TimFreq_Value") and '@IP' in ref:
name = removesuffix(name, "Freq_Value")
peripherals = ref['@IP']
peripherals = peripherals.split(",")
for p in peripherals:
chip_clocks[p] = name
clocks[ff] = chip_clocks
parse_documentations()
parse_dma()
parse_gpio_af()
parse_headers()
parse_clocks()
parse_chips()