Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for running IOMCU FW on CubeRedSecondary #29094

Open
wants to merge 19 commits into
base: master
Choose a base branch
from

Conversation

bugobliterator
Copy link
Member

@bugobliterator bugobliterator commented Jan 17, 2025

Tested working DSHOT, PWM modes and RC Input on CubredSecondary with IOFirmware.

No size changes for older iomcus, checked using sizecompare for both with and without dshot.

Allows updates to the secondary MCU's firmware even after the IOFirmware is flashed.

@bugobliterator bugobliterator marked this pull request as draft January 17, 2025 00:33
@bugobliterator bugobliterator force-pushed the pr-cubered-iomcu branch 3 times, most recently from ed65847 to 2647c5d Compare January 17, 2025 04:22
@bugobliterator bugobliterator force-pushed the pr-cubered-iomcu branch 2 times, most recently from dfc80b2 to 100901b Compare January 28, 2025 01:52
@bugobliterator bugobliterator requested review from andyp1per and tridge and removed request for andyp1per January 29, 2025 03:52
@bugobliterator bugobliterator marked this pull request as ready for review January 29, 2025 03:52
Copy link
Collaborator

@andyp1per andyp1per left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good. Some small changes. Think you need to think about how it looks if someone adds F4 support (which has been requested)

@@ -281,7 +290,7 @@ bool AP_IOMCU::erase()
debug("erase...");
send(PROTO_CHIP_ERASE);
send(PROTO_EOC);
return get_sync(10000);
return get_sync(20000);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a timeout? Maybe add a comment

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE);
STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE
#if defined(STM32H7)
| (1<<20)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs a define

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will leave a comment, we don't have a standard define for this as this is from an errata

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed through define and also in UARTDriver

STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE);
STM32_DMA_CR_MINC | STM32_DMA_CR_TCIE
#if defined(STM32H7)
| (1<<20)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

uart->usart->CR1 = cr1 | USART_CR1_SBK;

CLEAR_ISR_FLAG(LBD);
SEND_BREAK();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a slight change in behaviour because this was a local variable for threading reasons. Ideally would preserve the current semantics

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to change it back to original as it doesn't really matter in this context.

But the change is actually correct. Firstly a thread cannot interrupt an interrupt, another higher priority interrupt can. Secondly, only bit we want to set in CR1 is Line Break, rest of the bits we don't want to touch. With the older method there's a chance we can end up doing just that.
Basically if there was another interrupt or peripheral event (between where we store cr1 and call SEND_BREAK) where the CR1 is changed we will end up changing it again unknowingly. Fortuntely we don't mess with CR1 outside of init and this interrupt, so it is a non-issue. But in general this can cause issue.

@@ -176,7 +257,7 @@ static void idle_rx_handler(hal_uart_driver *uart)

if ((sr & USART_SR_TC) && (cr1 & USART_CR1_TCIE)) {
/* TC interrupt cleared and disabled.*/
uart->usart->SR &= ~USART_SR_TC;
CLEAR_ISR_FLAG(TC);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be better to use the MODIFY_REG() macro that you used in bdshot

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't work, here. Its a completely different operation for STM32H7. its a modify register operation in STM32F1 but a simple write operation in H7. So this is the only way.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

uart->dmatx = dmaStreamAllocI(STM32_UART_USART2_TX_DMA_STREAM,
STM32_UART_USART2_IRQ_PRIORITY,
uart->dmatx = dmaStreamAllocI(HAL_IO_FMU_COMMS_TX_DMA_STREAM,
12, //IRQ Priority
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is bypassing the config, thing it should use the define or put a define above

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I was trying to avoid adding yet another define that's going to remain untouched. Will add this to define as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still pending?

@@ -447,7 +567,7 @@ void AP_IOMCU_FW::update()
loop_counter++;

if (do_reboot && (last_ms > reboot_time)) {
hal.scheduler->reboot(true);
hal.scheduler->reboot(false);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was incorrect to begin with. We don't actually want to hold in bootloader here. And we don't for iomcu bootloader we are currently using (as its extremely old and didn't even have that feature).

The behaviour of this in current bootloader is that it holds the MCU in bootloader forever. If you reboot ardupilot, the iomcu will sit in bootloader forever, hence failing IOMCU sanity check and force trigger a firmware update everytime.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok sounds reasonable. Probably needs testing on other boards that have iofirmware update issues - particularly Pixhawk6X

@bugobliterator bugobliterator force-pushed the pr-cubered-iomcu branch 2 times, most recently from 5fad99f to 18c7ecc Compare January 30, 2025 02:56
Copy link
Collaborator

@andyp1per andyp1per left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just needs testing iofirmware upgrades on some of the other boards (e.g. Pixhawk6X)

Copy link
Contributor

@tridge tridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm glad to see this becoming an option, just need to work out how to properly dynamically choose if UART is IOMCU or some other protocol, the method in this PR doesn't look right
also, this does change the size of iofirmware_f103_lowpolh.bin by 8 bytes, need to work out why

@@ -1884,6 +1884,12 @@ void usb_initialise(void)
// disable TX/RX pins for unusued uart
void UARTDriver::disable_rxtx(void) const
{
#if HAL_WITH_IO_MCU
if (sdef.get_index() == HAL_UART_IOMCU_IDX) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this added? it would be valid to call this function on IOMCU uart if BRD_IO_ENABLE was false for example. We don't do that now, but I don't understand the special case
I suspect this is all about the serial port being able to be either a link to IOMCU or a normal serial manager uart (eg. for PPP of mavlink)
why not add a SERIALn_PROTOCOL=IOMCU enum value, then handle this in SerialManager?

@@ -87,6 +87,12 @@ int main(void)
bool try_boot = false;
uint32_t timeout = HAL_BOOTLOADER_TIMEOUT;

#if defined(HAL_BL_IOMCU_FW_DETECT) && HAL_BL_IOMCU_FW_DETECT
if (is_iomcu_fw_present()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed? This seems to be forcing a shorter timeout if there is already a IO fw present, which seems odd

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current iofirmware bootloader has 200ms timeout. Also if iofirmware doesn't boot faster than when AP_IOMCU tries to detect it, it will simply try to reflash.

void AP_memory_guard_error(uint16_t error);
void AP_memory_guard_error(uint16_t error) {
// simply panic for now
AP_HAL::panic("PANIC: memory guard error %u", error);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't a reboot be better? a panic will loop forever, causing the vehicle to crash with no IO

self.env_vars['IOMCU_FW'] = 0

# check if heater pin defined
if 'HEATER' in self.bylabel.keys():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already handle not having a heater with the HEATER_SET() macro, why also handle it at the build level?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to disable multiple builds for highpol and lowpol heaters. With hwdef without Heaters we were still doing that.

'#define HAL_UART_IO_DRIVER ChibiOS::UARTDriver uart_io(HAL_UART_IOMCU_IDX)\n'
)
serial_list.append(self.config['IOMCU_UART'][0])
if self.config['IOMCU_UART'][0].startswith('HAL_SERIAL'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we made AP_IOMCU take a pointer to a AP_HAL::UARTDriver instead of a reference then we could have an AP::iomcu()->set_uart() call that could be made from AP_SerialManager. That would enable runtime configuration.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I am going to do this in a separate PR, as that will require quite a decent amount of change to AP_IOMCU. A separate PR for this change will be much easier to review.

For this PR I can go part of the way and use SerialProtocol and hal.serial instead of current method of declairing a separate serial driver for all iomcu users.

if (BoardConfig.io_enabled()) {
// If IO is enabled, we need to set the protocol to None
// as the IO MCU UART is handled by the AP_IOMCU class
serial_manager.set_protocol_and_baud(HAL_UART_IOMCU_IDX, AP_SerialManager::SerialProtocol_None, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SerialProtocol_IOMCU would make more sense

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to SerialProtocol_IOMCU

// as the IO MCU UART is handled by the AP_IOMCU class
serial_manager.set_protocol_and_baud(HAL_UART_IOMCU_IDX, AP_SerialManager::SerialProtocol_None, 0);
} else {
iomcu.wipe();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are we wiping the fw if IO not enabled?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to allow flashing a different firmware to secondary. We have to reduce the boot time if IOMCU is enabled to 200ms, which is not long enough for it to be caught over USB. To allow updating Secondary MCU over USB again we need wipe iofirmware.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not relevant anymore, moved to different procedure to allow updates other than iofirmware.

@bugobliterator
Copy link
Member Author

@tridge I have pushed changes per your comments.

  • got rid of the bootloader changes, and using bootloader CRC to verify IOMCU at boot.
  • Added option SerialProtocol_IOMCU, currently serialmanager is not doing anything with it.
  • moved to using serialxDriver for IOMCU as well.


define HAL_USE_RTC FALSE
define HAL_NO_UARTDRIVER TRUE
define HAL_LOGGING_ENABLED 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these in here? These are the default for io firmware... (defaults_periph.h)

define HAL_GPIO_PIN_SPEKTRUM_OUT PAL_LINE(GPIOC,11U)


define AP_NETWORKING_BACKEND_PPP 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is odd?

Comment on lines +65 to +68
#define STR(x) XSTR(x)
#define XSTR(x) #x


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused?

@@ -95,6 +95,14 @@ THD_WORKING_AREA(_monitor_thread_wa, MONITOR_THD_WA_SIZE);
#define AP_HAL_CHIBIOS_IN_EXPECTED_DELAY_WHEN_NOT_INITIALISED 1
#endif

#if defined(STM32H7) && defined(IOMCU_FW)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#if defined(STM32H7) && defined(IOMCU_FW)
#if AP_REBOOT_ON_MEMGUARD_ERROR

... and default it off and define to true in the hwdef.

Copy link
Contributor

@tridge tridge left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd like to see binary cmp showing no change in IO fw builds

@@ -97,6 +101,14 @@ bool AP_IOMCU::upload_fw(void)
}
debug("found bootloader revision: %u", unsigned(bl_rev));

if (bl_rev > 2) {
// verify the CRC of the IO firmware
if (verify_rev3(fw_size)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why add this in? It makes me a bit nervous to be changing this behaviour for existing boards
maybe ifdef it for only this "modern" IOMCU

debug("Err: failed to contact bootloader");
uint32_t bl_rev, unused;
if (!get_info(INFO_BL_REV, bl_rev) ||
!get_info(INFO_BOARD_ID, unused) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think make these changes under #ifdef for IOMCU_CHIBIOS_BOOTLOADER

@@ -281,7 +293,7 @@ bool AP_IOMCU::erase()
debug("erase...");
send(PROTO_CHIP_ERASE);
send(PROTO_EOC);
return get_sync(10000);
return get_sync(20000); // 20s timeout for erase
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ifdef IOMCU_CHIBIOS_BOOTLOADER or define for timeout

@@ -417,12 +429,13 @@ bool AP_IOMCU::verify_rev3(uint32_t fw_size_local)
/* compare the CRC sum from the IO with the one calculated */
if (sum != crc) {
debug("CRC wrong: received: 0x%x, expected: 0x%x", (unsigned)crc, (unsigned)sum);
get_sync();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ifdef

return false;
}

crc_is_ok = true;

return true;
return get_sync();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ifdef

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants