mirror of
https://github.com/ArduPilot/ardupilot.git
synced 2026-10-06 19:00:27 +08:00
Machine resets clear GPIO input latches, so restore the hwdef defaults alongside the vector table in the generated reset macro. Save persistent flash through atomic replacement to keep the previous image intact on interrupted writes, and exercise reset, erase and save behavior in Renode.
2668 lines
103 KiB
Python
Executable File
2668 lines
103 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
'''
|
|
Generate a Renode board overlay from ArduPilot ChibiOS hwdef files.
|
|
|
|
AP_FLAKE8_CLEAN
|
|
|
|
The normal ChibiOS hwdef compiler is deliberately used here. Its expanded
|
|
configuration and resolved DMA definitions remain the single interpretation of
|
|
hwdef.dat; this generator only translates those results into Renode wiring.
|
|
'''
|
|
|
|
import argparse
|
|
import ast
|
|
import contextlib
|
|
import fnmatch
|
|
import io
|
|
import json
|
|
import re
|
|
import shlex
|
|
import sys
|
|
|
|
from pathlib import Path
|
|
|
|
from driver_catalog import ATTACHABLE_DEVICES
|
|
from driver_catalog import i2c_endpoints
|
|
|
|
FAMILIES = {
|
|
'STM32F103xB': {
|
|
'name': 'f103',
|
|
'base': 'stm32f103_base.repl',
|
|
'script': 'ardupilot_f103.resc',
|
|
'uid_address': 0x1FFFF7E8,
|
|
'spis': {'SPI1', 'SPI2'},
|
|
'i2cs': {'I2C1', 'I2C2'},
|
|
'uarts': {'USART1', 'USART2', 'USART3', 'UART4', 'UART5'},
|
|
'sd_buses': {},
|
|
'can_buses': {'CAN': 'can1'},
|
|
'timers': {1, 2, 3, 4},
|
|
'uart_irq': {
|
|
'USART1': 37, 'USART2': 38, 'USART3': 39,
|
|
'UART4': 52, 'UART5': 53,
|
|
},
|
|
'spi_irq': {'SPI1': 35, 'SPI2': 36},
|
|
'i2c_irq': {'I2C1': (31, 32), 'I2C2': (33, 34)},
|
|
},
|
|
'STM32G474xx': {
|
|
'name': 'g474',
|
|
'base': 'stm32g474_base.repl',
|
|
'script': 'ardupilot_g474.resc',
|
|
'uid_address': 0x1FFF7590,
|
|
'adcs': {'ADC1': 'adc1'},
|
|
'adc_existing': True,
|
|
'spis': {'SPI1', 'SPI2', 'SPI3', 'SPI4'},
|
|
'i2cs': {'I2C1', 'I2C2', 'I2C3', 'I2C4'},
|
|
'uarts': {
|
|
'USART1', 'USART2', 'USART3', 'UART4', 'UART5', 'LPUART1',
|
|
},
|
|
'sd_buses': {},
|
|
'can_buses': {
|
|
'CAN1': 'fdcan1', 'CAN2': 'fdcan2', 'CAN3': 'fdcan3',
|
|
},
|
|
'timers': {1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 17, 20},
|
|
'uart_irq': {
|
|
'USART1': 37, 'USART2': 38, 'USART3': 39,
|
|
'UART4': 52, 'UART5': 53, 'LPUART1': 91,
|
|
},
|
|
'spi_irq': {'SPI1': 35, 'SPI2': 36, 'SPI3': 51, 'SPI4': 84},
|
|
'i2c_irq': {
|
|
'I2C1': (31, 32), 'I2C2': (33, 34),
|
|
'I2C3': (92, 93), 'I2C4': (82, 83),
|
|
},
|
|
},
|
|
'STM32F405xx': {
|
|
'name': 'f405',
|
|
'base': 'stm32f405_base.repl',
|
|
'script': 'ardupilot_f405.resc',
|
|
'uid_address': 0x1FFF7A10,
|
|
'adcs': {'ADC1': 'adc1'},
|
|
'spis': {'SPI1', 'SPI2', 'SPI3'},
|
|
'i2cs': {'I2C1', 'I2C2', 'I2C3'},
|
|
'uarts': {'USART1', 'USART2', 'USART3', 'UART4', 'UART5', 'USART6'},
|
|
'sd_buses': {'SDIO': 'sdmmc'},
|
|
'sd_fifo': {'SDIO': 0x40012C80},
|
|
'can_buses': {'CAN1': 'can1', 'CAN2': 'can2'},
|
|
'timers': {1, 2, 3, 4, 5, 8},
|
|
'uart_irq': {
|
|
'USART1': 37, 'USART2': 38, 'USART3': 39,
|
|
'UART4': 52, 'UART5': 53, 'USART6': 71,
|
|
},
|
|
'spi_irq': {'SPI1': 35, 'SPI2': 36, 'SPI3': 51},
|
|
'i2c_irq': {'I2C1': (31, 32), 'I2C2': (33, 34), 'I2C3': (72, 73)},
|
|
},
|
|
'STM32F407xx': {
|
|
'name': 'f407',
|
|
'base': 'stm32f405_base.repl',
|
|
'script': 'ardupilot_f405.resc',
|
|
'uid_address': 0x1FFF7A10,
|
|
'adcs': {'ADC1': 'adc1'},
|
|
'spis': {'SPI1', 'SPI2', 'SPI3'},
|
|
'i2cs': {'I2C1', 'I2C2', 'I2C3'},
|
|
'uarts': {'USART1', 'USART2', 'USART3', 'UART4', 'UART5', 'USART6'},
|
|
'sd_buses': {'SDIO': 'sdmmc'},
|
|
'sd_fifo': {'SDIO': 0x40012C80},
|
|
'can_buses': {'CAN1': 'can1', 'CAN2': 'can2'},
|
|
'timers': {1, 2, 3, 4, 5, 8},
|
|
'uart_irq': {
|
|
'USART1': 37, 'USART2': 38, 'USART3': 39,
|
|
'UART4': 52, 'UART5': 53, 'USART6': 71,
|
|
},
|
|
'spi_irq': {'SPI1': 35, 'SPI2': 36, 'SPI3': 51},
|
|
'i2c_irq': {'I2C1': (31, 32), 'I2C2': (33, 34), 'I2C3': (72, 73)},
|
|
},
|
|
'STM32F427xx': {
|
|
'name': 'f427',
|
|
'base': 'stm32f427_base.repl',
|
|
'script': 'ardupilot_f405.resc',
|
|
'uid_address': 0x1FFF7A10,
|
|
'adcs': {'ADC1': 'adc1'},
|
|
'spis': {'SPI1', 'SPI2', 'SPI3', 'SPI4', 'SPI5', 'SPI6'},
|
|
'i2cs': {'I2C1', 'I2C2', 'I2C3'},
|
|
'uarts': {
|
|
'USART1', 'USART2', 'USART3', 'UART4', 'UART5',
|
|
'USART6', 'UART7', 'UART8',
|
|
},
|
|
'sd_buses': {'SDIO': 'sdmmc'},
|
|
'sd_fifo': {'SDIO': 0x40012C80},
|
|
'can_buses': {'CAN1': 'can1', 'CAN2': 'can2'},
|
|
'timers': {1, 2, 3, 4, 5, 8, 12},
|
|
'uart_irq': {
|
|
'USART1': 37, 'USART2': 38, 'USART3': 39,
|
|
'UART4': 52, 'UART5': 53, 'USART6': 71,
|
|
'UART7': 82, 'UART8': 83,
|
|
},
|
|
'spi_irq': {
|
|
'SPI1': 35, 'SPI2': 36, 'SPI3': 51,
|
|
'SPI4': 84, 'SPI5': 85, 'SPI6': 86,
|
|
},
|
|
'i2c_irq': {'I2C1': (31, 32), 'I2C2': (33, 34), 'I2C3': (72, 73)},
|
|
},
|
|
'STM32F767xx': {
|
|
'name': 'f767',
|
|
'base': 'stm32f767_base.repl',
|
|
'script': 'ardupilot_f767.resc',
|
|
'uid_address': 0x1FF0F420,
|
|
'spis': {'SPI1', 'SPI2', 'SPI3', 'SPI4', 'SPI5', 'SPI6'},
|
|
'i2cs': {'I2C1', 'I2C2', 'I2C3', 'I2C4'},
|
|
'uarts': {
|
|
'USART1', 'USART2', 'USART3', 'UART4', 'UART5',
|
|
'USART6', 'UART7', 'UART8',
|
|
},
|
|
'sd_buses': {
|
|
'SDMMC1': 'sdmmc', 'SDMMC2': 'sdmmc2', 'SDIO': 'sdmmc',
|
|
},
|
|
'sd_fifo': {
|
|
'SDMMC1': 0x40012C80, 'SDMMC2': 0x40011C80,
|
|
'SDIO': 0x40012C80,
|
|
},
|
|
'can_buses': {'CAN1': 'can1', 'CAN2': 'can2'},
|
|
'timers': {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14},
|
|
'uart_irq': {
|
|
'USART1': 37, 'USART2': 38, 'USART3': 39,
|
|
'UART4': 52, 'UART5': 53, 'USART6': 71,
|
|
'UART7': 82, 'UART8': 83,
|
|
},
|
|
'spi_irq': {
|
|
'SPI1': 35, 'SPI2': 36, 'SPI3': 51,
|
|
'SPI4': 84, 'SPI5': 85, 'SPI6': 86,
|
|
},
|
|
'i2c_irq': {
|
|
'I2C1': (31, 32), 'I2C2': (33, 34),
|
|
'I2C3': (72, 73), 'I2C4': (95, 96),
|
|
},
|
|
},
|
|
'STM32H743xx': {
|
|
'name': 'h743',
|
|
'base': 'stm32h743_base.repl',
|
|
'script': 'ardupilot_h743.resc',
|
|
'uid_address': 0x1FF1E800,
|
|
'adcs': {'ADC1': 'adcM1S2'},
|
|
'adc_existing': True,
|
|
'adc_sample_shift': 4,
|
|
'spis': {'SPI1', 'SPI2', 'SPI3', 'SPI4', 'SPI5', 'SPI6'},
|
|
'i2cs': {'I2C1', 'I2C2', 'I2C3', 'I2C4'},
|
|
'uarts': {
|
|
'USART1', 'USART2', 'USART3', 'UART4', 'UART5',
|
|
'USART6', 'UART7', 'UART8', 'LPUART1',
|
|
},
|
|
'sd_buses': {'SDMMC1': 'sdmmc', 'SDMMC2': 'sdmmc2'},
|
|
'can_buses': {'CAN1': 'fdcan1', 'CAN2': 'fdcan2'},
|
|
'timers': {1, 2, 3, 4, 5, 8, 12, 13, 14, 15},
|
|
'uart_irq': {
|
|
'USART1': 26, 'USART2': 27, 'USART3': 28, 'USART6': 29,
|
|
'UART4': 30, 'UART5': 31, 'UART7': 32, 'UART8': 33,
|
|
'LPUART1': 64,
|
|
},
|
|
'spi_irq': {
|
|
'SPI1': 35, 'SPI2': 36, 'SPI3': 51,
|
|
'SPI4': 84, 'SPI5': 85, 'SPI6': 86,
|
|
},
|
|
},
|
|
'STM32H757xx': {
|
|
'name': 'h757',
|
|
'base': 'stm32h743_base.repl',
|
|
'script': 'ardupilot_h743.resc',
|
|
'uid_address': 0x1FF1E800,
|
|
'adcs': {'ADC1': 'adcM1S2'},
|
|
'adc_existing': True,
|
|
'adc_sample_shift': 4,
|
|
'spis': {'SPI1', 'SPI2', 'SPI3', 'SPI4', 'SPI5', 'SPI6'},
|
|
'i2cs': {'I2C1', 'I2C2', 'I2C3', 'I2C4'},
|
|
'uarts': {
|
|
'USART1', 'USART2', 'USART3', 'UART4', 'UART5',
|
|
'USART6', 'UART7', 'UART8', 'LPUART1',
|
|
},
|
|
'sd_buses': {'SDMMC1': 'sdmmc', 'SDMMC2': 'sdmmc2'},
|
|
'can_buses': {'CAN1': 'fdcan1', 'CAN2': 'fdcan2'},
|
|
'timers': {1, 2, 3, 4, 5, 8, 12, 13, 14, 15},
|
|
'uart_irq': {
|
|
'USART1': 26, 'USART2': 27, 'USART3': 28, 'USART6': 29,
|
|
'UART4': 30, 'UART5': 31, 'UART7': 32, 'UART8': 33,
|
|
'LPUART1': 64,
|
|
},
|
|
'spi_irq': {
|
|
'SPI1': 35, 'SPI2': 36, 'SPI3': 51,
|
|
'SPI4': 84, 'SPI5': 85, 'SPI6': 86,
|
|
},
|
|
},
|
|
'STM32L431xx': {
|
|
'name': 'l4',
|
|
'base': 'stm32l4_base.repl',
|
|
'script': 'ardupilot_l4.resc',
|
|
'uid_address': 0x1FFF7590,
|
|
'adcs': {'ADC1': 'adc1'},
|
|
'adc_existing': True,
|
|
'spis': {'SPI1', 'SPI2', 'SPI3'},
|
|
'i2cs': {'I2C1', 'I2C2', 'I2C3', 'I2C4'},
|
|
'uarts': {'USART1', 'USART2', 'USART3', 'UART4', 'LPUART1'},
|
|
'sd_buses': {},
|
|
'can_buses': {'CAN1': 'can1'},
|
|
'timers': {1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 17},
|
|
'uart_irq': {
|
|
'USART1': 37, 'USART2': 38, 'USART3': 39,
|
|
'UART4': 52, 'LPUART1': 70,
|
|
},
|
|
'spi_irq': {'SPI1': 35, 'SPI2': 36, 'SPI3': 51},
|
|
'i2c_irq': {
|
|
'I2C1': (31, 32), 'I2C2': (33, 34),
|
|
'I2C3': (72, 73), 'I2C4': (83, 84),
|
|
},
|
|
},
|
|
'STM32F303xC': {
|
|
'name': 'f303',
|
|
'base': 'stm32f303_base.repl',
|
|
'script': 'ardupilot_f303.resc',
|
|
'uid_address': 0x1FFFF7AC,
|
|
'spis': {'SPI1', 'SPI2', 'SPI3'},
|
|
'i2cs': {'I2C1', 'I2C2', 'I2C3'},
|
|
'uarts': {'USART1', 'USART2', 'USART3', 'UART4', 'UART5'},
|
|
'sd_buses': {},
|
|
'can_buses': {'CAN': 'can1'},
|
|
'timers': {1, 2, 3, 4, 6, 7, 8, 15, 16, 17, 20},
|
|
'uart_irq': {
|
|
'USART1': 37, 'USART2': 38, 'USART3': 39,
|
|
'UART4': 52, 'UART5': 53,
|
|
},
|
|
'spi_irq': {'SPI1': 35, 'SPI2': 36, 'SPI3': 51},
|
|
'i2c_irq': {
|
|
'I2C1': (31, 32), 'I2C2': (33, 34),
|
|
'I2C3': (72, 73),
|
|
},
|
|
},
|
|
}
|
|
|
|
# The F732 peripherals used by AP_Periph are register-compatible with the
|
|
# existing F767 platform model.
|
|
FAMILIES['STM32F732xx'] = FAMILIES['STM32F767xx']
|
|
# F412 AP_Periph boards use the subset of F4 peripherals already modelled by
|
|
# the F405 platform.
|
|
FAMILIES['STM32F412Rx'] = dict(
|
|
FAMILIES['STM32F405xx'], base='stm32f412_base.repl')
|
|
# G441 and G491 use the G4 peripheral instances exercised by the G474 model.
|
|
FAMILIES['STM32G441xx'] = FAMILIES['STM32G474xx']
|
|
FAMILIES['STM32G491xx'] = FAMILIES['STM32G474xx']
|
|
# F105 connectivity-line parts retain the F1 register layout while adding
|
|
# memory and peripherals.
|
|
FAMILIES['STM32F105xC'] = dict(
|
|
FAMILIES['STM32F103xB'], name='f105', base='stm32f105_base.repl',
|
|
spis={'SPI1', 'SPI2', 'SPI3'},
|
|
spi_irq={'SPI1': 35, 'SPI2': 36, 'SPI3': 51})
|
|
# The CKS F407 and H723 targets use the peripheral subsets modelled by their
|
|
# corresponding STM32 family platforms.
|
|
FAMILIES['CKS32F407xx'] = FAMILIES['STM32F407xx']
|
|
FAMILIES['STM32H723xx'] = FAMILIES['STM32H743xx']
|
|
# The AP_Periph L4 targets use the same peripheral instances, with the L496
|
|
# additionally exposing UART5.
|
|
FAMILIES['STM32L476xx'] = FAMILIES['STM32L431xx']
|
|
FAMILIES['STM32L496xx'] = dict(
|
|
FAMILIES['STM32L431xx'],
|
|
uarts=FAMILIES['STM32L431xx']['uarts'] | {'UART5'},
|
|
uart_irq=dict(FAMILIES['STM32L431xx']['uart_irq'], UART5=53))
|
|
# L4R5 flight controllers add the SDMMCv2 peripheral to the L4 peripheral
|
|
# subset used by the existing model.
|
|
FAMILIES['STM32L4R5xx'] = dict(
|
|
FAMILIES['STM32L496xx'], sd_buses={'SDMMC1': 'sdmmc'}, dmamux=True)
|
|
|
|
IMU_MODELS = {
|
|
'ADIS1647x': 'Sensors.AP_ADIS1647x',
|
|
'ADIS16607': 'Sensors.AP_ADIS16607',
|
|
'BMI055': 'Sensors.AP_RegisterIMU',
|
|
'BMI088': 'Sensors.AP_RegisterIMU',
|
|
'BMI160': 'Sensors.AP_RegisterIMU',
|
|
'BMI270': 'Sensors.AP_RegisterIMU',
|
|
'Invensense': 'Sensors.AP_ICM20689',
|
|
'Invensensev2': 'Sensors.AP_InvensenseV2',
|
|
'Invensensev3': 'Sensors.AP_ICM42688',
|
|
'LSM9DS0': 'Sensors.AP_RegisterIMU',
|
|
'LSM6DSV': 'Sensors.AP_RegisterIMU',
|
|
'SCHA63T': 'Sensors.AP_SCHA63T',
|
|
}
|
|
|
|
WHOAMI_VALUES = {
|
|
'MPU_WHOAMI_MPU60X0': 0x68,
|
|
'MPU_WHOAMI_MPU9250': 0x71,
|
|
'MPU_WHOAMI_ICM20608': 0xAF,
|
|
'MPU_WHOAMI_ICM20602': 0x12,
|
|
'MPU_WHOAMI_ICM20689': 0x98,
|
|
'INV2_WHOAMI_ICM20948': 0xEA,
|
|
'INV2_WHOAMI_ICM20649': 0xE1,
|
|
}
|
|
|
|
ROTATIONS = {
|
|
name: value for value, name in enumerate((
|
|
'ROTATION_NONE',
|
|
'ROTATION_YAW_45',
|
|
'ROTATION_YAW_90',
|
|
'ROTATION_YAW_135',
|
|
'ROTATION_YAW_180',
|
|
'ROTATION_YAW_225',
|
|
'ROTATION_YAW_270',
|
|
'ROTATION_YAW_315',
|
|
'ROTATION_ROLL_180',
|
|
'ROTATION_ROLL_180_YAW_45',
|
|
'ROTATION_ROLL_180_YAW_90',
|
|
'ROTATION_ROLL_180_YAW_135',
|
|
'ROTATION_PITCH_180',
|
|
'ROTATION_ROLL_180_YAW_225',
|
|
'ROTATION_ROLL_180_YAW_270',
|
|
'ROTATION_ROLL_180_YAW_315',
|
|
'ROTATION_ROLL_90',
|
|
'ROTATION_ROLL_90_YAW_45',
|
|
'ROTATION_ROLL_90_YAW_90',
|
|
'ROTATION_ROLL_90_YAW_135',
|
|
'ROTATION_ROLL_270',
|
|
'ROTATION_ROLL_270_YAW_45',
|
|
'ROTATION_ROLL_270_YAW_90',
|
|
'ROTATION_ROLL_270_YAW_135',
|
|
'ROTATION_PITCH_90',
|
|
'ROTATION_PITCH_270',
|
|
'ROTATION_PITCH_180_YAW_90',
|
|
'ROTATION_PITCH_180_YAW_270',
|
|
'ROTATION_ROLL_90_PITCH_90',
|
|
'ROTATION_ROLL_180_PITCH_90',
|
|
'ROTATION_ROLL_270_PITCH_90',
|
|
'ROTATION_ROLL_90_PITCH_180',
|
|
'ROTATION_ROLL_270_PITCH_180',
|
|
'ROTATION_ROLL_90_PITCH_270',
|
|
'ROTATION_ROLL_180_PITCH_270',
|
|
'ROTATION_ROLL_270_PITCH_270',
|
|
'ROTATION_ROLL_90_PITCH_180_YAW_90',
|
|
'ROTATION_ROLL_90_YAW_270',
|
|
'ROTATION_ROLL_90_PITCH_68_YAW_293',
|
|
'ROTATION_PITCH_315',
|
|
'ROTATION_ROLL_90_PITCH_315',
|
|
'ROTATION_PITCH_7',
|
|
'ROTATION_ROLL_45',
|
|
'ROTATION_ROLL_315',
|
|
))
|
|
}
|
|
|
|
BARO_MODELS = {
|
|
'AUAV': 'Sensors.AP_Baro_AUAV',
|
|
'BMP085': 'Sensors.AP_BMP085',
|
|
'BMP280': 'Sensors.AP_BMP280',
|
|
'BMP388': 'Sensors.AP_BMP388',
|
|
'BMP581': 'Sensors.AP_BMP581',
|
|
'DPS280': 'Sensors.AP_DPS310',
|
|
'DPS310': 'Sensors.AP_DPS310',
|
|
'ICM20789': 'Sensors.AP_ICM20789Barometer',
|
|
'ICP101XX': 'Sensors.AP_ICP101XX',
|
|
'ICP201XX': 'Sensors.AP_ICP201XX',
|
|
'LPS2XH': 'Sensors.AP_LPS2XH',
|
|
'MS5611': 'Sensors.AP_MS5611',
|
|
'SPL06': 'Sensors.AP_DPS310',
|
|
}
|
|
|
|
SPI_BARO_IDENTITIES = {
|
|
'BMP280': (0x58, 0xD0),
|
|
'BMP388': (0x50, 0x00),
|
|
'BMP581': (0x50, 0x01),
|
|
'DPS280': (0x10, 0x0D),
|
|
'DPS310': (0x10, 0x0D),
|
|
'LPS2XH': (0xB1, 0x0F),
|
|
'SPL06': (0x10, 0x0D),
|
|
}
|
|
|
|
|
|
def _hwdef_root(root):
|
|
return root / 'libraries' / 'AP_HAL_ChibiOS' / 'hwdef'
|
|
|
|
|
|
def _resolved_mcu(path, seen=None):
|
|
'''Find the effective MCU while following the same relative includes.'''
|
|
if seen is None:
|
|
seen = set()
|
|
path = path.resolve()
|
|
if path in seen:
|
|
return None
|
|
seen.add(path)
|
|
mcu = None
|
|
try:
|
|
lines = path.read_text().splitlines()
|
|
except OSError:
|
|
return None
|
|
for raw in lines:
|
|
line = raw.split('#', 1)[0].strip()
|
|
if not line:
|
|
continue
|
|
fields = shlex.split(line)
|
|
if fields[0] == 'include' and len(fields) == 2:
|
|
included = _resolved_mcu(path.parent / fields[1], seen)
|
|
if included is not None:
|
|
mcu = included
|
|
elif fields[0] == 'MCU' and len(fields) >= 3:
|
|
mcu = fields[2]
|
|
elif fields[0] == 'undef' and len(fields) == 2 and fields[1] == 'MCU':
|
|
mcu = None
|
|
return mcu
|
|
|
|
|
|
def _resolved_env(path, name, seen=None):
|
|
'''Find an inherited hwdef env value while preserving include order.'''
|
|
if seen is None:
|
|
seen = set()
|
|
path = path.resolve()
|
|
if path in seen:
|
|
return None
|
|
seen.add(path)
|
|
value = None
|
|
try:
|
|
lines = path.read_text().splitlines()
|
|
except OSError:
|
|
return None
|
|
for raw in lines:
|
|
line = raw.split('#', 1)[0].strip()
|
|
if not line:
|
|
continue
|
|
fields = shlex.split(line)
|
|
if fields[0] == 'include' and len(fields) == 2:
|
|
included = _resolved_env(path.parent / fields[1], name, seen)
|
|
if included is not None:
|
|
value = included
|
|
elif fields[0] == 'env' and len(fields) >= 3 and fields[1] == name:
|
|
value = fields[2]
|
|
return value
|
|
|
|
|
|
def supported_boards(root):
|
|
'''Board name -> MCU for targets with enough hwdef data to run directly.'''
|
|
found = {}
|
|
for board_dir in sorted(_hwdef_root(root).iterdir()):
|
|
app = board_dir / 'hwdef.dat'
|
|
bootloader = board_dir / 'hwdef-bl.dat'
|
|
if not app.is_file():
|
|
continue
|
|
is_periph = _resolved_env(app, 'AP_PERIPH') == '1'
|
|
if not bootloader.is_file() and not is_periph:
|
|
continue
|
|
mcu = _resolved_mcu(app)
|
|
if mcu in FAMILIES:
|
|
found[board_dir.name] = mcu
|
|
return found
|
|
|
|
|
|
def is_periph_board(root, board):
|
|
return _resolved_env(_hwdef_root(root) / board / 'hwdef.dat', 'AP_PERIPH') == '1'
|
|
|
|
|
|
def _load_compiler(root):
|
|
scripts = _hwdef_root(root) / 'scripts'
|
|
if str(scripts) not in sys.path:
|
|
sys.path.insert(0, str(scripts))
|
|
from chibios_hwdef import ChibiOSHWDef
|
|
return ChibiOSHWDef
|
|
|
|
|
|
def _compile_hwdef(root, hwdef, outdir, bootloader=False):
|
|
'''Run the production hwdef parser and return its resolved object.'''
|
|
outdir.mkdir(parents=True, exist_ok=True)
|
|
compiler = _load_compiler(root)
|
|
config = compiler(
|
|
outdir=str(outdir),
|
|
bootloader=bootloader,
|
|
quiet=True,
|
|
hwdef=[str(hwdef)],
|
|
# The direct script currently requires a string here. A deliberately
|
|
# absent absolute path still allows the board's defaults.parm fallback.
|
|
default_params_filepath=str(outdir / '__no_command_line_defaults__'),
|
|
)
|
|
# Some sensor conversion helpers print diagnostics independently of quiet.
|
|
diagnostics = io.StringIO()
|
|
try:
|
|
with contextlib.redirect_stdout(diagnostics):
|
|
config.run()
|
|
except SystemExit as error:
|
|
message = diagnostics.getvalue().strip()
|
|
raise ValueError(message or 'hwdef compilation failed') from error
|
|
return config
|
|
|
|
|
|
def _can_buses(app, family):
|
|
configured = family.get('can_buses', {})
|
|
order = app.get_config('CAN_ORDER', required=False, aslist=True) or []
|
|
if order:
|
|
buses = []
|
|
for entry in order:
|
|
bus = str(entry).upper()
|
|
if not bus.startswith('CAN'):
|
|
bus = 'CAN%s' % bus
|
|
if bus in configured and bus in app.bytype:
|
|
buses.append((bus, configured[bus]))
|
|
return buses
|
|
return [(bus, peripheral) for bus, peripheral in configured.items()
|
|
if bus in app.bytype]
|
|
|
|
|
|
def _configuration_ports(app, family):
|
|
ports = []
|
|
serial_order = app.get_config(
|
|
'SERIAL_ORDER', required=False, aslist=True) or []
|
|
for index, peripheral in enumerate(serial_order):
|
|
if peripheral in family['uarts']:
|
|
target = peripheral.lower()
|
|
if family['name'] in (
|
|
'h743', 'h757', 'f303', 'f767', 'g474', 'l4'):
|
|
target += 'Host'
|
|
ports.append({
|
|
'id': 'SERIAL%u' % index,
|
|
'name': 'SERIAL%u (%s)' % (index, peripheral),
|
|
'bus': 'uart',
|
|
'peripheral': peripheral,
|
|
'target': target,
|
|
'index': index,
|
|
})
|
|
|
|
i2c_order = app.get_config(
|
|
'I2C_ORDER', required=False, aslist=True) or []
|
|
for index, peripheral in enumerate(i2c_order):
|
|
if peripheral in family['i2cs']:
|
|
ports.append({
|
|
'id': 'I2C%u' % index,
|
|
'name': 'I2C bus %u (%s)' % (index, peripheral),
|
|
'bus': 'i2c',
|
|
'peripheral': peripheral,
|
|
'index': index,
|
|
})
|
|
|
|
for index, (bus, peripheral) in enumerate(_can_buses(app, family)):
|
|
ports.append({
|
|
'id': 'CAN%u' % (index + 1),
|
|
'name': 'CAN port %u (%s)' % (index + 1, bus),
|
|
'bus': 'can',
|
|
'peripheral': peripheral,
|
|
'can_bus': bus,
|
|
'index': index,
|
|
})
|
|
return ports
|
|
|
|
|
|
def configuration_ports(root, board, outdir):
|
|
'''Return user-connectable ports from the expanded application hwdef.'''
|
|
boards = supported_boards(root)
|
|
if board not in boards:
|
|
raise ValueError('%s is not a supported Renode target' % board)
|
|
app = _compile_hwdef(
|
|
root, _hwdef_root(root) / board / 'hwdef.dat', Path(outdir))
|
|
return _configuration_ports(app, FAMILIES[app.mcu_type])
|
|
|
|
|
|
def _resolve_attachments(app, family, attachments):
|
|
ports = {port['id']: port for port in _configuration_ports(app, family)}
|
|
resolved = []
|
|
occupied = set()
|
|
occupied_physics = {}
|
|
for index, attachment in enumerate(attachments or []):
|
|
if not isinstance(attachment, dict):
|
|
raise ValueError('device attachment %u is not an object' % index)
|
|
device_id = attachment.get('device')
|
|
port_id = attachment.get('port')
|
|
device = ATTACHABLE_DEVICES.get(device_id)
|
|
port = ports.get(port_id)
|
|
if device is None:
|
|
raise ValueError('unknown emulated device %s' % device_id)
|
|
if port is None:
|
|
raise ValueError('unknown configuration port %s' % port_id)
|
|
if device['bus'] != port['bus']:
|
|
raise ValueError('%s cannot attach to %s' %
|
|
(device['name'], port['name']))
|
|
configuration = attachment.get('configuration', {})
|
|
if not isinstance(configuration, dict):
|
|
raise ValueError('%s configuration is not an object' % device['name'])
|
|
allowed_configuration = set()
|
|
if device['category'] in ('Compass', 'Rangefinder'):
|
|
allowed_configuration.add('orientation')
|
|
if device['bus'] == 'can':
|
|
allowed_configuration.add('node_id')
|
|
unknown_configuration = set(configuration) - allowed_configuration
|
|
if unknown_configuration:
|
|
raise ValueError('unsupported %s setting %s' % (
|
|
device['name'], sorted(unknown_configuration)[0]))
|
|
if 'orientation' in configuration:
|
|
orientation = configuration['orientation']
|
|
if (isinstance(orientation, bool) or
|
|
not isinstance(orientation, int) or
|
|
not 0 <= orientation <= 43):
|
|
raise ValueError('%s orientation must be from 0 to 43' %
|
|
device['name'])
|
|
if 'node_id' in configuration:
|
|
node_id = configuration['node_id']
|
|
if (isinstance(node_id, bool) or not isinstance(node_id, int) or
|
|
not 0 <= node_id <= 127):
|
|
raise ValueError('%s node ID must be from 0 to 127' %
|
|
device['name'])
|
|
if port['bus'] == 'uart' and port_id in occupied:
|
|
raise ValueError('%s already has an attached device' % port['name'])
|
|
if port['bus'] == 'i2c':
|
|
addresses = tuple(
|
|
endpoint['address'] for endpoint in i2c_endpoints(device))
|
|
conflict = next(
|
|
(address for address in addresses
|
|
if (port_id, address) in occupied), None)
|
|
if conflict is not None:
|
|
raise ValueError('%s already has a device at 0x%02X' %
|
|
(port['name'], conflict))
|
|
occupied.update((port_id, address) for address in addresses)
|
|
else:
|
|
occupied.add(port_id)
|
|
physics_index = attachment.get('physics_index')
|
|
physics = device.get('physics')
|
|
if physics is None:
|
|
if physics_index is not None:
|
|
raise ValueError('%s has no selectable physics channel' %
|
|
device['name'])
|
|
else:
|
|
source = physics['source']
|
|
used = occupied_physics.setdefault(source, set())
|
|
if physics_index is None:
|
|
physics_index = next((candidate for candidate in
|
|
range(physics['count'])
|
|
if candidate not in used), None)
|
|
if physics_index is None:
|
|
raise ValueError('no free %s physics channels' % source)
|
|
if (isinstance(physics_index, bool) or
|
|
not isinstance(physics_index, int) or
|
|
not 0 <= physics_index < physics['count']):
|
|
raise ValueError('%s physics index must be from 0 to %u' %
|
|
(source, physics['count'] - 1))
|
|
if physics_index in used:
|
|
raise ValueError('%s physics channel %u is already used' %
|
|
(source, physics_index))
|
|
used.add(physics_index)
|
|
resolved.append({
|
|
'id': 'configDevice%u' % index,
|
|
'device_id': device_id,
|
|
'device': device,
|
|
'port': port,
|
|
'physics_index': physics_index,
|
|
'configuration': dict(configuration),
|
|
})
|
|
return resolved
|
|
|
|
|
|
def _defines(path):
|
|
found = {}
|
|
for line in path.read_text().splitlines():
|
|
fields = line.split('//', 1)[0].split(None, 2)
|
|
if len(fields) == 3 and fields[0] == '#define':
|
|
# Board-specific definitions precede the guarded AP_Periph
|
|
# fallbacks in generated hwdef.h. The latter are only active
|
|
# when the former do not exist, so retain the first definition.
|
|
found.setdefault(fields[1], fields[2].strip())
|
|
return found
|
|
|
|
|
|
def _define_enabled(defines, name):
|
|
value = defines.get(name)
|
|
if value is None:
|
|
return False
|
|
try:
|
|
return _constant_integer(value) != 0
|
|
except ValueError:
|
|
return value not in ('FALSE', 'false')
|
|
|
|
|
|
def _constant_integer(expression):
|
|
'''Evaluate an integer-only hwdef expression without executing code.'''
|
|
operators = {
|
|
ast.Add: lambda left, right: left + right,
|
|
ast.Sub: lambda left, right: left - right,
|
|
ast.Mult: lambda left, right: left * right,
|
|
ast.FloorDiv: lambda left, right: left // right,
|
|
ast.LShift: lambda left, right: left << right,
|
|
ast.RShift: lambda left, right: left >> right,
|
|
ast.BitOr: lambda left, right: left | right,
|
|
ast.BitAnd: lambda left, right: left & right,
|
|
}
|
|
|
|
def evaluate(node):
|
|
if isinstance(node, ast.Constant) and isinstance(node.value, int):
|
|
return node.value
|
|
if isinstance(node, ast.UnaryOp) and isinstance(node.op, (ast.UAdd, ast.USub)):
|
|
value = evaluate(node.operand)
|
|
return value if isinstance(node.op, ast.UAdd) else -value
|
|
if isinstance(node, ast.BinOp) and type(node.op) in operators:
|
|
return operators[type(node.op)](evaluate(node.left), evaluate(node.right))
|
|
raise ValueError('not a constant integer expression: %s' % expression)
|
|
|
|
return evaluate(ast.parse(expression, mode='eval').body)
|
|
|
|
|
|
def _constant_number(expression):
|
|
'''Evaluate the simple floating-point expressions emitted by hwdef.'''
|
|
operators = {
|
|
ast.Add: lambda left, right: left + right,
|
|
ast.Sub: lambda left, right: left - right,
|
|
ast.Mult: lambda left, right: left * right,
|
|
ast.Div: lambda left, right: left / right,
|
|
}
|
|
|
|
def evaluate(node):
|
|
if (isinstance(node, ast.Constant) and
|
|
isinstance(node.value, (int, float))):
|
|
return node.value
|
|
if isinstance(node, ast.UnaryOp) and isinstance(node.op, (ast.UAdd, ast.USub)):
|
|
value = evaluate(node.operand)
|
|
return value if isinstance(node.op, ast.UAdd) else -value
|
|
if isinstance(node, ast.BinOp) and type(node.op) in operators:
|
|
return operators[type(node.op)](evaluate(node.left), evaluate(node.right))
|
|
raise ValueError('not a constant numeric expression: %s' % expression)
|
|
|
|
cleaned = re.sub(r'(?<=\d)[fFuUlL]+\b', '', expression)
|
|
return float(evaluate(ast.parse(cleaned, mode='eval').body))
|
|
|
|
|
|
def _analog_pins(path):
|
|
'''Return logical analog pin -> (ADC channel, volts/count).'''
|
|
pins = {}
|
|
in_macro = False
|
|
for line in path.read_text().splitlines():
|
|
if line.startswith('#define HAL_ANALOG_PINS'):
|
|
in_macro = True
|
|
continue
|
|
if not in_macro:
|
|
continue
|
|
match = re.match(
|
|
r'\s*\{\s*(\d+)\s*,\s*(\d+)\s*,\s*([^}]+)\}', line)
|
|
if match is None:
|
|
break
|
|
channel, logical_pin, scale = match.groups()
|
|
pins[int(logical_pin)] = (int(channel), _constant_number(scale.strip()))
|
|
return pins
|
|
|
|
|
|
def _default_parameters(path):
|
|
parameters = {}
|
|
if not path.is_file():
|
|
return parameters
|
|
for line in path.read_text().splitlines():
|
|
fields = line.split('#', 1)[0].split()
|
|
if len(fields) >= 2:
|
|
parameters[fields[0]] = fields[1]
|
|
return parameters
|
|
|
|
|
|
def _crc32_small(data, padded_size):
|
|
'''Match AP_Math crc32_small(), including erased IO flash padding.'''
|
|
if len(data) > padded_size:
|
|
raise ValueError('IO firmware is larger than AP_IOMCU_FW_FLASH_SIZE')
|
|
crc = 0
|
|
for value in data + b'\xff' * (padded_size - len(data)):
|
|
crc ^= value
|
|
for _ in range(8):
|
|
crc = (crc >> 1) ^ (0xEDB88320 if crc & 1 else 0)
|
|
return crc
|
|
|
|
|
|
def _dma_stream(value):
|
|
match = re.fullmatch(r'STM32_DMA_STREAM_ID\((\d+),(\d+)\)', value.replace(' ', ''))
|
|
if match is None:
|
|
return None
|
|
return int(match.group(1)), int(match.group(2))
|
|
|
|
|
|
def _imu_whoami(device_name, defines):
|
|
'''Find the board-validation WHOAMI for a named SPI sensor, if present.'''
|
|
validation = defines.get('HAL_VALIDATE_BOARD', '')
|
|
match = re.search(
|
|
r'spi_check_register(?:_inv2)?\(\s*"%s"\s*,[^,]+,\s*([A-Za-z0-9_xX]+)' %
|
|
re.escape(device_name), validation)
|
|
if match is not None:
|
|
value = match.group(1)
|
|
if value in WHOAMI_VALUES:
|
|
return WHOAMI_VALUES[value]
|
|
try:
|
|
return int(value, 0)
|
|
except ValueError:
|
|
pass
|
|
names = {
|
|
'icm20602': 0x12,
|
|
'icm20608': 0xAF,
|
|
'icm20689': 0x98,
|
|
'icm20948': 0xEA,
|
|
'icm20649': 0xE1,
|
|
'icm42670': 0x67,
|
|
'icm42688': 0x47,
|
|
'icm45686': 0xE9,
|
|
'mpu6000': 0x68,
|
|
'mpu9250': 0x71,
|
|
}
|
|
for part, value in names.items():
|
|
if part in device_name.lower():
|
|
return value
|
|
return None
|
|
|
|
|
|
def _imu_children(imu, resolved_devices, defines, name):
|
|
'''Return model/device/property tuples for one resolved IMU declaration.'''
|
|
driver = imu[0]
|
|
model = IMU_MODELS[driver]
|
|
rotation_name = next(
|
|
(argument for argument in imu[1:] if argument.startswith('ROTATION_')),
|
|
'ROTATION_NONE')
|
|
rotation = ROTATIONS.get(rotation_name)
|
|
rotation_property = [] if rotation is None else [' rotation: %u' % rotation]
|
|
if driver == 'SCHA63T':
|
|
return [('%sPart%d' % (name, index), model, bus, cs, rotation_property)
|
|
for index, (_, bus, cs) in enumerate(resolved_devices)]
|
|
if driver in ('BMI055', 'BMI088', 'LSM9DS0'):
|
|
children = []
|
|
for index, (device_name, bus, cs) in enumerate(resolved_devices):
|
|
is_accel = index == 0 or device_name.lower().endswith(('_a', 'accel'))
|
|
if driver == 'LSM9DS0':
|
|
whoami = 0x49 if index else 0xD4
|
|
else:
|
|
whoami = (0xFA if driver == 'BMI055' else 0x1E) if is_accel else 0x0F
|
|
children.append((
|
|
'%s%s' % (name, 'Accel' if is_accel else 'Gyro'),
|
|
model, bus, cs,
|
|
[' whoAmI: 0x%02X' % whoami] + rotation_property))
|
|
return children
|
|
|
|
device_name, bus, cs = resolved_devices[0]
|
|
properties = list(rotation_property)
|
|
fixed_whoami = {
|
|
'BMI160': 0xD1,
|
|
'BMI270': 0x24,
|
|
'LSM6DSV': 0x70,
|
|
}.get(driver)
|
|
whoami = fixed_whoami if fixed_whoami is not None else _imu_whoami(
|
|
device_name, defines)
|
|
if whoami is not None:
|
|
properties.append(' whoAmI: 0x%02X' % whoami)
|
|
return [(name, model, bus, cs, properties)]
|
|
|
|
|
|
def _dmamux_requests_from(path):
|
|
requests = {}
|
|
for line in path.read_text().splitlines():
|
|
match = re.match(r'#define\s+(STM32_DMAMUX1_\w+)\s+(\d+)', line)
|
|
if match:
|
|
requests[match.group(1)] = int(match.group(2))
|
|
return requests
|
|
|
|
|
|
def _dmamux_requests(root):
|
|
path = (root / 'modules' / 'ChibiOS' / 'os' / 'hal' / 'ports' /
|
|
'STM32' / 'STM32H7xx' / 'stm32_dmamux.h')
|
|
return _dmamux_requests_from(path)
|
|
|
|
|
|
def _g474_dmamux_requests(root):
|
|
path = (root / 'modules' / 'ChibiOS' / 'os' / 'hal' / 'ports' /
|
|
'STM32' / 'STM32G4xx' / 'stm32_dmamux.h')
|
|
return _dmamux_requests_from(path)
|
|
|
|
|
|
def _l4plus_dmamux_requests(root):
|
|
path = (root / 'modules' / 'ChibiOS' / 'os' / 'hal' / 'ports' /
|
|
'STM32' / 'STM32L4xx+' / 'stm32_dmamux.h')
|
|
return _dmamux_requests_from(path)
|
|
|
|
|
|
def _safe_name(value):
|
|
return re.sub(r'[^a-zA-Z0-9_]', '_', value)
|
|
|
|
|
|
def _spi_device_names(expression):
|
|
matches = re.findall(r'get_device\("([^"\n]+)"\)', expression)
|
|
if matches:
|
|
return matches
|
|
match = re.fullmatch(r'SPI:([^\s]+)', expression)
|
|
return [match.group(1)] if match else []
|
|
|
|
|
|
def _i2c_device(expression):
|
|
match = re.fullmatch(r'GET_I2C_DEVICE\((\d+),\s*(0x[0-9A-Fa-f]+|\d+)\)', expression)
|
|
if match is None:
|
|
return None
|
|
return int(match.group(1)), int(match.group(2), 0)
|
|
|
|
|
|
def _icm20948_compass_rotations(config):
|
|
'''Map Invensense-v2 instance to the hwdef rotation of its AK09916.'''
|
|
rotations = {}
|
|
for compass in config.compass_list:
|
|
if compass.driver != 'AK09916' or compass.probe != 'probe_ICM20948':
|
|
continue
|
|
if compass.devlist and str(compass.devlist[0]).isdigit():
|
|
rotations[int(compass.devlist[0])] = ROTATIONS.get(
|
|
compass.rotation, 0)
|
|
return rotations
|
|
|
|
|
|
def _sensor_devices(config, family, defines, fram_path, warnings,
|
|
sigrok_bus=None, num_imus=None, imu_names=None):
|
|
'''Return sensor declarations, CS routes and storage metadata.'''
|
|
spi_devices = {dev[0]: dev for dev in config.spidev}
|
|
declarations = []
|
|
chip_selects = {}
|
|
spi_children = []
|
|
|
|
imu_locations = set()
|
|
emulated_imus = 0
|
|
inv2_instance = 0
|
|
icm20948_compass_rotations = _icm20948_compass_rotations(config)
|
|
available_imu_names = set()
|
|
selected_imu_names = None if imu_names is None else set(imu_names)
|
|
for index, imu in enumerate(config.imu_list):
|
|
if len(imu) < 2:
|
|
warnings.append('unmodelled IMU: %s' % ' '.join(imu))
|
|
continue
|
|
device_names = []
|
|
for argument in imu[1:]:
|
|
for device_name in _spi_device_names(argument):
|
|
if device_name not in spi_devices:
|
|
aliases = [device_name.removesuffix('_cs')]
|
|
device_name = next(
|
|
(alias for alias in aliases if alias in spi_devices),
|
|
device_name)
|
|
if device_name in spi_devices and device_name not in device_names:
|
|
device_names.append(device_name)
|
|
available_imu_names.update(device_names)
|
|
if (selected_imu_names is not None and
|
|
not selected_imu_names.intersection(device_names)):
|
|
continue
|
|
if num_imus is not None and emulated_imus >= num_imus:
|
|
continue
|
|
if not device_names:
|
|
i2c_devices = [device for device in map(_i2c_device, imu[1:])
|
|
if device is not None]
|
|
if i2c_devices and imu[0] in ('BMI055', 'BMI088', 'Invensense'):
|
|
rotation_name = next(
|
|
(argument for argument in imu[1:]
|
|
if argument.startswith('ROTATION_')),
|
|
'ROTATION_NONE')
|
|
rotation = ROTATIONS.get(rotation_name)
|
|
i2c_order = config.get_config(
|
|
'I2C_ORDER', required=False, aslist=True) or []
|
|
resolved_i2c = []
|
|
for device_index, (bus_index, address) in enumerate(i2c_devices):
|
|
if not 0 <= bus_index < len(i2c_order):
|
|
continue
|
|
bus = i2c_order[bus_index]
|
|
if bus not in family['i2cs']:
|
|
continue
|
|
location = (bus, address)
|
|
if location in imu_locations:
|
|
continue
|
|
imu_locations.add(location)
|
|
whoami = 0x0F
|
|
if device_index == 0:
|
|
whoami = 0xFA if imu[0] == 'BMI055' else 0x1E
|
|
if imu[0] == 'Invensense':
|
|
whoami = 0x68
|
|
resolved_i2c.append((bus, address, whoami))
|
|
for device_index, (bus, address, whoami) in enumerate(resolved_i2c):
|
|
declarations += [
|
|
'imu%dI2C%d: Sensors.AP_I2CRegisterIMU @ %s 0x%02X' %
|
|
(index, device_index, bus.lower(), address),
|
|
' whoAmI: 0x%02X' % whoami,
|
|
]
|
|
if rotation is not None:
|
|
declarations.append(' rotation: %u' % rotation)
|
|
declarations.append('')
|
|
if resolved_i2c:
|
|
emulated_imus += 1
|
|
continue
|
|
warnings.append('cannot resolve IMU SPI device: %s' % ' '.join(imu))
|
|
continue
|
|
locations = []
|
|
resolved_devices = []
|
|
for device_name in device_names:
|
|
device = spi_devices[device_name]
|
|
bus, cs_label = device[1], device[3]
|
|
if bus not in family['spis']:
|
|
warnings.append('%s is not present in the current MCU base' % bus)
|
|
break
|
|
cs = config.bylabel.get(cs_label)
|
|
if cs is None:
|
|
warnings.append('cannot resolve chip select %s' % cs_label)
|
|
break
|
|
locations.append((bus, cs.port, cs.pin))
|
|
resolved_devices.append((device_name, bus, cs))
|
|
if len(resolved_devices) != len(device_names):
|
|
continue
|
|
if imu[0] not in IMU_MODELS:
|
|
warnings.append('unmodelled IMU: %s' % ' '.join(imu))
|
|
continue
|
|
if any(location in imu_locations for location in locations):
|
|
continue
|
|
imu_locations.update(locations)
|
|
name = 'imu%d' % index
|
|
children = _imu_children(imu, resolved_devices, defines, name)
|
|
if children:
|
|
if imu[0] == 'Invensensev2':
|
|
# AK09916 behind the ICM20948 I2C master, probed by instance
|
|
rotation = icm20948_compass_rotations.get(inv2_instance)
|
|
if rotation is not None:
|
|
children[0][4].append(
|
|
' AuxiliaryCompassRotation: %u' % rotation)
|
|
inv2_instance += 1
|
|
spi_children += children
|
|
emulated_imus += 1
|
|
|
|
if selected_imu_names is not None:
|
|
unknown = selected_imu_names - available_imu_names
|
|
if unknown:
|
|
raise ValueError('unknown IMU device%s: %s' % (
|
|
'' if len(unknown) == 1 else 's', ', '.join(sorted(unknown))))
|
|
|
|
i2c_order = config.get_config('I2C_ORDER', required=False, aslist=True) or []
|
|
baro_locations = set()
|
|
for index, baro in enumerate(config.baro_list):
|
|
if not baro.devlist:
|
|
warnings.append('unsupported barometer bus: %s' % baro.driver)
|
|
continue
|
|
device = baro.devlist[0]
|
|
i2c_declaration = None
|
|
cs = None
|
|
if hasattr(device, 'busnum') and hasattr(device, 'busaddr'):
|
|
if not 0 <= device.busnum < len(i2c_order):
|
|
warnings.append('invalid I2C index for barometer %s' % baro.driver)
|
|
continue
|
|
bus = i2c_order[device.busnum]
|
|
if bus not in family['i2cs']:
|
|
warnings.append('%s is not present in the current MCU base' % bus)
|
|
continue
|
|
location = (bus, device.busaddr)
|
|
i2c_declaration = 'baro%d: %%s @ %s 0x%02X' % (
|
|
index, bus.lower(), device.busaddr)
|
|
elif hasattr(device, 'name'):
|
|
spi_device = spi_devices.get(device.name)
|
|
if spi_device is None:
|
|
warnings.append('cannot resolve barometer SPI device: %s' % device.name)
|
|
continue
|
|
bus, cs_label = spi_device[1], spi_device[3]
|
|
if bus not in family['spis']:
|
|
warnings.append('%s is not present in the current MCU base' % bus)
|
|
continue
|
|
cs = config.bylabel.get(cs_label)
|
|
if cs is None:
|
|
warnings.append('cannot resolve chip select %s' % cs_label)
|
|
continue
|
|
location = (bus, cs.port, cs.pin)
|
|
else:
|
|
warnings.append('unsupported barometer bus: %s' % baro.driver)
|
|
continue
|
|
if location in baro_locations:
|
|
continue
|
|
baro_locations.add(location)
|
|
model = BARO_MODELS.get(baro.driver)
|
|
if model is None:
|
|
warnings.append('unmodelled barometer: %s' % baro.driver)
|
|
continue
|
|
name = 'baro%d' % index
|
|
if cs is not None:
|
|
properties = []
|
|
identity = SPI_BARO_IDENTITIES.get(baro.driver)
|
|
if identity is not None:
|
|
model = 'Sensors.AP_SPIBarometer'
|
|
properties = [
|
|
' chipId: 0x%02X' % identity[0],
|
|
' chipIdRegister: 0x%02X' % identity[1],
|
|
]
|
|
spi_children.append((name, model, bus, cs, properties))
|
|
else:
|
|
declarations += [i2c_declaration % model, '']
|
|
|
|
has_fram = defines.get('HAL_WITH_RAMTRON', '0') != '0'
|
|
if has_fram:
|
|
device = spi_devices.get('ramtron')
|
|
if device is None:
|
|
warnings.append('HAL_WITH_RAMTRON is set without SPIDEV ramtron')
|
|
has_fram = False
|
|
else:
|
|
bus, cs_label = device[1], device[3]
|
|
cs = config.bylabel.get(cs_label)
|
|
if bus not in family['spis']:
|
|
warnings.append('%s RAMTRON bus is not present in the current MCU base' % bus)
|
|
has_fram = False
|
|
elif cs is None:
|
|
warnings.append('cannot resolve RAMTRON chip select %s' % cs_label)
|
|
has_fram = False
|
|
else:
|
|
spi_children.append((
|
|
'fram', 'Miscellaneous.AP_RAMTRON', bus, cs,
|
|
[' fileName: %s' % json.dumps(str(fram_path))]))
|
|
|
|
spi_sdcard = None
|
|
if defines.get('HAL_USE_MMC_SPI', 'FALSE') == 'TRUE':
|
|
device = spi_devices.get('sdcard')
|
|
if device is None:
|
|
warnings.append('HAL_USE_MMC_SPI is set without SPIDEV sdcard')
|
|
else:
|
|
bus, cs_label = device[1], device[3]
|
|
cs = config.bylabel.get(cs_label)
|
|
if bus not in family['spis']:
|
|
warnings.append('%s SD card bus is not present in the current MCU base' % bus)
|
|
elif cs is None:
|
|
warnings.append('cannot resolve SD card chip select %s' % cs_label)
|
|
else:
|
|
# The file-backed card is created in the RESC once its image
|
|
# path is known. Reserve an address in the bus multiplexer now.
|
|
spi_children.append((None, None, bus, cs, []))
|
|
|
|
children_by_bus = {}
|
|
for child in spi_children:
|
|
children_by_bus.setdefault(child[2], []).append(child)
|
|
if sigrok_bus is not None:
|
|
children_by_bus.setdefault(sigrok_bus, [])
|
|
for bus in sorted(children_by_bus):
|
|
mux = '%sMux' % bus.lower()
|
|
children = children_by_bus[bus]
|
|
declarations += [
|
|
'%s: Miscellaneous.AP_SPIMultiplexer @ %s' % (mux, bus.lower()),
|
|
]
|
|
if bus == sigrok_bus:
|
|
declarations.append(' Analyzer: sigrok')
|
|
declarations.append('')
|
|
for address, (name, model, _, cs, properties) in enumerate(children):
|
|
if model is None:
|
|
spi_sdcard = (bus.lower(), mux, address)
|
|
else:
|
|
declarations += [
|
|
'%s: %s @ %s %d' % (name, model, mux, address),
|
|
] + properties + ['']
|
|
chip_selects.setdefault((cs.port, cs.pin), []).append(
|
|
'%s@%d' % (mux, address))
|
|
|
|
return declarations, chip_selects, has_fram, emulated_imus, spi_sdcard
|
|
|
|
|
|
def _iomcu_device(root, app, family, defines, address, warnings,
|
|
synthetic_iomcu=True):
|
|
uart = app.get_config('IOMCU_UART', required=False, default=None)
|
|
if uart is None:
|
|
return [], None, None
|
|
if uart not in family['uarts']:
|
|
warnings.append('%s IOMCU UART is not present in the current MCU base' % uart)
|
|
return [], None, None
|
|
firmware = app.romfs.get('io_firmware.bin')
|
|
if firmware is None:
|
|
warnings.append('IOMCU_UART is set without ROMFS io_firmware.bin')
|
|
return [], None, None
|
|
firmware_path = Path(firmware)
|
|
if not firmware_path.is_absolute():
|
|
firmware_path = root / firmware_path
|
|
try:
|
|
data = firmware_path.read_bytes()
|
|
flash_size = _constant_integer(
|
|
defines.get('AP_IOMCU_FW_FLASH_SIZE', '0xF000'))
|
|
firmware_crc = _crc32_small(data, flash_size)
|
|
except (OSError, SyntaxError, ValueError, ZeroDivisionError) as error:
|
|
warnings.append('cannot configure IOMCU firmware CRC: %s' % error)
|
|
return [], None, None
|
|
lines = [
|
|
'iomcu: Miscellaneous.AP_IOMCU @ sysbus 0x%08X' % address,
|
|
' uart: %s' % uart.lower(),
|
|
' firmwareCrc: 0x%08X' % firmware_crc,
|
|
'',
|
|
] if synthetic_iomcu else []
|
|
return lines, uart, firmware_path
|
|
|
|
|
|
def _hwdef_gpios(app):
|
|
gpios = {}
|
|
pins = list(app.bylabel.values())
|
|
for alternate in app.altmap.values():
|
|
pins.extend(alternate.values())
|
|
for pin in pins:
|
|
gpio = pin.extra_value('GPIO', type=int)
|
|
if gpio is not None:
|
|
gpios[gpio] = pin
|
|
return sorted(gpios.items())
|
|
|
|
|
|
def _timer_actuator_devices(app, family, iomcu_uart, alloc, warnings):
|
|
'''Map expanded hwdef PWM(n) pins onto protocol actuator channels.'''
|
|
pins = list(app.bylabel.values())
|
|
for alternate in app.altmap.values():
|
|
pins.extend(alternate.values())
|
|
timer_channels = {}
|
|
outputs = {}
|
|
output_offset = 8 if iomcu_uart is not None else 0
|
|
for pin in pins:
|
|
pwm = pin.extra_value('PWM', type=int)
|
|
if pwm is None:
|
|
continue
|
|
match = re.fullmatch(r'TIM(\d+)_CH([1-4])(N?)', pin.label or '')
|
|
if match is None:
|
|
warnings.append('%s PWM(%u) is not a timer channel' %
|
|
(pin.portpin, pwm))
|
|
continue
|
|
timer = int(match.group(1))
|
|
channel = int(match.group(2))
|
|
complementary = bool(match.group(3))
|
|
output = output_offset + pwm - 1
|
|
if timer not in family['timers']:
|
|
warnings.append('TIM%u PWM(%u) is not in the current MCU base' %
|
|
(timer, pwm))
|
|
continue
|
|
if not 0 <= output < 32:
|
|
warnings.append('PWM(%u) is outside the physics actuator range' % pwm)
|
|
continue
|
|
key = (timer, channel)
|
|
if key in timer_channels and timer_channels[key][0] != output:
|
|
warnings.append('TIM%u_CH%u has conflicting PWM outputs' % key)
|
|
continue
|
|
if output in outputs and outputs[output] != key:
|
|
warnings.append('PWM(%u) is assigned to multiple timer channels' % pwm)
|
|
continue
|
|
timer_channels[key] = (output, complementary)
|
|
outputs[output] = key
|
|
|
|
lines = []
|
|
for timer in sorted({key[0] for key in timer_channels}):
|
|
lines += [
|
|
'timer%dActuators: Miscellaneous.AP_STM32_Timer_Actuators @ '
|
|
'sysbus 0x%08X' % (timer, alloc()),
|
|
' timer: timer%d' % timer,
|
|
]
|
|
for channel in range(1, 5):
|
|
channel_config = timer_channels.get((timer, channel))
|
|
if channel_config is not None:
|
|
output, complementary = channel_config
|
|
lines.append(' output%d: %d' % (channel, output))
|
|
if complementary:
|
|
lines.append(' complementary%d: true' % channel)
|
|
lines.append('')
|
|
return lines
|
|
|
|
|
|
def _power_status_inputs(app):
|
|
'''Return hwdef power-status input pins and their simulated levels.'''
|
|
labels = (
|
|
'VDD_BRICK_VALID', 'VDD_BRICK_nVALID',
|
|
'VDD_BRICK2_VALID', 'VDD_BRICK2_nVALID',
|
|
'VBUS', 'VBUS_VALID', 'VBUS_nVALID',
|
|
'VDD_5V_HIPOWER_OC', 'VDD_5V_HIPOWER_nOC',
|
|
'VDD_5V_PERIPH_OC', 'VDD_5V_PERIPH_nOC',
|
|
)
|
|
inputs = []
|
|
for label in labels:
|
|
pin = app.bylabel.get(label)
|
|
if pin is None or pin.get_MODER_value() != 'INPUT':
|
|
continue
|
|
# The emulated board's 5 V supply represents USB power. Override the
|
|
# passive pull (or an unpulled/open-drain input) on its VBUS status
|
|
# input with the active level.
|
|
if label in ('VBUS', 'VBUS_VALID'):
|
|
level = True
|
|
elif label == 'VBUS_nVALID':
|
|
level = False
|
|
else:
|
|
pull = pin.get_PUPDR_value()
|
|
if pull == 'PULLUP':
|
|
level = True
|
|
elif pull == 'PULLDOWN':
|
|
level = False
|
|
else:
|
|
continue
|
|
inputs.append((pin, level))
|
|
return inputs
|
|
|
|
|
|
def _gpio_routes(family_name, chip_selects, sigrok_pins=None,
|
|
hwdef_gpios=None):
|
|
sigrok_pins = sigrok_pins or {}
|
|
hwdef_gpios = hwdef_gpios or []
|
|
lines = []
|
|
ports = sorted({port for port, _ in chip_selects} |
|
|
{port for port, _ in sigrok_pins} |
|
|
{pin.port for _, pin in hwdef_gpios})
|
|
for port in ports:
|
|
lines += ['gpioPort%s:' % port]
|
|
for pin in range(16):
|
|
targets = list(chip_selects.get((port, pin), []))
|
|
channel = sigrok_pins.get((port, pin))
|
|
if channel is not None:
|
|
targets.append('sigrok@%u' % channel)
|
|
if family_name in ('f103', 'f105'):
|
|
targets.append('afio#%d@%d' %
|
|
(ord(port) - ord('A'), pin))
|
|
else:
|
|
targets.append('syscfg#%d@%d' % (ord(port) - ord('A'), pin))
|
|
lines.append(' %d -> %s' % (pin, ' | '.join(targets)))
|
|
lines.append('')
|
|
return lines
|
|
|
|
|
|
def _f405_dma_wiring(defines, family, alloc, warnings):
|
|
lines = []
|
|
rx_uarts = []
|
|
|
|
if _define_enabled(defines, 'AP_PERIPH_BATTERY_ENABLED'):
|
|
for peripheral, model in family.get('adcs', {}).items():
|
|
value = defines.get('STM32_ADC_%s_DMA_STREAM' % peripheral)
|
|
stream = _dma_stream(value) if value else None
|
|
if stream is None:
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'%s:' % model,
|
|
' DMARequest -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
'%sCircularDma: Miscellaneous.AP_STM32DMA_Circular @ '
|
|
'sysbus 0x%08X' % (model, alloc()),
|
|
' dma: dma%d' % dma,
|
|
' stream: %d' % channel,
|
|
' adc: %s' % model,
|
|
'',
|
|
]
|
|
|
|
for bus, peripheral in family['sd_buses'].items():
|
|
value = defines.get('STM32_SDC_%s_DMA_STREAM' % bus)
|
|
stream = _dma_stream(value) if value else None
|
|
if stream is None:
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'%s:' % peripheral,
|
|
' DMAReceive -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
'%sDmaPump: Miscellaneous.AP_STM32F_SDMMC_DmaPump @ sysbus 0x%08X' %
|
|
(peripheral, alloc()),
|
|
' sdmmc: %s' % peripheral,
|
|
' dma: dma%d' % dma,
|
|
' stream: %d' % channel,
|
|
' fifoAddress: 0x%08X' % family['sd_fifo'][bus],
|
|
'',
|
|
]
|
|
|
|
for peripheral, irq in family['uart_irq'].items():
|
|
value = defines.get('STM32_UART_%s_RX_DMA_STREAM' % peripheral)
|
|
stream = _dma_stream(value) if value else None
|
|
if stream is None:
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' -> nvic@%d' % irq,
|
|
' DMARequest -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
]
|
|
rx_uarts.append((peripheral, dma, channel))
|
|
|
|
for peripheral, irq in family['spi_irq'].items():
|
|
value = defines.get('STM32_SPI_%s_RX_DMA_STREAM' % peripheral)
|
|
stream = _dma_stream(value) if value else None
|
|
if stream is None:
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' IRQ -> nvic@%d' % irq,
|
|
' DMARecieve -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
]
|
|
|
|
for peripheral, (event_irq, error_irq) in family['i2c_irq'].items():
|
|
value = defines.get('STM32_I2C_%s_RX_DMA_STREAM' % peripheral)
|
|
stream = _dma_stream(value) if value else None
|
|
if stream is None:
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' EventInterrupt -> nvic@%d' % event_irq,
|
|
' ErrorInterrupt -> nvic@%d' % error_irq,
|
|
' RxDmaRequest -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
]
|
|
|
|
for peripheral, dma, channel in rx_uarts:
|
|
name = _safe_name(peripheral.lower() + 'RxPump')
|
|
lines += [
|
|
'%s: Miscellaneous.AP_UartRxDmaPump @ sysbus 0x%08X' % (name, alloc()),
|
|
' dma: dma%d' % dma,
|
|
' stream: %d' % channel,
|
|
' uart: %s' % peripheral.lower(),
|
|
'',
|
|
]
|
|
|
|
for name, value in sorted(defines.items()):
|
|
match = re.fullmatch(r'STM32_TIM_TIM(\d+)_UP_DMA_STREAM', name)
|
|
stream = _dma_stream(value)
|
|
if match is None or stream is None:
|
|
continue
|
|
timer = int(match.group(1))
|
|
if timer not in family['timers']:
|
|
warnings.append('timer%d update DMA is not in the current MCU base' % timer)
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'timer%dUpdateDMA: Miscellaneous.AP_STM32_Timer_UpdateDMA @ sysbus 0x%08X' %
|
|
(timer, alloc()),
|
|
' timer: timer%d' % timer,
|
|
' UpdateDMA -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
]
|
|
return lines
|
|
|
|
|
|
def _f767_dma_wiring(defines, family, alloc, warnings):
|
|
lines = []
|
|
|
|
for bus, peripheral in family['sd_buses'].items():
|
|
value = defines.get('STM32_SDC_%s_DMA_STREAM' % bus)
|
|
stream = _dma_stream(value) if value else None
|
|
if stream is None:
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'%s:' % peripheral,
|
|
' DMAReceive -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
'%sDmaPump: Miscellaneous.AP_STM32F_SDMMC_DmaPump @ sysbus 0x%08X' %
|
|
(peripheral, alloc()),
|
|
' sdmmc: %s' % peripheral,
|
|
' dma: dma%d' % dma,
|
|
' stream: %d' % channel,
|
|
' fifoAddress: 0x%08X' % family['sd_fifo'][bus],
|
|
'',
|
|
]
|
|
|
|
for peripheral in family['uart_irq']:
|
|
value = defines.get('STM32_UART_%s_RX_DMA_STREAM' % peripheral)
|
|
stream = _dma_stream(value) if value else None
|
|
if stream is None:
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' ReceiveDmaRequest -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
'%sIdle: Miscellaneous.AP_STM32F7_USART_Idle @ sysbus 0x%08X' %
|
|
(peripheral.lower(), alloc()),
|
|
' uart: %s' % peripheral.lower(),
|
|
'',
|
|
]
|
|
|
|
for peripheral in family['spi_irq']:
|
|
value = defines.get('STM32_SPI_%s_RX_DMA_STREAM' % peripheral)
|
|
stream = _dma_stream(value) if value else None
|
|
if stream is None:
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' DMARecieve -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
]
|
|
|
|
for peripheral in family['i2c_irq']:
|
|
value = defines.get('STM32_I2C_%s_RX_DMA_STREAM' % peripheral)
|
|
stream = _dma_stream(value) if value else None
|
|
if stream is None:
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' RxDmaRequest -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
]
|
|
|
|
for name, value in sorted(defines.items()):
|
|
match = re.fullmatch(r'STM32_TIM_TIM(\d+)_UP_DMA_STREAM', name)
|
|
stream = _dma_stream(value)
|
|
if match is None or stream is None:
|
|
continue
|
|
timer = int(match.group(1))
|
|
if timer not in family['timers']:
|
|
warnings.append('timer%d update DMA is not in the current MCU base' % timer)
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'timer%dUpdateDMA: Miscellaneous.AP_STM32_Timer_UpdateDMA @ sysbus 0x%08X' %
|
|
(timer, alloc()),
|
|
' timer: timer%d' % timer,
|
|
' UpdateDMA -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
]
|
|
return lines
|
|
|
|
|
|
def _h743_dma_wiring(root, defines, family, alloc, warnings):
|
|
lines = []
|
|
requests = _dmamux_requests(root)
|
|
|
|
for peripheral, model in family.get('adcs', {}).items():
|
|
channel = defines.get('STM32_ADC_%s_DMA_CHAN' % peripheral)
|
|
request = requests.get(channel)
|
|
if request is None:
|
|
continue
|
|
lines += [
|
|
'%s:' % model,
|
|
' DMARequest -> dmamux1@%d' % request,
|
|
'',
|
|
]
|
|
stream = _dma_stream(defines.get(
|
|
'STM32_ADC_%s_DMA_STREAM' % peripheral, ''))
|
|
if stream is not None:
|
|
dma, dma_stream = stream
|
|
lines += [
|
|
'%sCircularDma: Miscellaneous.AP_STM32DMA_Circular @ '
|
|
'sysbus 0x%08X' % (model, alloc()),
|
|
' dma: dma%d' % dma,
|
|
' stream: %d' % dma_stream,
|
|
' adc: %s' % model,
|
|
'',
|
|
]
|
|
|
|
for peripheral, irq in family['uart_irq'].items():
|
|
channel = defines.get('STM32_UART_%s_RX_DMA_CHAN' % peripheral)
|
|
request = requests.get(channel)
|
|
if request is None:
|
|
continue
|
|
irq_target = 'exti@%d' % irq if peripheral != 'LPUART1' else 'exti@64'
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' IRQ -> %s' % irq_target,
|
|
' ReceiveDmaRequest -> dmamux1@%d' % request,
|
|
'',
|
|
'%sIdle: Miscellaneous.AP_STM32F7_USART_Idle @ sysbus 0x%08X' %
|
|
(peripheral.lower(), alloc()),
|
|
' uart: %s' % peripheral.lower(),
|
|
'',
|
|
]
|
|
|
|
for peripheral, irq in family['spi_irq'].items():
|
|
channel = defines.get('STM32_SPI_%s_RX_DMA_CHAN' % peripheral)
|
|
request = requests.get(channel)
|
|
if request is None:
|
|
continue
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' IRQ -> nvic@%d' % irq,
|
|
' DMARecieve -> dmamux1@%d' % request,
|
|
'',
|
|
]
|
|
|
|
for name, channel in sorted(defines.items()):
|
|
match = re.fullmatch(r'STM32_TIM_TIM(\d+)_UP_DMA_CHAN', name)
|
|
if match is None:
|
|
continue
|
|
timer = int(match.group(1))
|
|
request = requests.get(channel)
|
|
if request is None:
|
|
continue
|
|
if timer not in family['timers']:
|
|
warnings.append('timer%d update DMA is not in the current MCU base' % timer)
|
|
continue
|
|
lines += [
|
|
'timer%dUpdateDMA: Miscellaneous.AP_STM32_Timer_UpdateDMA @ sysbus 0x%08X' %
|
|
(timer, alloc()),
|
|
' timer: timer%d' % timer,
|
|
' UpdateDMA -> dmamux1@%d' % request,
|
|
'',
|
|
]
|
|
return lines
|
|
|
|
|
|
def _dmamux_dma_wiring(requests, defines, family, alloc, warnings):
|
|
lines = []
|
|
|
|
for peripheral, model in family.get('adcs', {}).items():
|
|
channel = defines.get('STM32_ADC_%s_DMA_CHAN' % peripheral)
|
|
request = requests.get(channel)
|
|
if request is None:
|
|
continue
|
|
lines += [
|
|
'%s:' % model,
|
|
' DMARequest -> dmamux1@%d' % request,
|
|
'',
|
|
]
|
|
|
|
for peripheral in sorted(family['i2cs']):
|
|
if 'STM32_I2C_%s_DMA_CHANNEL' % peripheral not in defines:
|
|
continue
|
|
rx_request = requests.get('STM32_DMAMUX1_%s_RX' % peripheral)
|
|
tx_request = requests.get('STM32_DMAMUX1_%s_TX' % peripheral)
|
|
if rx_request is None or tx_request is None:
|
|
warnings.append('cannot resolve %s DMAMUX requests' % peripheral)
|
|
continue
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' RxDmaRequest -> dmamux1@%d' % rx_request,
|
|
' TxDmaRequest -> dmamux1@%d' % tx_request,
|
|
'',
|
|
]
|
|
|
|
for peripheral, irq in family['uart_irq'].items():
|
|
channel = defines.get('STM32_UART_%s_RX_DMA_CHAN' % peripheral)
|
|
request = requests.get(channel)
|
|
if request is None:
|
|
continue
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' IRQ -> nvic@%d' % irq,
|
|
' ReceiveDmaRequest -> dmamux1@%d' % request,
|
|
'',
|
|
'%sIdle: Miscellaneous.AP_STM32F7_USART_Idle @ sysbus 0x%08X' %
|
|
(peripheral.lower(), alloc()),
|
|
' uart: %s' % peripheral.lower(),
|
|
'',
|
|
]
|
|
|
|
for peripheral, irq in family['spi_irq'].items():
|
|
channel = defines.get('STM32_SPI_%s_RX_DMA_CHAN' % peripheral)
|
|
request = requests.get(channel)
|
|
if request is None:
|
|
continue
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' IRQ -> nvic@%d' % irq,
|
|
' DMARecieve -> dmamux1@%d' % request,
|
|
'',
|
|
]
|
|
|
|
return lines
|
|
|
|
|
|
def _g474_dma_wiring(root, defines, family, alloc, warnings):
|
|
return _dmamux_dma_wiring(
|
|
_g474_dmamux_requests(root), defines, family, alloc, warnings)
|
|
|
|
|
|
def _l4plus_dma_wiring(root, defines, family, alloc, warnings):
|
|
return _dmamux_dma_wiring(
|
|
_l4plus_dmamux_requests(root), defines, family, alloc, warnings)
|
|
|
|
|
|
def _l4_dma_wiring(defines, family, alloc, warnings):
|
|
lines = []
|
|
|
|
for peripheral, model in family.get('adcs', {}).items():
|
|
value = defines.get('STM32_ADC_%s_DMA_STREAM' % peripheral)
|
|
stream = _dma_stream(value) if value else None
|
|
if stream is None:
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'%s:' % model,
|
|
' DMARequest -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
]
|
|
|
|
for peripheral in sorted(family['i2cs']):
|
|
rx_value = defines.get('STM32_I2C_%s_RX_DMA_STREAM' % peripheral)
|
|
tx_value = defines.get('STM32_I2C_%s_TX_DMA_STREAM' % peripheral)
|
|
rx_stream = _dma_stream(rx_value) if rx_value else None
|
|
tx_stream = _dma_stream(tx_value) if tx_value else None
|
|
if rx_stream is None or tx_stream is None:
|
|
continue
|
|
rx_dma, rx_channel = rx_stream
|
|
tx_dma, tx_channel = tx_stream
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' RxDmaRequest -> dma%d@%d' % (rx_dma, rx_channel),
|
|
' TxDmaRequest -> dma%d@%d' % (tx_dma, tx_channel),
|
|
'',
|
|
]
|
|
|
|
for peripheral in family['uart_irq']:
|
|
value = defines.get('STM32_UART_%s_RX_DMA_STREAM' % peripheral)
|
|
stream = _dma_stream(value) if value else None
|
|
if stream is None:
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' ReceiveDmaRequest -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
'%sIdle: Miscellaneous.AP_STM32F7_USART_Idle @ sysbus 0x%08X' %
|
|
(peripheral.lower(), alloc()),
|
|
' uart: %s' % peripheral.lower(),
|
|
'',
|
|
]
|
|
|
|
for peripheral in family['spi_irq']:
|
|
value = defines.get('STM32_SPI_%s_RX_DMA_STREAM' % peripheral)
|
|
stream = _dma_stream(value) if value else None
|
|
if stream is None:
|
|
continue
|
|
dma, channel = stream
|
|
lines += [
|
|
'%s:' % peripheral.lower(),
|
|
' DMARecieve -> dma%d@%d' % (dma, channel),
|
|
'',
|
|
]
|
|
|
|
return lines
|
|
|
|
|
|
def _pin_name(pin, fallback):
|
|
if pin is None:
|
|
return fallback
|
|
return '%s/%s' % (pin.portpin, pin.label)
|
|
|
|
|
|
def _sigrok_match_name(name):
|
|
return re.sub(r'USART(?=\d)', 'UART', name.upper())
|
|
|
|
|
|
def _select_sigrok_signals(signals, patterns):
|
|
if patterns is None:
|
|
return list(range(len(signals)))
|
|
selected = []
|
|
for index, signal in enumerate(signals):
|
|
aliases = [_sigrok_match_name(alias) for alias in signal['aliases']]
|
|
if any(fnmatch.fnmatchcase(alias, _sigrok_match_name(pattern))
|
|
for pattern in patterns for alias in aliases):
|
|
selected.append(index)
|
|
if not selected:
|
|
raise ValueError(
|
|
'sigrok channel patterns matched no channels (available: %s)' %
|
|
', '.join(signal['name'] for signal in signals))
|
|
return selected
|
|
|
|
|
|
def _sigrok_signal(pin, fallback, *aliases):
|
|
name = _pin_name(pin, fallback)
|
|
names = {name, fallback}
|
|
names.update(aliases)
|
|
if pin is not None:
|
|
names.update((pin.portpin, pin.label))
|
|
return {'name': name, 'aliases': names}
|
|
|
|
|
|
def _sigrok_gpio_signal(gpio, pin):
|
|
signal = _sigrok_signal(pin, pin.label, 'GPIO%u' % gpio)
|
|
signal['name'] += '/GPIO%u' % gpio
|
|
return signal
|
|
|
|
|
|
def _spi_frequency(expression):
|
|
units = {'GHZ': 1000000000, 'MHZ': 1000000, 'KHZ': 1000, 'HZ': 1}
|
|
cleaned = expression.upper()
|
|
for unit, multiplier in units.items():
|
|
cleaned = re.sub(r'\b%s\b' % unit, str(multiplier), cleaned)
|
|
frequency = round(_constant_number(cleaned))
|
|
if frequency <= 0:
|
|
raise ValueError('invalid SPI frequency %s' % expression)
|
|
return frequency
|
|
|
|
|
|
def _sigrok_spi_capture(app, family):
|
|
bus = next((entry for entry in app.spi_list
|
|
if entry in family['spis']), None)
|
|
if bus is None:
|
|
raise ValueError('sigrok capture needs a supported SPI bus')
|
|
|
|
pins = app.bytype.get(bus, [])
|
|
|
|
def signal(suffix):
|
|
pin = next((entry for entry in pins
|
|
if entry.label.endswith(suffix)), None)
|
|
if pin is None:
|
|
raise ValueError('cannot resolve %s%s pin' % (bus, suffix))
|
|
return pin
|
|
|
|
devices = [device for device in app.spidev if device[1] == bus]
|
|
mode = 0
|
|
frequency = 1000000
|
|
if devices:
|
|
match = re.fullmatch(r'MODE([0-3])', devices[0][4].upper())
|
|
if match is not None:
|
|
mode = int(match.group(1))
|
|
frequency = _spi_frequency(devices[0][5])
|
|
|
|
chip_selects = []
|
|
seen = set()
|
|
for device in devices:
|
|
pin = app.bylabel.get(device[3])
|
|
if pin is None or (pin.port, pin.pin) in seen:
|
|
continue
|
|
seen.add((pin.port, pin.pin))
|
|
chip_selects.append(pin)
|
|
|
|
return {
|
|
'bus': bus,
|
|
'clock': signal('_SCK'),
|
|
'mosi': signal('_MOSI'),
|
|
'miso': signal('_MISO'),
|
|
'chip_selects': chip_selects,
|
|
'mode': mode,
|
|
'frequency': frequency,
|
|
}
|
|
|
|
|
|
def _platform(root, board, app, outdir, fram_path, is_periph, warnings,
|
|
sigrok=False, num_imus=None, imu_names=None, synthetic_iomcu=True,
|
|
attachments=None):
|
|
family = FAMILIES[app.mcu_type]
|
|
base = root / 'Tools' / 'renode' / 'platforms' / family['base']
|
|
lines = [
|
|
'// GENERATED from libraries/AP_HAL_ChibiOS/hwdef/%s/hwdef.dat.' % board,
|
|
'// Edit the hwdef or Tools/renode/gen_board.py, not this file.',
|
|
'// Primary application RAM is 0x%08X (%u KiB).' %
|
|
(app.get_ram_map()[0][0], app.get_ram_map()[0][1]),
|
|
'',
|
|
'using "%s"' % base,
|
|
'',
|
|
]
|
|
if family['name'] in (
|
|
'f103', 'f105', 'f303', 'f405', 'f407', 'f427', 'f767',
|
|
'h743', 'h757', 'l4', 'g474'):
|
|
lines += [
|
|
'rcc:',
|
|
' hseFrequency: %u' % app.get_config(
|
|
'OSCILLATOR_HZ', type=int),
|
|
'',
|
|
]
|
|
hwdef_h = outdir / 'hwdef' / 'hwdef.h'
|
|
defines = _defines(hwdef_h)
|
|
defaults = _default_parameters(
|
|
outdir / 'hwdef' / 'processed_defaults.parm')
|
|
sigrok_capture = _sigrok_spi_capture(app, family) if sigrok else None
|
|
gpio_pins = _hwdef_gpios(app)
|
|
address = 0x60000010
|
|
lines += [
|
|
'physics: Miscellaneous.AP_Physics @ sysbus 0x%08X' % address,
|
|
'',
|
|
]
|
|
address += 0x100
|
|
if sigrok_capture is not None:
|
|
lines += [
|
|
'sigrok: Miscellaneous.AP_Sigrok @ sysbus 0x%08X' % address,
|
|
'',
|
|
]
|
|
address += 0x100
|
|
if gpio_pins:
|
|
lines += [
|
|
'gpioStimulus: Miscellaneous.AP_GPIOStimulus @ sysbus 0x%08X' %
|
|
address,
|
|
]
|
|
for gpio, pin in gpio_pins:
|
|
lines.append(' %u -> gpioPort%s@%u' %
|
|
(gpio, pin.port, pin.pin))
|
|
lines.append('')
|
|
address += 4
|
|
sensor_lines, chip_selects, has_fram, emulated_imus, spi_sdcard = _sensor_devices(
|
|
app, family, defines, fram_path, warnings,
|
|
sigrok_capture['bus'] if sigrok_capture else None, num_imus, imu_names)
|
|
lines += sensor_lines
|
|
iomcu_lines, iomcu_uart, iomcu_firmware = _iomcu_device(
|
|
root, app, family, defines, address, warnings, synthetic_iomcu)
|
|
lines += iomcu_lines
|
|
if iomcu_uart is not None:
|
|
address += 4
|
|
|
|
def alloc(size=4):
|
|
nonlocal address
|
|
value = address
|
|
address += size
|
|
return value
|
|
|
|
lines += _timer_actuator_devices(
|
|
app, family, iomcu_uart, alloc, warnings)
|
|
|
|
resolved_attachments = _resolve_attachments(app, family, attachments)
|
|
|
|
def periph_uart(port_name, port_value):
|
|
index = _constant_integer(port_value)
|
|
if index < 0:
|
|
return None
|
|
serial_order = app.get_config(
|
|
'SERIAL_ORDER', required=False, aslist=True) or []
|
|
if not 0 <= index < len(serial_order):
|
|
warnings.append('%s selects missing SERIAL%d' % (port_name, index))
|
|
return None
|
|
uart = serial_order[index]
|
|
if uart not in family['uarts']:
|
|
warnings.append('%s selects unsupported %s' % (port_name, uart))
|
|
return None
|
|
return uart
|
|
|
|
gps_uart = None
|
|
if is_periph and _define_enabled(defines, 'AP_PERIPH_GPS_ENABLED'):
|
|
if 'GPS_PORT' in defaults:
|
|
gps_uart = periph_uart('GPS_PORT', defaults['GPS_PORT'])
|
|
elif 'HAL_PERIPH_GPS_PORT_DEFAULT' in defines:
|
|
gps_uart = periph_uart(
|
|
'GPS_PORT', defines['HAL_PERIPH_GPS_PORT_DEFAULT'])
|
|
else:
|
|
serial_order = app.get_config(
|
|
'SERIAL_ORDER', required=False, aslist=True) or []
|
|
gps_uart = next((uart for uart in reversed(serial_order)
|
|
if uart in family['uarts']), None)
|
|
if gps_uart is not None:
|
|
lines += [
|
|
'gps: Sensors.AP_UBlox @ sysbus 0x%08X' % alloc(),
|
|
'',
|
|
]
|
|
|
|
airspeed_bus = None
|
|
if is_periph and _define_enabled(defines, 'AP_PERIPH_AIRSPEED_ENABLED'):
|
|
i2c_order = app.get_config(
|
|
'I2C_ORDER', required=False, aslist=True) or []
|
|
airspeed_index = _constant_integer(defaults.get(
|
|
'ARSPD_BUS', defines.get('HAL_AIRSPEED_BUS_DEFAULT', '0')))
|
|
airspeed_type = _constant_integer(defaults.get(
|
|
'ARSPD_TYPE', defines.get('HAL_AIRSPEED_TYPE_DEFAULT', '1')))
|
|
if not 0 <= airspeed_index < len(i2c_order):
|
|
warnings.append('airspeed bus %d is not present in I2C_ORDER' %
|
|
airspeed_index)
|
|
elif i2c_order[airspeed_index] not in family['i2cs']:
|
|
warnings.append('airspeed bus selects unsupported %s' %
|
|
i2c_order[airspeed_index])
|
|
elif airspeed_type == 1:
|
|
airspeed_bus = i2c_order[airspeed_index]
|
|
lines += [
|
|
'airspeed: Sensors.AP_Airspeed @ %s 0x28' %
|
|
airspeed_bus.lower(),
|
|
'',
|
|
]
|
|
elif airspeed_type in (7, 9, 10, 11, 12):
|
|
airspeed_bus = i2c_order[airspeed_index]
|
|
dlvr_range = {7: 5, 9: 10, 10: 20, 11: 30, 12: 60}[
|
|
airspeed_type]
|
|
lines += [
|
|
'airspeed: Sensors.AP_Airspeed @ %s 0x28' %
|
|
airspeed_bus.lower(),
|
|
' dlvrRangeInH2O: %u' % dlvr_range,
|
|
'',
|
|
]
|
|
elif airspeed_type == 15:
|
|
airspeed_bus = i2c_order[airspeed_index]
|
|
lines += [
|
|
'airspeed: Sensors.AP_Airspeed_ASP5033 @ %s 0x6C' %
|
|
airspeed_bus.lower(),
|
|
'',
|
|
]
|
|
elif airspeed_type in (17, 18, 19):
|
|
airspeed_bus = i2c_order[airspeed_index]
|
|
lines += [
|
|
'airspeed: Sensors.AP_Airspeed_AUAV @ %s 0x26' %
|
|
airspeed_bus.lower(),
|
|
'',
|
|
]
|
|
elif airspeed_type == 0:
|
|
pass
|
|
else:
|
|
warnings.append('unmodelled airspeed type %d' % airspeed_type)
|
|
|
|
rangefinder_uart = None
|
|
rangefinder_type = None
|
|
if is_periph and _define_enabled(defines, 'AP_PERIPH_RANGEFINDER_ENABLED'):
|
|
rangefinder_uart = periph_uart(
|
|
'RNGFND_PORT', defaults.get(
|
|
'RNGFND_PORT', defines.get(
|
|
'AP_PERIPH_RANGEFINDER_PORT_DEFAULT', '3')))
|
|
rangefinder_type = _constant_integer(
|
|
defaults.get('RNGFND1_TYPE', '0'))
|
|
if rangefinder_uart is not None:
|
|
model = ('Sensors.AP_LightWare' if rangefinder_type == 8 else
|
|
'Sensors.AP_Benewake')
|
|
lines += [
|
|
'rangefinder: %s @ sysbus 0x%08X' % (model, alloc()),
|
|
'',
|
|
]
|
|
|
|
for attachment in resolved_attachments:
|
|
device = attachment['device']
|
|
port = attachment['port']
|
|
if port['bus'] != 'i2c' and device.get('model') is None:
|
|
continue
|
|
if port['bus'] == 'uart':
|
|
declaration = '%s: %s @ sysbus 0x%08X' % (
|
|
attachment['id'], device['model'], alloc())
|
|
else:
|
|
endpoints = i2c_endpoints(device)
|
|
for index, endpoint in enumerate(endpoints):
|
|
endpoint_id = attachment['id']
|
|
if len(endpoints) > 1:
|
|
endpoint_id += 'Part%u' % index
|
|
declaration = '%s: %s @ %s 0x%02X' % (
|
|
endpoint_id, endpoint['model'],
|
|
port['peripheral'].lower(), endpoint['address'])
|
|
physics = device.get('physics')
|
|
if physics is not None:
|
|
declaration += '\n %s: %u' % (
|
|
physics['property'], attachment['physics_index'])
|
|
configuration = attachment['configuration']
|
|
if (device['category'] == 'Compass' and
|
|
'orientation' in configuration):
|
|
declaration += '\n Rotation: %u' % configuration['orientation']
|
|
lines += [declaration, '']
|
|
continue
|
|
physics = device.get('physics')
|
|
if physics is not None:
|
|
declaration += '\n %s: %u' % (
|
|
physics['property'], attachment['physics_index'])
|
|
configuration = attachment['configuration']
|
|
if (device['category'] == 'Compass' and
|
|
'orientation' in configuration):
|
|
declaration += '\n Rotation: %u' % configuration['orientation']
|
|
lines += [declaration, '']
|
|
|
|
adcs = family.get('adcs', {})
|
|
battery_samples = []
|
|
if is_periph and _define_enabled(defines, 'AP_PERIPH_BATTERY_ENABLED'):
|
|
analog_pins = _analog_pins(hwdef_h)
|
|
battery_inputs = []
|
|
adc_disabled = any(
|
|
name in defines and not _define_enabled(defines, name)
|
|
for name in ('HAL_USE_ADC', 'STM32_ADC_USE_ADC1'))
|
|
if not adc_disabled:
|
|
max_instances = _constant_integer(defines.get(
|
|
'AP_BATT_MONITOR_MAX_INSTANCES', '1'))
|
|
if any(name.startswith('HAL_BATT2_') for name in defines):
|
|
max_instances = max(2, max_instances)
|
|
for index in range(max_instances):
|
|
instance = '' if index == 0 else str(index + 1)
|
|
parameter = 'BATT%s_' % instance
|
|
compile_default = 'HAL_BATT%s_' % instance
|
|
battery_type = _constant_integer(defaults.get(
|
|
parameter + 'MONITOR', defines.get(
|
|
compile_default + 'MONITOR_DEFAULT', '0')))
|
|
for kind, types, pin_suffix, scale_suffix, fallback_pin, \
|
|
fallback_scale, desired in (
|
|
('voltage', (3, 4, 25), 'VOLT_PIN', 'VOLT_SCALE',
|
|
'4', '10.1', 24.0),
|
|
('current', (4, 31), 'CURR_PIN', 'CURR_SCALE',
|
|
'3', '17.0', 10.0)):
|
|
runtime_pin = parameter + pin_suffix
|
|
compile_pin = compile_default + pin_suffix
|
|
enabled = (battery_type in types or runtime_pin in defaults or
|
|
compile_pin in defines)
|
|
if not enabled:
|
|
continue
|
|
pin_value = defaults.get(
|
|
runtime_pin, defines.get(compile_pin, fallback_pin))
|
|
runtime_scale = parameter + (
|
|
'VOLT_MULT' if kind == 'voltage' else 'AMP_PERVLT')
|
|
compile_scale = compile_default + scale_suffix
|
|
scale_value = defaults.get(
|
|
runtime_scale, defines.get(compile_scale, fallback_scale))
|
|
offset = 0.0
|
|
if kind == 'current':
|
|
offset = _constant_number(defaults.get(
|
|
parameter + 'AMP_OFFSET', defines.get(
|
|
'AP_BATT%s_CURR_AMP_OFFSET_DEFAULT' % instance,
|
|
'0')))
|
|
battery_inputs.append((
|
|
kind + instance, pin_value, scale_value, desired, offset))
|
|
|
|
if battery_inputs and 'ADC1' not in adcs:
|
|
warnings.append('battery ADC is not modelled for %s' % app.mcu_type)
|
|
elif battery_inputs:
|
|
for name, pin_value, scale_value, desired, offset in battery_inputs:
|
|
logical_pin = _constant_integer(pin_value)
|
|
if logical_pin < 0:
|
|
continue
|
|
analog = analog_pins.get(logical_pin)
|
|
if analog is None:
|
|
warnings.append('battery %s selects unmapped analog pin %d' %
|
|
(name, logical_pin))
|
|
continue
|
|
channel, volts_per_count = analog
|
|
multiplier = _constant_number(scale_value)
|
|
sensor_voltage = desired / multiplier + offset
|
|
if name.startswith('voltage'):
|
|
sensor_voltage = min(sensor_voltage, 2.5)
|
|
raw = round(sensor_voltage / volts_per_count)
|
|
if not 0 <= raw <= 4095:
|
|
warnings.append('simulated battery %s is outside ADC range' %
|
|
name)
|
|
raw = min(4095, max(0, raw))
|
|
raw <<= family.get('adc_sample_shift', 0)
|
|
battery_samples.append((name, adcs['ADC1'], channel, raw))
|
|
|
|
board_voltage_pin = defines.get('ANALOG_VCC_5V_PIN')
|
|
if board_voltage_pin is not None and 'ADC1' in adcs:
|
|
logical_pin = _constant_integer(board_voltage_pin)
|
|
analog = _analog_pins(hwdef_h).get(logical_pin)
|
|
if analog is None:
|
|
warnings.append('board voltage selects unmapped analog pin %d' %
|
|
logical_pin)
|
|
else:
|
|
channel, volts_per_count = analog
|
|
raw = round(5.0 / volts_per_count)
|
|
if not 0 <= raw <= 4095:
|
|
warnings.append('simulated board voltage is outside ADC range')
|
|
raw = min(4095, max(0, raw))
|
|
raw <<= family.get('adc_sample_shift', 0)
|
|
battery_samples.append(
|
|
('board voltage', adcs['ADC1'], channel, raw))
|
|
|
|
# ChibiOS can initialize ADC1 for board-specific analog consumers even
|
|
# when none of the generic battery parameters select an input. Keep the
|
|
# converter present so ADC startup and DMA cannot spin on an SVD stub.
|
|
if 'ADC1' in adcs and not family.get('adc_existing'):
|
|
lines += [
|
|
'%s: Analog.AP_STM32_ADC @ sysbus 0x%08X' %
|
|
(adcs['ADC1'], family.get('adc_base', 0x40012000)),
|
|
' IRQ -> nvic@%d' % family.get('adc_irq', 18),
|
|
'',
|
|
]
|
|
|
|
can_buses = []
|
|
for bus, peripheral in _can_buses(app, family):
|
|
can_buses.append((bus, peripheral))
|
|
lines += [
|
|
'%sMcast: CAN.AP_CANMcast @ sysbus 0x%08X' %
|
|
(bus.lower(), alloc(0x100)),
|
|
'',
|
|
]
|
|
|
|
has_ethernet = family['name'] in ('h743', 'h757') and 'ETH1' in app.bytype
|
|
if has_ethernet:
|
|
phy_address = _constant_number(
|
|
defines.get('BOARD_PHY_ADDRESS', '0'))
|
|
lines += [
|
|
'ethernet: Network.SynopsysDWCEthernetQualityOfService @ {',
|
|
' sysbus 0x40028000;',
|
|
' sysbus new Bus.BusMultiRegistration { address: 0x40028C00; '
|
|
'size: 0x200; region: "mtl" };',
|
|
' sysbus new Bus.BusMultiRegistration { address: 0x40029000; '
|
|
'size: 0x200; region: "dma" }',
|
|
' }',
|
|
' systemClockFrequency: 50000000',
|
|
' dmaBusWidth: BusWidth.Bits32',
|
|
' -> nvic@61',
|
|
'',
|
|
'ethernetFixup: Miscellaneous.AP_STM32H7_Ethernet @ sysbus 0x%08X' %
|
|
alloc(4),
|
|
' ethernet: ethernet',
|
|
'',
|
|
'ethernetPhy: Network.EthernetPhysicalLayer @ ethernet %u' %
|
|
phy_address,
|
|
' BasicControl: 0x3100',
|
|
' BasicStatus: 0x782D',
|
|
' Id1: 0x0007',
|
|
' Id2: 0xC130',
|
|
' AutoNegotiationAdvertisement: 0x01E1',
|
|
' AutoNegotiationLinkPartnerBasePageAbility: 0x0001',
|
|
' AutoNegotiationExpansion: 0x0064',
|
|
' AutoNegotiationNextPageTransmit: 0x2001',
|
|
'',
|
|
]
|
|
|
|
if family['name'] not in ('f103', 'f105', 'f303', 'g474', 'l4'):
|
|
lines += [
|
|
'dma1Fix: Miscellaneous.AP_STM32DMA_Fixup @ sysbus 0x%08X' % alloc(),
|
|
' dma: dma1',
|
|
'',
|
|
'dma2Fix: Miscellaneous.AP_STM32DMA_Fixup @ sysbus 0x%08X' % alloc(),
|
|
' dma: dma2',
|
|
'',
|
|
]
|
|
if family['name'] in ('f405', 'f407', 'f427'):
|
|
lines += _f405_dma_wiring(defines, family, alloc, warnings)
|
|
elif family['name'] == 'f767':
|
|
lines += _f767_dma_wiring(defines, family, alloc, warnings)
|
|
elif family['name'] == 'g474':
|
|
lines += _g474_dma_wiring(root, defines, family, alloc, warnings)
|
|
elif family['name'] == 'l4' and family.get('dmamux'):
|
|
lines += _l4plus_dma_wiring(root, defines, family, alloc, warnings)
|
|
elif family['name'] in ('f103', 'f105', 'f303', 'l4'):
|
|
lines += _l4_dma_wiring(defines, family, alloc, warnings)
|
|
elif family['name'] in ('h743', 'h757'):
|
|
lines += _h743_dma_wiring(root, defines, family, alloc, warnings)
|
|
if family['name'] in (
|
|
'f303', 'f405', 'f407', 'f427', 'f767', 'g474', 'h743', 'h757', 'l4'):
|
|
serial_uarts = app.get_config('SERIAL_ORDER', required=False, aslist=True) or []
|
|
for peripheral in dict.fromkeys(serial_uarts):
|
|
if peripheral not in family['uarts']:
|
|
continue
|
|
lines += [
|
|
'%sHost: Miscellaneous.AP_UARTPacer @ sysbus 0x%08X' %
|
|
(peripheral.lower(), alloc()),
|
|
' uart: %s' % peripheral.lower(),
|
|
'',
|
|
]
|
|
sigrok_pins = {}
|
|
if sigrok_capture is not None:
|
|
sigrok_pins = {
|
|
(pin.port, pin.pin): channel
|
|
for channel, pin in enumerate(
|
|
sigrok_capture['chip_selects'], start=5)
|
|
}
|
|
used_pins = {
|
|
(pin.port, pin.pin) for pin in (
|
|
[sigrok_capture['clock'], sigrok_capture['mosi'],
|
|
sigrok_capture['miso']] +
|
|
sigrok_capture['chip_selects'])
|
|
}
|
|
sigrok_capture['gpio_pins'] = [
|
|
(gpio, pin) for gpio, pin in gpio_pins
|
|
if (pin.port, pin.pin) not in used_pins
|
|
]
|
|
first_gpio_channel = 5 + len(sigrok_capture['chip_selects'])
|
|
sigrok_pins.update({
|
|
(pin.port, pin.pin): channel
|
|
for channel, (_, pin) in enumerate(
|
|
sigrok_capture['gpio_pins'], start=first_gpio_channel)
|
|
})
|
|
lines += _gpio_routes(
|
|
family['name'], chip_selects, sigrok_pins, gpio_pins)
|
|
return ('\n'.join(lines).rstrip() + '\n', has_fram, iomcu_uart,
|
|
iomcu_firmware,
|
|
can_buses, has_ethernet, gps_uart, airspeed_bus, battery_samples,
|
|
rangefinder_uart, rangefinder_type, gpio_pins, sigrok_capture,
|
|
emulated_imus, spi_sdcard, resolved_attachments)
|
|
|
|
|
|
def _serial_device(app, family, serial_index, excluded=None):
|
|
order = app.get_config('SERIAL_ORDER', required=False, aslist=True) or []
|
|
excluded = set(excluded or ())
|
|
requested_index = serial_index
|
|
if serial_index is not None:
|
|
if not 0 <= serial_index < len(order):
|
|
raise ValueError('SERIAL%d is outside SERIAL_ORDER' % serial_index)
|
|
device = order[serial_index]
|
|
else:
|
|
serial_index = next((index for index, entry in enumerate(order)
|
|
if (entry in family['uarts'] and
|
|
entry not in excluded)), None)
|
|
device = order[serial_index] if serial_index is not None else None
|
|
if device is None and requested_index is None:
|
|
return None, None
|
|
if device is None or device in {'EMPTY', 'OTG1', 'OTG2'}:
|
|
raise ValueError('selected serial port is not a UART')
|
|
if device not in family['uarts']:
|
|
raise ValueError('%s is not present in the current MCU base' % device)
|
|
return serial_index, device
|
|
|
|
|
|
def _script(root, board, app, bootloader, platform, serial_index, uart_port,
|
|
iomcu_uart, can_buses, has_ethernet, gps_uart, airspeed_bus,
|
|
battery_samples, rangefinder_uart, rangefinder_type, warnings,
|
|
gpio_pins, sigrok_capture=None, quiet_peripherals=True,
|
|
sigrok_channels=None, spi_sdcard=None, real_iomcu=False,
|
|
attachments=None, system_timer=None, uart_socket_dir=None):
|
|
family = FAMILIES[app.mcu_type]
|
|
reserve_kb = app.get_config('FLASH_RESERVE_START_KB', default=0, type=int)
|
|
boot_kb = bootloader.get_config(
|
|
'FLASH_BOOTLOADER_LOAD_KB', required=False, default=reserve_kb, type=int)
|
|
# Older F4 hwdefs omit FLASH_RESERVE_START_KB from the application file.
|
|
# In that case the bootloader load offset is the authoritative application
|
|
# base, as it is in the generated ChibiOS build configuration.
|
|
effective_reserve_kb = boot_kb if reserve_kb == 0 else reserve_kb
|
|
app_base = 0x08000000 + effective_reserve_kb * 1024
|
|
attached_uarts = [
|
|
attachment['port']['peripheral'] for attachment in attachments or []
|
|
if attachment['port']['bus'] == 'uart'
|
|
]
|
|
serial_index, serial = _serial_device(
|
|
app, family, serial_index,
|
|
excluded=(gps_uart, rangefinder_uart, iomcu_uart, *attached_uarts))
|
|
uart_socket = (uart_socket_dir / ('APM-UDS-serial%u' % serial_index)
|
|
if uart_socket_dir is not None and serial is not None else None)
|
|
tick = app.get_config(
|
|
'STM32_ST_USE_TIMER', required=False, default=system_timer)
|
|
sd_buses = {name for name in app.bytype
|
|
if name.startswith('SDMMC') or name == 'SDIO'}
|
|
supported_sd_buses = set(family['sd_buses'])
|
|
present_sd_buses = sorted(sd_buses & supported_sd_buses)
|
|
has_sd = bool(present_sd_buses)
|
|
has_sdcard = has_sd or spi_sdcard is not None
|
|
sd_bus = present_sd_buses[0] if has_sd else None
|
|
if len(present_sd_buses) > 1:
|
|
warnings.append('multiple SDMMC buses are not supported concurrently')
|
|
for bus in sorted(sd_buses - supported_sd_buses):
|
|
warnings.append('%s is not present in the current MCU base' % bus)
|
|
common = root / 'Tools' / 'renode' / 'scripts' / family['script']
|
|
lines = [
|
|
':name: %s' % board,
|
|
':description: generated ArduPilot platform from hwdef.dat',
|
|
'',
|
|
'# GENERATED by Tools/renode/gen_board.py.',
|
|
'$repo?=@%s' % root,
|
|
'$elf?=@none',
|
|
'$platform=@%s' % platform,
|
|
'$app_base=0x%08X' % app_base,
|
|
'include $repo/Tools/renode/peripherals/common/AP_SigrokInterface.cs',
|
|
]
|
|
if uart_socket is not None:
|
|
lines.append(
|
|
'include $repo/Tools/renode/peripherals/common/AP_UnixSocketTerminal.cs')
|
|
if gpio_pins:
|
|
lines.append(
|
|
'include $repo/Tools/renode/peripherals/common/AP_GPIOStimulus.cs')
|
|
if sigrok_capture is not None:
|
|
lines.append(
|
|
'include $repo/Tools/renode/peripherals/common/AP_Sigrok.cs')
|
|
if spi_sdcard is not None:
|
|
lines.append(
|
|
'include $repo/Tools/renode/peripherals/storage/AP_SPI_SDCard.cs')
|
|
lines += ['include @%s' % common, '']
|
|
for name, adc, channel, raw in battery_samples:
|
|
lines += [
|
|
'# simulated %s input' % name,
|
|
'sysbus.%s FeedSample %u %u -1' % (adc, raw, channel),
|
|
'',
|
|
]
|
|
if serial is not None:
|
|
serial_target = serial.lower()
|
|
if family['name'] in (
|
|
'f303', 'f405', 'f407', 'f427', 'f767', 'g474', 'h743', 'h757', 'l4'):
|
|
serial_target += 'Host'
|
|
terminal_target = serial_target
|
|
if sigrok_capture is not None:
|
|
uart_pins = app.bytype.get(serial, [])
|
|
|
|
def uart_signal(suffix):
|
|
pin = next((entry for entry in uart_pins
|
|
if entry.label.endswith(suffix)), None)
|
|
return _sigrok_signal(pin, serial + suffix, serial)
|
|
|
|
spi = sigrok_capture['bus']
|
|
sigrok_signal_info = [
|
|
uart_signal('_TX'),
|
|
uart_signal('_RX'),
|
|
_sigrok_signal(sigrok_capture['clock'], spi + '_SCK', spi),
|
|
_sigrok_signal(sigrok_capture['mosi'], spi + '_MOSI', spi),
|
|
_sigrok_signal(sigrok_capture['miso'], spi + '_MISO', spi),
|
|
] + [
|
|
_sigrok_signal(pin, pin.label, spi)
|
|
for pin in sigrok_capture['chip_selects']
|
|
] + [
|
|
_sigrok_gpio_signal(gpio, pin)
|
|
for gpio, pin in sigrok_capture['gpio_pins']
|
|
]
|
|
selected_signals = _select_sigrok_signals(
|
|
sigrok_signal_info, sigrok_channels)
|
|
configured_signals = [signal['name']
|
|
for signal in sigrok_signal_info]
|
|
sigrok_signals = [configured_signals[index]
|
|
for index in selected_signals]
|
|
lines += [
|
|
'sysbus.sigrok DeviceName %s' %
|
|
json.dumps('ArduPilot %s' % board),
|
|
'sysbus.sigrok SpiMode %u' % sigrok_capture['mode'],
|
|
'sysbus.sigrok SpiFrequency %u' %
|
|
sigrok_capture['frequency'],
|
|
'sysbus.sigrok ChipSelectCount %u' %
|
|
len(sigrok_capture['chip_selects']),
|
|
'sysbus.sigrok ConfigureSignals %s' %
|
|
json.dumps('|'.join(configured_signals)),
|
|
'sysbus.sigrok SelectSignals %s' %
|
|
json.dumps(','.join(str(index)
|
|
for index in selected_signals)),
|
|
'sysbus.sigrok AttachUART sysbus.%s' % serial_target,
|
|
'',
|
|
]
|
|
terminal_target = 'sigrok'
|
|
if uart_socket is not None:
|
|
lines.append('emulation CreateUnixSocketTerminal %s "serial"' %
|
|
json.dumps(str(uart_socket)))
|
|
else:
|
|
lines.append(
|
|
'emulation CreateServerSocketTerminal %u "serial" false' %
|
|
uart_port)
|
|
lines += [
|
|
'connector Connect sysbus.%s serial' % terminal_target,
|
|
'',
|
|
]
|
|
elif sigrok_capture is not None:
|
|
raise ValueError('sigrok capture needs a selected hardware UART')
|
|
if iomcu_uart is not None and real_iomcu:
|
|
iomcu_target = iomcu_uart.lower()
|
|
iomcu_target += 'Host'
|
|
lines += [
|
|
'emulation CreateUARTHub "iomcuHub"',
|
|
'connector Connect sysbus.%s iomcuHub' % iomcu_target,
|
|
]
|
|
lines += [
|
|
'',
|
|
]
|
|
if gps_uart is not None:
|
|
gps_target = gps_uart.lower()
|
|
if family['name'] in ('h743', 'h757', 'f303', 'f767', 'g474', 'l4'):
|
|
gps_target += 'Host'
|
|
lines += [
|
|
'emulation CreateUARTHub "gpsHub"',
|
|
'connector Connect sysbus.%s gpsHub' % gps_target,
|
|
'connector Connect sysbus.gps gpsHub',
|
|
'',
|
|
]
|
|
if rangefinder_uart is not None:
|
|
rangefinder_target = rangefinder_uart.lower()
|
|
if family['name'] in ('h743', 'h757', 'f303', 'f767', 'g474', 'l4'):
|
|
rangefinder_target += 'Host'
|
|
lines += [
|
|
'emulation CreateUARTHub "rangefinderHub"',
|
|
'connector Connect sysbus.%s rangefinderHub' % rangefinder_target,
|
|
'connector Connect sysbus.rangefinder rangefinderHub',
|
|
'',
|
|
]
|
|
for attachment in attachments or []:
|
|
if attachment['port']['bus'] != 'uart':
|
|
continue
|
|
peripheral = attachment['port']['peripheral']
|
|
target = peripheral.lower()
|
|
if family['name'] in ('h743', 'h757', 'f303', 'f767', 'g474', 'l4'):
|
|
target += 'Host'
|
|
hub = '%sHub' % attachment['id']
|
|
lines += [
|
|
'emulation CreateUARTHub "%s"' % hub,
|
|
'connector Connect sysbus.%s %s' % (target, hub),
|
|
'connector Connect sysbus.%s %s' % (attachment['id'], hub),
|
|
'',
|
|
]
|
|
for bus, peripheral in can_buses:
|
|
hub = '%sHub' % bus.lower()
|
|
lines += [
|
|
'emulation CreateCANHub "%s"' % hub,
|
|
'connector Connect sysbus.%s %s' % (peripheral, hub),
|
|
'connector Connect sysbus.%sMcast %s' % (bus.lower(), hub),
|
|
'',
|
|
]
|
|
if has_ethernet:
|
|
lines += [
|
|
'sysbus.ethernet ActivePhy RMII',
|
|
'',
|
|
]
|
|
power_inputs = _power_status_inputs(app)
|
|
gpio_defaults = []
|
|
if power_inputs:
|
|
# drive the input latches directly; a Python read hook would run
|
|
# on every IDR read
|
|
gpio_defaults += ['sysbus.gpioPort%s OnGPIO %u %s' %
|
|
(pin.port, pin.pin, 'true' if level else 'false')
|
|
for pin, level in power_inputs]
|
|
|
|
for index in range(1, 5):
|
|
label = 'GPIO_CAN_I2C%d_SEL' % index
|
|
pin = app.bylabel.get(label)
|
|
if pin is not None:
|
|
# select I2C rather than CAN on the shared connector
|
|
gpio_defaults += [
|
|
'sysbus.gpioPort%s OnGPIO %u false' % (pin.port, pin.pin),
|
|
]
|
|
if gpio_defaults:
|
|
# Replace the base script's reset macro, retaining its vector table
|
|
# setup and restoring input latches cleared by a machine reset.
|
|
lines += ['# hwdef input defaults must survive firmware resets',
|
|
'macro reset', '"""',
|
|
' cpu VectorTableOffset $vector_base']
|
|
lines += [' ' + command for command in gpio_defaults]
|
|
lines += ['"""', 'runMacro $reset', '']
|
|
if has_sd:
|
|
lines += [
|
|
'$sdcard?=@none',
|
|
'machine SdCardFromFile $sdcard sysbus.%s 0x10000000 True "sdcard"' %
|
|
family['sd_buses'][sd_bus],
|
|
'',
|
|
]
|
|
elif spi_sdcard is not None:
|
|
bus, mux, address = spi_sdcard
|
|
lines += [
|
|
'$sdcard?=@none',
|
|
('machine AP_SpiSdCardFromFile $sdcard sysbus.%s.%s %u '
|
|
'0x10000000 True "sdcard"') % (bus, mux, address),
|
|
'',
|
|
]
|
|
if tick is not None and int(tick) in family['timers']:
|
|
lines += [
|
|
'sysbus SetHookBeforePeripheralWrite sysbus.timer%s '
|
|
'"if offset == 0x34 and value == 0: value = 1"' % tick,
|
|
'',
|
|
]
|
|
elif tick is not None:
|
|
warnings.append('system timer%s is not in the current MCU base' % tick)
|
|
|
|
if family['name'] in ('h743', 'h757'):
|
|
for spi in app.spi_list:
|
|
if spi in family['spis']:
|
|
lines.append('sysbus SetHookBeforePeripheralWrite sysbus.%s '
|
|
'"if offset == 0x0C: value |= 0x400000"' % spi.lower())
|
|
lines.append('')
|
|
|
|
if quiet_peripherals:
|
|
# Renode formats and queues every entry at or above the lowest level
|
|
# configured for any source, and the STM32 models emit tagged-register
|
|
# warnings on most SPI and DMA accesses. Only a global error-only
|
|
# level keeps that work off the emulation thread.
|
|
lines += ['# errors only: peripheral warnings cost emulation throughput',
|
|
'logLevel 3']
|
|
lines.append('')
|
|
return '\n'.join(lines), {
|
|
'app_base': app_base,
|
|
'bootloader_load_base': 0x08000000 + boot_kb * 1024,
|
|
'has_sdcard': has_sdcard,
|
|
'serial_index': serial_index,
|
|
'serial': serial,
|
|
'iomcu_uart': iomcu_uart,
|
|
'family': family['name'],
|
|
'mcu_type': app.mcu_type,
|
|
'uid_address': family['uid_address'],
|
|
'can_buses': [bus for bus, _ in can_buses],
|
|
'has_ethernet': has_ethernet,
|
|
'primary_ram_base': app.get_ram_map()[0][0],
|
|
'airspeed_bus': airspeed_bus,
|
|
'battery_channels': {
|
|
name: channel for name, _, channel, _ in battery_samples
|
|
if name.startswith(('voltage', 'current'))
|
|
},
|
|
'rangefinder_uart': rangefinder_uart,
|
|
'rangefinder_type': rangefinder_type,
|
|
'sigrok_signals': sigrok_signals if sigrok_capture is not None else [],
|
|
'sigrok_spi': (sigrok_capture['bus']
|
|
if sigrok_capture is not None else None),
|
|
'sigrok_spi_frequency': (sigrok_capture['frequency']
|
|
if sigrok_capture is not None else None),
|
|
'uart_socket': uart_socket,
|
|
}
|
|
|
|
|
|
def generate(root, board, outdir, serial_index=None, uart_port=5762,
|
|
state_dir=None, quiet_peripherals=True, sigrok=False,
|
|
sigrok_channels=None, num_imus=None, imu_names=None, real_iomcu=False,
|
|
attachments=None, uds=False):
|
|
'''Generate the board REPL/RESC and return paths plus run metadata.'''
|
|
root = root.resolve()
|
|
outdir = outdir.resolve()
|
|
state_dir = (Path(state_dir) if state_dir is not None else
|
|
root / 'renode' / board).resolve()
|
|
if num_imus is not None and num_imus < 0:
|
|
raise ValueError('number of IMUs cannot be negative')
|
|
boards = supported_boards(root)
|
|
if board not in boards:
|
|
raise ValueError('%s is not a supported Renode target' % board)
|
|
board_dir = _hwdef_root(root) / board
|
|
is_periph = _resolved_env(board_dir / 'hwdef.dat', 'AP_PERIPH') == '1'
|
|
outdir.mkdir(parents=True, exist_ok=True)
|
|
app = _compile_hwdef(root, board_dir / 'hwdef.dat', outdir / 'hwdef')
|
|
app_defines = _defines(outdir / 'hwdef' / 'hwdef.h')
|
|
bootloader_hwdef = board_dir / 'hwdef-bl.dat'
|
|
bootloader = (_compile_hwdef(
|
|
root, bootloader_hwdef, outdir / 'hwdef-bl', bootloader=True)
|
|
if bootloader_hwdef.is_file() else app)
|
|
if app.mcu_type != boards[board] or bootloader.mcu_type != app.mcu_type:
|
|
raise ValueError('application and bootloader MCU definitions disagree')
|
|
|
|
warnings = []
|
|
repl = outdir / ('%s.repl' % board)
|
|
resc = outdir / ('%s.resc' % board)
|
|
(platform, has_fram, iomcu_uart, iomcu_firmware, can_buses, has_ethernet, gps_uart,
|
|
airspeed_bus, battery_samples, rangefinder_uart,
|
|
rangefinder_type, gpio_pins, sigrok_capture, emulated_imus,
|
|
spi_sdcard, resolved_attachments) = _platform(
|
|
root, board, app, outdir, state_dir / 'fram.img', is_periph, warnings,
|
|
sigrok, num_imus, imu_names, synthetic_iomcu=not real_iomcu,
|
|
attachments=attachments)
|
|
repl.write_text(platform)
|
|
script, metadata = _script(
|
|
root, board, app, bootloader, repl, serial_index, uart_port,
|
|
iomcu_uart, can_buses, has_ethernet, gps_uart, airspeed_bus,
|
|
battery_samples, rangefinder_uart, rangefinder_type, warnings,
|
|
gpio_pins, sigrok_capture, quiet_peripherals, sigrok_channels,
|
|
spi_sdcard, real_iomcu, resolved_attachments,
|
|
app_defines.get('STM32_ST_USE_TIMER'), state_dir if uds else None)
|
|
resc.write_text(script)
|
|
metadata.update({
|
|
'repl': repl,
|
|
'resc': resc,
|
|
'warnings': warnings,
|
|
'hwdef_h': outdir / 'hwdef' / 'hwdef.h',
|
|
'has_fram': has_fram,
|
|
'fram_size': 32 * 1024 if has_fram else 0,
|
|
'gps_uart': gps_uart,
|
|
'iomcu_firmware': iomcu_firmware,
|
|
'num_imus': emulated_imus,
|
|
'gpio_pins': {
|
|
gpio: pin.portpin for gpio, pin in gpio_pins
|
|
},
|
|
'is_periph': is_periph,
|
|
'attachments': resolved_attachments,
|
|
})
|
|
return metadata
|
|
|
|
|
|
def main():
|
|
root = Path(__file__).resolve().parents[2]
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('board', nargs='?')
|
|
parser.add_argument('--list', action='store_true')
|
|
parser.add_argument('--outdir')
|
|
parser.add_argument('--state-dir',
|
|
help='persistent state directory (default: renode/<board>)')
|
|
parser.add_argument('--serial', type=int, help='SERIAL_ORDER index to expose')
|
|
parser.add_argument('--uart-port', type=int, default=5762)
|
|
parser.add_argument('--unix-domain-socket', '--uds', dest='uds',
|
|
action='store_true',
|
|
help='expose the selected UART at APM-UDS-serialN '
|
|
'under the state directory')
|
|
parser.add_argument('--sigrok', action='store_true')
|
|
parser.add_argument('--sigrok-channels',
|
|
help='comma-separated sigrok channel wildcard patterns')
|
|
args = parser.parse_args()
|
|
if not 1 <= args.uart_port <= 65535:
|
|
parser.error('--uart-port must be between 1 and 65535')
|
|
boards = supported_boards(root)
|
|
if args.list:
|
|
print('\n'.join(boards))
|
|
return 0
|
|
if args.board is None:
|
|
parser.error('a board is required unless --list is used')
|
|
outdir = (Path(args.outdir) if args.outdir else
|
|
root / 'build' / args.board / 'renode' / 'generated')
|
|
try:
|
|
if args.sigrok_channels is not None and not args.sigrok:
|
|
parser.error('--sigrok-channels requires --sigrok')
|
|
sigrok_channels = (args.sigrok_channels.split(',')
|
|
if args.sigrok_channels is not None else None)
|
|
if sigrok_channels is not None and any(
|
|
not pattern.strip() for pattern in sigrok_channels):
|
|
parser.error('--sigrok-channels contains an empty pattern')
|
|
sigrok_channels = ([pattern.strip() for pattern in sigrok_channels]
|
|
if sigrok_channels is not None else None)
|
|
result = generate(root, args.board, outdir, args.serial, args.uart_port,
|
|
args.state_dir, sigrok=args.sigrok,
|
|
sigrok_channels=sigrok_channels, uds=args.uds)
|
|
except (OSError, ValueError) as error:
|
|
print('error: %s' % error, file=sys.stderr)
|
|
return 1
|
|
print(result['repl'])
|
|
print(result['resc'])
|
|
for warning in result['warnings']:
|
|
print('warning: %s' % warning, file=sys.stderr)
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|