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.