Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
e1f6699
build(deps): bump EMIL to 0059646 for the services/hil terminal
claude Oct 3, 2026
dccd1be
fix(uart): disable the LPUART clock when an LPUART UartStm is destroyed
claude Oct 3, 2026
ebde276
feat(validation): add HIL validation firmware for NUCLEO-WB55RG and N…
claude Oct 4, 2026
fa1142f
fix(validation): harden the SPI, UART, ADC and encoder factories afte…
claude Oct 4, 2026
cfcabcf
feat(validation): add the hal_st_validation host package and pytest s…
claude Oct 4, 2026
7ff9dbc
fix(pwm): make PwmStm constructible and reset timer state between users
claude Oct 4, 2026
349d49c
fix(adc): support multi-channel sequences and TIM1 triggers on ADC4
claude Oct 4, 2026
cb03b77
fix(spi): start SPI v2 transfers that begin with a receive
claude Oct 4, 2026
01ae3f9
fix(dma): stop DMA streams without a late completion
claude Oct 4, 2026
df2de25
fix(uart): leave no UART state behind and survive overruns
claude Oct 4, 2026
4692418
fix(gpio): select the right port in EXTICR
claude Oct 4, 2026
4c5084a
fix(qei): reset the LPTIM before configuring the encoder
claude Oct 4, 2026
e2ba885
test(validation): guard the driver fixes instead of expecting the gaps
claude Oct 4, 2026
63133a7
fix(validation): wire the WB55 TIM2 encoder B to PA1 and document hea…
claude Oct 4, 2026
4fe3600
fix(uart): deinitialise the UART before gating its clock
claude Oct 4, 2026
d93f14b
fix(adc): reject a repeated channel with a different sampling time
claude Oct 4, 2026
e7dfbd4
docs(validation): align the markdown tables
claude Oct 4, 2026
1a622fa
devcontainer updated
gabrielfrasantos Oct 4, 2026
49d3731
docs: wrap markdown lines that exceed MegaLinter's 400-character limit
claude Oct 4, 2026
e6a871c
build(gen): generate leading-underscore IPs so STM32WB55 gets QUADSPI
claude Oct 4, 2026
b60d0e4
build(gen): add LPTIM to the STM32WBA peripheral table
claude Oct 4, 2026
00a90b9
fix(i2c): recover I2cStm from NACK, bus error and arbitration loss on…
claude Oct 4, 2026
85da300
fix(spi): run SpiSlaveStmDma on STM32WB/WBA and clear SPE between tra…
claude Oct 4, 2026
c4c02ae
fix(spi): check the SPI v3 data size range
claude Oct 4, 2026
b731025
fix(flash): erase the pages of the flash region on STM32WB/WBA
claude Oct 4, 2026
57c42ea
fix(flash): erase the pages of the synchronous flash region on STM32W…
claude Oct 4, 2026
95dced2
fix(flash): erase the page of an ECC error
claude Oct 4, 2026
067e89f
fix(rng): keep HSI48 running across a synchronized RNG read when it w…
claude Oct 4, 2026
48da16f
fix(hsem): wait for the semaphore and query it without locking
claude Oct 4, 2026
9d5e381
fix(dma): encode 16- and 32-bit DMA data widths on STM32WB and STM32WBA
claude Oct 4, 2026
bf98e6f
fix(rng): build RandomDataGeneratorStm
claude Oct 4, 2026
a1d3792
fix(clock): clock the STM32WBA55 RNG from HSI in the Nucleo default c…
claude Oct 4, 2026
d042cc2
fix(backupram): expose BackupRamStm as hal::BackupRam and enable TAMP…
claude Oct 4, 2026
966a219
fix(adc): measure the requested number of samples and stop the conver…
claude Oct 4, 2026
b8a1522
fix(gpio): index SynchronousGpioStm ports by their letter on STM32WB/WBA
claude Oct 4, 2026
1b65627
fix(qspi): complete QuadSpiStmDma writes on transfer complete and kee…
claude Oct 4, 2026
6d2d77f
feat(validation): add resource allocation, payloads and board data fo…
claude Oct 4, 2026
40b6977
docs(validation): document the I2C, SPI loop and bundle2 wiring options
claude Oct 4, 2026
2856e28
feat(validation): validate I2cStm with an I2C target scaffold and an …
claude Oct 4, 2026
7cabd2d
feat(validation): validate SpiSlaveStmDma, SPI data sizes and the sec…
claude Oct 4, 2026
ff44dcc
feat(validation): validate TimerStm, TimerPwmStm, the LPTIM drivers a…
claude Oct 4, 2026
7b628a2
feat(validation): validate PWM options, ADC single/temperature/burst …
claude Oct 4, 2026
38dc4d2
feat(validation): validate send-only UART, synchronous GPIO, clocks a…
claude Oct 4, 2026
9e0f6f0
feat(validation): validate the RNG, AES and PKA drivers
claude Oct 5, 2026
684bad1
feat(validation): validate flash, HSEM, backup RAM and low-power drivers
claude Oct 5, 2026
98f2c0b
feat(validation): validate QUADSPI on the NUCLEO-WB55RG
claude Oct 5, 2026
81428df
fix(rng): register the RNG interrupt under the name each device declares
claude Oct 5, 2026
9efd9ce
fix(qspi): derive the QUADSPI FIFO threshold from the FTHRES field
claude Oct 5, 2026
e80bf9b
fix(flash): erase per bank on dual-bank STM32WBA
claude Oct 5, 2026
5cf9d71
fix(flash): unlock the flash for the ECC error page erase
claude Oct 5, 2026
0711b87
fix(hsem): do not enable the HSEM interrupt while waiting for a lock
claude Oct 5, 2026
b741c5e
fix(timer): set the counter direction after clearing CMS and SMS
claude Oct 5, 2026
483ea3a
fix(pwm): reset the break and dead-time register in TimerPwmStm
claude Oct 5, 2026
037af25
fix(validation): address the review of the new command groups and tests
claude Oct 5, 2026
6dc23f3
ci: allowlist the NIST AES-128 test-vector keys in the secret scan
claude Oct 5, 2026
9e8b2a1
docs(validation): drop the space inside the hsi48 code span
claude Oct 5, 2026
bd4a3fe
docs(validation): code-span the --with <tag> hint so GitHub renders it
claude Oct 5, 2026
dc7084e
Merge main (squashed #95) into ccr-b7e792a2-full-coverage
claude Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitleaks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ title = "secret scan config"
useDefault = true

[allowlist]
description = "Generated KiCad footprint cache — datasheet URLs, not secrets"
description = "Generated KiCad footprint cache (datasheet URLs) and the public NIST AES-128 test-vector keys of the validation suite, not secrets"
paths = [
'''integration_test/pcb/integration_test_board/fp-info-cache''',
]
regexes = [
'''000102030405060708090a0b0c0d0e0f''',
'''2b7e151628aed2a6abf7158809cf4f3c''',
]
20 changes: 20 additions & 0 deletions hal_st/stm32fxxx/AdcDmaStm.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "hal_st/stm32fxxx/AdcDmaStm.hpp"
#include "infra/event/EventDispatcher.hpp"
#include "infra/util/ReallyAssert.hpp"
#include <algorithm>

namespace hal
{
Expand Down Expand Up @@ -59,7 +61,14 @@ namespace hal

timer.Start();
Configure();
#if defined(STM32WB) || defined(STM32WBA)
const auto samples = std::min(numberOfSamples, buffer.size());
really_assert(samples != 0);
measurement = infra::Head(buffer, samples);
dmaStream.StartReceive(measurement);
#else
dmaStream.StartReceive(buffer);
#endif
LL_ADC_REG_StartConversion(adc.Handle().Instance);
}

Expand Down Expand Up @@ -91,6 +100,13 @@ namespace hal

void AdcTriggeredByTimerWithDma::TransferDone()
{
#if defined(STM32WB) || defined(STM32WBA)
// External-trigger mode keeps ADSTART set, and ADC_Disable refuses while it is set
LL_ADC_REG_StopConversion(adc.Handle().Instance);
while (LL_ADC_REG_IsStopConversionOngoing(adc.Handle().Instance))
{
}
#endif
auto result = ADC_Disable(&adc.Handle());
assert(result == HAL_OK);

Expand All @@ -99,7 +115,11 @@ namespace hal
if (this->onDone)
infra::EventDispatcher::Instance().Schedule([this]()
{
#if defined(STM32WB) || defined(STM32WBA)
onDone(measurement);
#else
onDone(buffer);
#endif
});
}
}
3 changes: 3 additions & 0 deletions hal_st/stm32fxxx/AdcDmaStm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ namespace hal
AnalogPinStm analogPin;
FreeRunningTimerStm timer;
infra::AutoResetFunction<void(infra::MemoryRange<uint16_t>)> onDone;
#if defined(STM32WB) || defined(STM32WBA)
infra::MemoryRange<uint16_t> measurement;
#endif
};
}

Expand Down
8 changes: 8 additions & 0 deletions hal_st/stm32fxxx/BackupRamStm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@

namespace hal
{
BackupRamStm::BackupRamStm()
{
#if defined(STM32WBA)
__HAL_RCC_RTCAPB_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();
#endif
}

infra::MemoryRange<volatile uint32_t> BackupRamStm::Get() const
{
#if defined(STM32G0)
Expand Down
4 changes: 3 additions & 1 deletion hal_st/stm32fxxx/BackupRamStm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
namespace hal
{
class BackupRamStm
: hal::BackupRam<volatile uint32_t>
: public hal::BackupRam<volatile uint32_t>
{
public:
BackupRamStm();

infra::MemoryRange<volatile uint32_t> Get() const override;
};
}
Expand Down
11 changes: 11 additions & 0 deletions hal_st/stm32fxxx/DefaultClockNucleoWBA55CG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* PLL_Q = 2
* PLL_R = 2
* Flash Latency(WS) = 3
* RNG kernel clock = HSI
*/
void ConfigureDefaultClockNucleoWBA55CG()
{
Expand Down Expand Up @@ -52,4 +53,14 @@ void ConfigureDefaultClockNucleoWBA55CG()
RCC_ClkInitStruct.AHB5_PLL1_CLKDivider = RCC_SYSCLK_PLL1_DIV4;
RCC_ClkInitStruct.AHB5_HSEHSI_CLKDivider = RCC_SYSCLK_HSEHSI_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3);

LL_RCC_HSI_Enable();
while (!LL_RCC_HSI_IsReady())
{
}

RCC_PeriphCLKInitTypeDef RCC_PeriphClkInitStruct = { 0 };
RCC_PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RNG;
RCC_PeriphClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_HSI;
HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInitStruct);
}
20 changes: 20 additions & 0 deletions hal_st/stm32fxxx/DmaStm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,13 @@ namespace hal
auto streamRegister = DmaChannel[dmaIndex][streamIndex];

#if defined(GPDMA1)
#if defined(STM32WBA)
const uint32_t log2 = dataSizeInBytes == 4 ? 2 : (dataSizeInBytes == 2 ? 1 : 0);
if (!(streamRegister->CTR2 & DMA_MEMORY_TO_MEMORY) && (streamRegister->CTR2 & DMA_MEMORY_TO_PERIPH))
MODIFY_REG(streamRegister->CTR1, DMA_CTR1_DDW_LOG2, log2 << DMA_CTR1_DDW_LOG2_Pos);
else if (!(streamRegister->CTR2 & DMA_MEMORY_TO_MEMORY))
MODIFY_REG(streamRegister->CTR1, DMA_CTR1_SDW_LOG2, log2 << DMA_CTR1_SDW_LOG2_Pos);
#else
dataSizeInBytes &= 0x03;
uint32_t mask{ 0 };
if (!(streamRegister->CTR2 & DMA_MEMORY_TO_MEMORY) && (streamRegister->CTR2 & DMA_MEMORY_TO_PERIPH))
Expand All @@ -539,8 +546,11 @@ namespace hal
mask = (dataSizeInBytes >> 1) << DMA_CTR1_SDW_LOG2_Pos;

streamRegister->CTR1 = (streamRegister->CTR1 & ~mask) | (streamRegister->CTR1 | mask);
#endif
#elif defined(DMA_SxCR_PSIZE)
streamRegister->CR = (streamRegister->CR & ~DMA_SxCR_PSIZE) | ((dataSizeInBytes & 1) == 0 ? DMA_SxCR_PSIZE_0 : 0) | (dataSizeInBytes > 2 ? DMA_SxCR_PSIZE_1 : 0);
#elif defined(STM32WB)
streamRegister->CCR = (streamRegister->CCR & ~DMA_CCR_PSIZE) | (dataSizeInBytes == 2 ? DMA_CCR_PSIZE_0 : 0) | (dataSizeInBytes == 4 ? DMA_CCR_PSIZE_1 : 0);
#else
streamRegister->CCR = (streamRegister->CCR & ~DMA_CCR_PSIZE) | ((dataSizeInBytes & 1) == 0 ? DMA_CCR_PSIZE_0 : 0) | (dataSizeInBytes > 2 ? DMA_CCR_PSIZE_1 : 0);
#endif
Expand All @@ -550,6 +560,13 @@ namespace hal
{
auto streamRegister = DmaChannel[dmaIndex][streamIndex];
#if defined(GPDMA1)
#if defined(STM32WBA)
const uint32_t log2 = dataSizeInBytes == 4 ? 2 : (dataSizeInBytes == 2 ? 1 : 0);
if (!(streamRegister->CTR2 & DMA_MEMORY_TO_MEMORY) && (streamRegister->CTR2 & DMA_MEMORY_TO_PERIPH))
MODIFY_REG(streamRegister->CTR1, DMA_CTR1_SDW_LOG2, log2 << DMA_CTR1_SDW_LOG2_Pos);
else if (!(streamRegister->CTR2 & DMA_MEMORY_TO_MEMORY))
MODIFY_REG(streamRegister->CTR1, DMA_CTR1_DDW_LOG2, log2 << DMA_CTR1_DDW_LOG2_Pos);
#else
dataSizeInBytes &= 0x03;
uint32_t mask{ 0 };
if (!(streamRegister->CTR2 & DMA_MEMORY_TO_MEMORY) && (streamRegister->CTR2 & DMA_MEMORY_TO_PERIPH))
Expand All @@ -558,8 +575,11 @@ namespace hal
mask = (dataSizeInBytes >> 1) << DMA_CTR1_DDW_LOG2_Pos;

streamRegister->CTR1 = (streamRegister->CTR1 & ~mask) | (streamRegister->CTR1 | mask);
#endif
#elif defined(DMA_SxCR_MSIZE)
streamRegister->CR = (streamRegister->CR & ~DMA_SxCR_MSIZE) | ((dataSizeInBytes & 1) == 0 ? DMA_SxCR_MSIZE_0 : 0) | (dataSizeInBytes > 2 ? DMA_SxCR_MSIZE_1 : 0);
#elif defined(STM32WB)
streamRegister->CCR = (streamRegister->CCR & ~DMA_CCR_MSIZE) | (dataSizeInBytes == 2 ? DMA_CCR_MSIZE_0 : 0) | (dataSizeInBytes == 4 ? DMA_CCR_MSIZE_1 : 0);
#else
streamRegister->CCR = (streamRegister->CCR & ~DMA_CCR_MSIZE) | ((dataSizeInBytes & 1) == 0 ? DMA_CCR_MSIZE_0 : 0) | (dataSizeInBytes > 2 ? DMA_CCR_MSIZE_1 : 0);
#endif
Expand Down
16 changes: 14 additions & 2 deletions hal_st/stm32fxxx/FlashCoordinatedWithWirelessStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,23 @@ namespace hal
{
if (__HAL_FLASH_GET_FLAG(FLASH_FLAG_ECCD))
{
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ECCD);
const uint32_t errorAddress = READ_BIT(FLASH->ECCR, FLASH_ECCR_ADDR_ECC);
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ECCD);

if (pFlash.Lock == HAL_LOCKED || __HAL_FLASH_GET_FLAG(FLASH_FLAG_BSY))
return;

// An unlocked FLASH_CR belongs to an interrupted WriteBuffer/EraseSectors, which must find it still unlocked
const bool wasLocked = READ_BIT(FLASH->CR, FLASH_CR_LOCK) != 0;
if (wasLocked && HAL_FLASH_Unlock() != HAL_OK)
return;

uint32_t pageError = 0;
FLASH_EraseInitTypeDef eraseInitStruct{ .TypeErase = FLASH_TYPEERASE_PAGES, .Page = flash.SectorOfAddress(errorAddress), .NbPages = 1 };
FLASH_EraseInitTypeDef eraseInitStruct{ .TypeErase = FLASH_TYPEERASE_PAGES, .Page = (errorAddress * 8U) / FLASH_PAGE_SIZE, .NbPages = 1 };
HAL_FLASHEx_Erase(&eraseInitStruct, &pageError);

if (wasLocked)
HAL_FLASH_Lock();
}
}

Expand Down
5 changes: 4 additions & 1 deletion hal_st/stm32fxxx/FlashInternalStm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ namespace hal
{
HAL_FLASH_Unlock();

#if defined(STM32WB) || defined(STM32G4) || defined(STM32G0) || defined(STM32WBA)
#if defined(STM32WB) || defined(STM32WBA)
const auto regionOffset = reinterpret_cast<uint32_t>(flashMemory.begin()) - FLASH_BASE;
detail::ErasePages((regionOffset + AddressOfSector(beginIndex)) / FLASH_PAGE_SIZE, (regionOffset + AddressOfSector(endIndex)) / FLASH_PAGE_SIZE);
#elif defined(STM32G4) || defined(STM32G0)
uint32_t pageError = 0;

FLASH_EraseInitTypeDef eraseInitStruct;
Expand Down
32 changes: 32 additions & 0 deletions hal_st/stm32fxxx/FlashInternalStmDetail.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
#define HAL_FLASH_INTERNAL_STM_DETAIL_HPP

#include DEVICE_HEADER
#include "infra/util/ReallyAssert.hpp"
#include "services/flash/FlashAlign.hpp"
#include <algorithm>
#include <cstdint>

namespace hal
Expand Down Expand Up @@ -53,6 +55,36 @@ namespace hal
chunk = flashAlign.Next();
}
}

#if defined(STM32WB) || defined(STM32WBA)
inline void ErasePages(uint32_t page, uint32_t endPage)
{
#if defined(FLASH_DBANK_SUPPORT)
const uint32_t pagesPerBank = FLASH_PAGE_NB;
const bool swapped = READ_BIT(FLASH->OPTR, FLASH_OPTR_SWAP_BANK) != 0;
#endif

while (page != endPage)
{
FLASH_EraseInitTypeDef eraseInitStruct{};
eraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
#if defined(FLASH_DBANK_SUPPORT)
const uint32_t bankEndPage = std::min(endPage, (page / pagesPerBank + 1) * pagesPerBank);
eraseInitStruct.Banks = (page >= pagesPerBank) == swapped ? FLASH_BANK_1 : FLASH_BANK_2;
eraseInitStruct.Page = page % pagesPerBank;
#else
const uint32_t bankEndPage = endPage;
eraseInitStruct.Page = page;
#endif
eraseInitStruct.NbPages = bankEndPage - page;

uint32_t pageError = 0;
auto result = HAL_FLASHEx_Erase(&eraseInitStruct, &pageError);
really_assert(result == HAL_OK);
page = bankEndPage;
}
}
#endif
}
}

Expand Down
8 changes: 4 additions & 4 deletions hal_st/stm32fxxx/GeneratePeripheralTableStructure.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,15 @@
<xsl:template match="mcu:IP">
<xsl:param name="ip-name"/>
<xsl:param name="prefix"/>
<xsl:if test="@Name = $ip-name">
<xsl:if test="@Name = $ip-name or @Name = concat('_', $ip-name)">
<item>
<xsl:attribute name="name"><xsl:value-of select="@InstanceName"/></xsl:attribute>
<xsl:choose>
<xsl:when test="$prefix = '' and @InstanceName = @Name">
<xsl:when test="$prefix = '' and @InstanceName = $ip-name">
<xsl:attribute name="position">1</xsl:attribute>
</xsl:when>
<xsl:when test="$prefix = '' and @InstanceName != @Name">
<xsl:attribute name="position"><xsl:value-of select="substring(@InstanceName, string-length(@Name) + 1)"/></xsl:attribute>
<xsl:when test="$prefix = '' and @InstanceName != $ip-name">
<xsl:attribute name="position"><xsl:value-of select="substring(@InstanceName, string-length($ip-name) + 1)"/></xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="position"><xsl:value-of select="substring(@InstanceName, string-length($prefix) + 1)"/></xsl:attribute>
Expand Down
Loading
Loading