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)

@@ -58,6 +58,14 @@
extern AP_IOMCU iomcu;
#endif

#ifdef IOMCU_FW
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 only with H7 I think? Probably should add that to the define.

Copy link
Member Author

@bugobliterator bugobliterator Jan 29, 2025

Choose a reason for hiding this comment

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

done

@@ -100,6 +104,34 @@ void AP_IOMCU::init(void)
initialised = true;
}

/*
disable the IOMCU operation by erasing iofirmware
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think this should have a different name given the fairly radical function of its operation

Copy link
Member Author

Choose a reason for hiding this comment

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

thoughts on name, erase is already taken.

Copy link
Member Author

Choose a reason for hiding this comment

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

changing it to wipe

@@ -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

libraries/AP_IOMCU/iofirmware/analog.cpp Outdated Show resolved Hide resolved
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.

@@ -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?

@@ -22,6 +22,8 @@
#include "hal.h"
#include "analog.h"

#ifdef HAL_DISABLE_ADC_DRIVER
Copy link
Contributor

Choose a reason for hiding this comment

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

is this supposed to be ifndef?

Copy link
Member Author

Choose a reason for hiding this comment

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

no, that is correct. If AP::HAL ADC Driver is disabled analog.cpp handles ADC periph

Otherwise we use hal.analogin

@@ -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.

*/
#if defined(HAL_BL_IOMCU_FW_DETECT) && HAL_BL_IOMCU_FW_DETECT
#define HAL_IOMCU_MAGIC 0x937E638A
uint32_t is_iomcu_fw_present(void)
Copy link
Contributor

Choose a reason for hiding this comment

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

i'd like to understand why this is needed

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 so we can reduce the boot time if IOMCU is present to 200ms,. This is done by reading the IOMCU_FW_DETECT magic written to the end of flash.

I think I am going to add a boot time field to AP_CheckFirmware instead. Was looking for a shortcut but this hasn't turned out to be a clean implementation.

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?

@@ -1052,6 +1052,17 @@ def write_mcu_config(self, f):
else:
self.env_vars['IOMCU_FW'] = 0

if self.get_config('IOMCU_FW', required=False):
Copy link
Contributor

Choose a reason for hiding this comment

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

cut and paste duplication?

'#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

// 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.

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.

3 participants