I2C transfer of large data (10kb +) crashes chibios Topic is solved

ChibiOS public support forum for topics related to the STMicroelectronics STM32 family of micro-controllers.

Moderators: RoccoMarco, barthess

Nicolai86
Posts: 9
Joined: Sun Jul 12, 2020 5:43 pm
Has thanked: 1 time

I2C transfer of large data (10kb +) crashes chibios

Postby Nicolai86 » Fri Jul 31, 2020 7:09 pm

Hey,

I've migrated the stm32_write_memory procedure from [stm32flash project](https://sourceforge.net/p/stm32flash/wiki/Home/) to work with chibios. The change itself is pretty straight forward: changing the i2c interactions to call i2cMasterTransmitTimeout and i2cMasterReceiveTimeout.

Now when I use chibios to remote-flash an example firmware of 13kb chibios crashes when starting to write data; originally I thought this is because chibios limits i2c transfers to 256 bytes max. However lowering the limit - writing in chunks of 128 bytes - doesn't help at all;

To verify it's not the stm32flash code which is at fault I migrated it to a STCubeMX code base (again, just changing i2c interaction code) and it works flawlessly. I'm flashing a STM32G0 from a STM32F303.

Any idea why chibios would crash when writing this data via i2c? Unfortunately my STLink broke so I can't debug on the device right now.

I'm executing this in the main loop of the program, not from any thread.

Here's the snippet in question: it writes the first chunk of data and then the bus hangs up.
I've confirmed via my salea logic pro that SCL is pulled low. Since the code works in a STCubeMX HAL setup I suspect it's not the slave device which causes this.

Code: Select all


// main.cpp
int main() {
 // all the usual init code
    // reset STM32G0 into alternate boot mode to allow flashing via i2c
     
    I2CDriver *p = &I2CD1;
   
    i2cStart(p, &i2ccfg);
    if (auto resp = stm32_extended_erase(p); resp != STM32_ERR_OK) {
        chprintf(chp, "unable to erase device\n");
    } else {
        chprintf(chp, "device erased\n");
    }

    i2cStart(p, &i2ccfg);
    if (auto resp = stm32_write_memory(p, 0x8000000, build_stm32g031k8_bin, build_stm32g031k8_bin_len);
            resp != STM32_ERR_OK) {
        chprintf(chp, "unable to program device\n");
    } else {
        chprintf(chp, "device programed\n");
    }
}

stm32_err_t stm32_write_memory(I2CDriver *p, uint32_t startAddress, const uint8_t *progData, uint32_t totalLength) {
    uint8_t buffer[255];
    uint32_t w;
    uint32_t address = startAddress;
    uint32_t remainingLength = totalLength;
    uint32_t offset = 0;

    while (remainingLength > 0) {
        w = remainingLength > 255 ? 255 : remainingLength;
        for (uint32_t i = 0; i < w; i++) {
            buffer[i] = progData[offset + i];
        }

        stm32_err_t err = stm32_write_data(p, address, buffer, w);
        if (err != STM32_ERR_OK) {
            return err;
        }
        address += w;
        remainingLength -= w;
        offset += w;
    }
    return STM32_ERR_OK;
}

inline stm32_err_t stm32_write_data(I2CDriver *p, uint32_t startAddress, uint8_t *data, uint32_t totalLength) {
    if (totalLength > 256) {
        return STM32_ERR_UNKNOWN;
    }

    if (startAddress & 0x3) {
        return STM32_ERR_UNKNOWN;
    }

    stm32_err_t resp = stm32_send_command_timeout(p, STM32_CMD_WRITE_MEMORY, STM32_MASSERASE_TIMEOUT);
    if (resp != STM32_ERR_OK) {
        return resp;
    }

    uint8_t addrBuf[5];
    addrBuf[0] = startAddress >> 24;
    addrBuf[1] = (startAddress >> 16) & 0xFF;
    addrBuf[2] = (startAddress >> 8) & 0xFF;
    addrBuf[3] = startAddress & 0xFF;
    addrBuf[4] = addrBuf[0] ^ addrBuf[1] ^ addrBuf[2] ^ addrBuf[3];
    msg_t resp2 = i2cMasterTransmitTimeout(p, STM32_I2C_ADDR,
                                           addrBuf, sizeof(addrBuf),
                                           0, 0,
                                           TIME_MS2I(STM32_MASSERASE_TIMEOUT));

    if (resp2 != MSG_OK) {
        return STM32_ERR_UNKNOWN;
    }

    resp = stm32_get_ack_timeout(p, STM32_MASSERASE_TIMEOUT);
    if (resp != STM32_ERR_OK) {
        return resp;
    }

    unsigned int i = 0;
    unsigned int aligned_len = (totalLength + 3) & ~3; // max 256
    uint8_t cs = aligned_len - 1;
    uint8_t buf[256 + 2] = {0};

    buf[0] = aligned_len - 1;
    for (i = 0; i < totalLength; i++) {
        cs ^= data[i];
        buf[i + 1] = data[i];
    }
    for (i = totalLength; i < aligned_len; i++) {
        cs ^= 0xFF;
        buf[i + 1] = 0xFF;
    }
    buf[aligned_len + 1] = cs;
    resp2 = i2cMasterTransmitTimeout(p, STM32_I2C_ADDR,
                                     buf, aligned_len + 2,
                                     0, 0, TIME_MS2I(STM32_MASSERASE_TIMEOUT));
    if (resp2 != MSG_OK) {
        return STM32_ERR_UNKNOWN;
    }
    return stm32_get_ack_timeout(p, STM32_BLKWRITE_TIMEOUT);
}


I varied all variables right now I can think of but nothing helps:
- slowing down the i2C Bus doesn't help (running at 1MHz)
- I2C bus works fine otherwise - there are other devices on the bus which work fine
- STM32G0 option bytes are configured to allow resetting it by exposing BOOT0 setting as pin
- STM32F303 works fine if I don't try to flash
- GCC flags are identical to STCubeMX
- migrating code to C++ or C doesn't change anything. the main project is in C++

Any suggestions on how to debug this are greatly appreciated.
Last edited by Nicolai86 on Fri Jul 31, 2020 7:21 pm, edited 1 time in total.

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: I2C transfer of large data (10kb +) crashes chibios

Postby Giovanni » Fri Jul 31, 2020 7:21 pm

Hi,

Are debug options enabled?

This could help: http://www.chibios.org/dokuwiki/doku.ph ... ebug_guide

If an error is caught then look at the error message and stack trace.

Giovanni

Nicolai86
Posts: 9
Joined: Sun Jul 12, 2020 5:43 pm
Has thanked: 1 time

Re: I2C transfer of large data (10kb +) crashes chibios

Postby Nicolai86 » Fri Jul 31, 2020 7:24 pm

Hey Giovanni,

thanks for the suggestion. I have debugging enabled, however my STLink V2 died some days ago and I'm still waiting on the replacment.
Is there any way to transmit this information from the STM32F303 without having a debug probe attached?

Best,
Raphael

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: I2C transfer of large data (10kb +) crashes chibios

Postby Giovanni » Fri Jul 31, 2020 7:57 pm

Not easily, once the system is stopped all drivers are dead, you would need to write some status dump code on halt, outputting on an UART for example.

Giovanni

Nicolai86
Posts: 9
Joined: Sun Jul 12, 2020 5:43 pm
Has thanked: 1 time

Re: I2C transfer of large data (10kb +) crashes chibios

Postby Nicolai86 » Sun Aug 02, 2020 9:13 pm

So I got my debugger replacement - it seems that chibios experiences a stack overflow, but not when sending the huge amount of data; it's experiencing it when sending 2 byte command before the actual transmission.

Here's the stack trace:

Code: Select all

chSysHalt chsys.c:205
chSchGoSleepS chschd.c:313
chSchGoSleepTimeoutS chschd.c:383
chThdSuspendTimeoutS chthreads.c:776
osalThreadSuspendTimeoutS osal.h:760
i2c_lld_master_transmit_timeout hal_i2c_lld.c:1152
i2cMasterTransmitTimeout hal_i2c.c:189
stm32_send_command_timeout remote_flash.c:46
stm32_write_data remote_flash.c:173
stm32_write_memory remote_flash.c:242
resetSTM main.cpp:229
main main.cpp:432
endinitloop crt0_v7m.S:330


the reason is "stack overflow"

repeated execution either ends up in above scenario or in an unhandled exception case:

Code: Select all

_unhandled_exception vectors.S:1027
VectorFC vectors.S:1021

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: I2C transfer of large data (10kb +) crashes chibios  Topic is solved

Postby Giovanni » Sun Aug 02, 2020 9:21 pm

OK, you have a thread stack overflowing, you need to understand which one. As a general rule do use large automatic variables (arrays declared within a function).

Giovanni

Nicolai86
Posts: 9
Joined: Sun Jul 12, 2020 5:43 pm
Has thanked: 1 time

Re: I2C transfer of large data (10kb +) crashes chibios

Postby Nicolai86 » Sun Aug 02, 2020 11:41 pm

I'm not sure I understand how the thread stacks should be involved here.

I disabled the idle main thread and this is executed directly inside main.
The data being transfered via i2c is declared as

Code: Select all

static const uint8_t build_stm32g031k8_bin[]
outside of main, so I'd expect it to be static to the entire program.

The only thread which is created (verified by having a breakpoint in chInitThread) is a virtual com port thread, which is idle while this transaction is executed.

Why/ how would the thread stack affect this?

Nicolai86
Posts: 9
Joined: Sun Jul 12, 2020 5:43 pm
Has thanked: 1 time

Re: I2C transfer of large data (10kb +) crashes chibios

Postby Nicolai86 » Mon Aug 03, 2020 1:01 am

So I ended up pulling in the exceptionvectors.c from [this thread](viewtopic.php?t=2506).

Code: Select all

HardFault_Handler exceptionvectors.c:63
<signal handler called> 0x00000000ffffffed
<unknown> 0x000000000000000a


Code: Select all

Signal = SIGTRAP (Trace/breakpoint trap)
isFaultOnStacking = {_Bool} false
faultAddress = {uint32_t} 3758157112
ctx = {port_extctx}
faultType = {FaultType} HardFault
isFaultImprecise = {_Bool} false
isFaultOnUnstacking = {_Bool} false
isFaultPrecise = {_Bool} false
isFaultAddressValid = {_Bool} false


I'm running 20.3.x at 5f523dc . I'll take a look if downgrading changes anything.

Nicolai86
Posts: 9
Joined: Sun Jul 12, 2020 5:43 pm
Has thanked: 1 time

Re: I2C transfer of large data (10kb +) crashes chibios

Postby Nicolai86 » Mon Aug 03, 2020 6:20 am

Downgrading didn't change anything. But maybe the UFSR holds an answer: per gdb the value is 0x4:

Code: Select all

(gdb)  print/x *(uint32_t *) 0xE000ED2A
$4 = 0x4


Which per online documentation means:

- NOCP - Indicates that a Cortex-M coprocessor instruction was issued but the coprocessor was disabled or not present. One common case where this fault happens is when code is compiled to use the Floating Point extension (-mfloat-abi=hard -mfpu=fpv4-sp-d16) but the coprocessor was not enabled on boot.
- INVSTATE - Indicates the processor has tried to execute an instruction with an invalid Execution Program Status Register (EPSR) value. Among other things the ESPR tracks whether or not the processor is in thumb mode state. Instructions which use “interworking addresses”2 (bx & blx or ldr & ldm when loading a pc-relative value) must set bit[0] of the instruction to 1 as this is used to update ESPR.T. If this rule is violated, a INVSTATE exception will be generated. When writing C code, the compiler will take care of this automatically, but this is a common bug which can arise when hand-writing assembly.

I 'll see if recompiling it without float abi helps. I don't know how to tell chibios to disable/ or enable the coprocessor...

User avatar
Giovanni
Site Admin
Posts: 14455
Joined: Wed May 27, 2009 8:48 am
Location: Salerno, Italy
Has thanked: 1076 times
Been thanked: 922 times
Contact:

Re: I2C transfer of large data (10kb +) crashes chibios

Postby Giovanni » Mon Aug 03, 2020 8:46 am

Main() can overflow too depending on what you do in there, the stack size setting for main() is in the makefile, see "process stack" NOT "main stack".

Giovanni


Return to “STM32 Support”

Who is online

Users browsing this forum: No registered users and 3 guests