Skip to main content
Associate II
July 31, 2026
Solved

Distinguishing H5 processor type from firmware

  • July 31, 2026
  • 4 replies
  • 96 views

We are using a mix of stm32 - G473, H503, H523, H563, H573.

For each of these, we have a bootloader that locks the chip security (to Closed for full regression with password on the H5s).

If we inadvertently put the wrong bootloader on a board, some combinations will brick the board - specifically, loading 523 or 563 bootloaders onto 503 or 573 boards goes to the closed state without a useful password in place.

I would like to programmatically check that each bootloader is running on its expected target before setting security. I see the IDCODE register contains 0x484 for STM32H562/563/573, or 0x478 for STM32H523/533. Unfortunately this distinguishes the 523/563 which is not such a problem for us, but not the 563/573 which is our problem.

Ideally, I would like a (different) function for each of our micros that will run correctly on the expected micro but return an error (or possibly hard fault) on anything else.

Thanks

Best answer by Zman552625

OK, this one works :-). Basically, I am trying to run full encryption cycle and check the results. On H563, the completion flag never gets set. This code probably can be shortened/simplified, but it is good enough for me…

// Since we compile this for H563 target, the H573 registers are not defined in the headers.
// Manual Base Address Definitions (From STM32H5 Reference Manual RM0481)
#define H563_AES_BASE (0x420C0000UL)

// Structural Register Offset Layout
typedef struct {
__IO uint32_t CR; // Control Register (Offset 0x00)
__IO uint32_t SR; // Status Register (Offset 0x04)
__IO uint32_t DINR; // Data Input Register (Offset 0x08)
__IO uint32_t DOUTR; // Data Output Register (Offset 0x0C)
__IO uint32_t KEYR0; // Key Register 0 (Offset 0x10)
__IO uint32_t KEYR1; // Key Register 1 (Offset 0x14)
__IO uint32_t KEYR2; // Key Register 2 (Offset 0x18)
__IO uint32_t KEYR3; // Key Register 3 (Offset 0x1C)
} H563_AES_TypeDef;

#define H563_AES ((H563_AES_TypeDef *) H563_AES_BASE)

// Manual Register Bitmasks
#define H563_RCC_AHB2ENR_AESEN (1UL << 16) // Bit 16 in RCC->AHB2ENR
#define H563_AES_CR_EN (1UL << 0) // Peripheral Enable
#define H563_AES_CR_CCFC (1UL << 7) // Computation Complete Flag Clear
#define H563_AES_SR_CCF (1UL << 0) // Computation Complete Flag
#define AES_TIMEOUT 1000UL // timeout for AES operation

bool isMCU_H573(void)
{

// Define 128-bit key and plaintext block arrays
uint32_t key[4] = {0x00010203, 0x04050607, 0x08090A0B, 0x0C0D0E0F};
uint32_t plaintext[4] = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
uint32_t ciphertext[4]= {0};
uint32_t TimeOut = AES_TIMEOUT;

// Enable the AES peripheral clock routing on the AHB2 bus
RCC->AHB2ENR |= H563_RCC_AHB2ENR_AESEN;

// Force a bus synchronization delay before accessing registers
__IO uint32_t bus_delay = RCC->AHB2ENR;
(void)bus_delay;

// Ensure peripheral is disabled before writing configuration
H563_AES->CR &= ~H563_AES_CR_EN;

// Configure for ECB Mode, 128-bit Key, Encryption, No byte swapping
H563_AES->CR = 0;

// Load the 128-bit key into the target key registers
H563_AES->KEYR0 = key[0];
H563_AES->KEYR1 = key[1];
H563_AES->KEYR2 = key[2];
H563_AES->KEYR3 = key[3];

// Enable the peripheral
H563_AES->CR |= H563_AES_CR_EN;

// Write input data. The 4th consecutive write triggers processing.
H563_AES->DINR = plaintext[0];
H563_AES->DINR = plaintext[1];
H563_AES->DINR = plaintext[2];
H563_AES->DINR = plaintext[3];

// Poll the Computation Complete Flag (CCF)
while (!(H563_AES->SR & H563_AES_SR_CCF) && (TimeOut != 0)) {
// wait for the flag, with timeout. Adjust AES_TIMEOUT value based on real performance of H573 in your setup. I did not bother, it is in us anyway :-)
TimeOut--;
}

// Clear the Computation Complete Flag for subsequent cycles
H563_AES->CR |= H563_AES_CR_CCFC;

if (TimeOut == 0)
{
// no results from AES, this is H563
return false;
}
else
{
// Read out the processed ciphertext block
ciphertext[0] = H563_AES->DOUTR;
ciphertext[1] = H563_AES->DOUTR;
ciphertext[2] = H563_AES->DOUTR;
ciphertext[3] = H563_AES->DOUTR;

// expected result is: 0x8A4433B4 0x4D961A0E 0x8FD38E55 0x45852AC1
if ((ciphertext[0] == 0x8A4433B4) && (ciphertext[1] == 0x4D961A0E) && (ciphertext[2] == 0x8FD38E55) && (ciphertext[3] == 0x45852AC1))
{
return true; // we have H573 and result is correct
}
else
{
return false; // result is not correct, assume H563
}
}
}

😎

4 replies

waclawek.jan
Super User
July 31, 2026

The ‘H563 and ‘H573 are physically identical chips; they differ only in the fact that in the ‘H563 the crypto functions are disabled.

You might perhaps be able to distinguish them by trying to set RCC_AHB2ENR.AESEN - in ‘H563 it probably won’t get set. Try.

JW

Associate
August 4, 2026

I have exactly the same issue, need to identify H563 vs H573. I tried a few methods and none of them really work.

  • HASH block exists in both chips and fully accessible, cannot use for detection.
  • H563 allows you to toggle RCC bit related to AES the same way as in H573.
  • AES control register is also accessible and I can toggle bits.

Here is an example code:

// WARNING! THIS IS NOT WORKING AS EXPECTED! DETECTION FAILS!

// Manually define register masks and offsets omitted from the H563 target headers
#define HARDWARE_RCC_AHB2ENR_AESEN (1UL << 16)
#define HARDWARE_AES_BASE (0x420C0000UL) // Base peripheral boundary for AES
#define HARDWARE_AES_CR (*(volatile uint32_t *)(HARDWARE_AES_BASE + 0x00UL))

/**
* @brief Dynamically detect if the physical chip is an H573 or H563
* by writing to the downstream peripheral config register.
* @retval true if physical silicon is STM32H573, false if STM32H563
*/
bool is_stm32h573(void)
{
// 1. Force enable the AES clock gating bit inside the RCC block
uint32_t original_rcc = RCC->AHB2ENR;
RCC->AHB2ENR |= HARDWARE_RCC_AHB2ENR_AESEN;

// Force a bus synchronization delay
__IO uint32_t bus_delay = RCC->AHB2ENR;
(void)bus_delay;

// 2. Read the initial peripheral configuration register state
uint32_t original_aes_cr = HARDWARE_AES_CR;

// 3. Attempt a scratchpad write to an execution control bit (e.g. EN bit 0)
HARDWARE_AES_CR = 0x00000001UL;

// Force data synchronization barrier
__DSB();

// 4. Read back the register to verify if the logic retained the change
uint32_t verification_aes_cr = HARDWARE_AES_CR;

// 5. Restore peripheral and clock routing states back to normal
HARDWARE_AES_CR = original_aes_cr;
RCC->AHB2ENR = original_rcc;

// If the functional register bit flipped, downstream logic exists (H573)
if (verification_aes_cr & 0x00000001UL) {
return true; // Chip is STM32H573
}

return false; // Chip is STM32H563
}

 

Zman552625Best answer
Associate
August 4, 2026

OK, this one works :-). Basically, I am trying to run full encryption cycle and check the results. On H563, the completion flag never gets set. This code probably can be shortened/simplified, but it is good enough for me…

// Since we compile this for H563 target, the H573 registers are not defined in the headers.
// Manual Base Address Definitions (From STM32H5 Reference Manual RM0481)
#define H563_AES_BASE (0x420C0000UL)

// Structural Register Offset Layout
typedef struct {
__IO uint32_t CR; // Control Register (Offset 0x00)
__IO uint32_t SR; // Status Register (Offset 0x04)
__IO uint32_t DINR; // Data Input Register (Offset 0x08)
__IO uint32_t DOUTR; // Data Output Register (Offset 0x0C)
__IO uint32_t KEYR0; // Key Register 0 (Offset 0x10)
__IO uint32_t KEYR1; // Key Register 1 (Offset 0x14)
__IO uint32_t KEYR2; // Key Register 2 (Offset 0x18)
__IO uint32_t KEYR3; // Key Register 3 (Offset 0x1C)
} H563_AES_TypeDef;

#define H563_AES ((H563_AES_TypeDef *) H563_AES_BASE)

// Manual Register Bitmasks
#define H563_RCC_AHB2ENR_AESEN (1UL << 16) // Bit 16 in RCC->AHB2ENR
#define H563_AES_CR_EN (1UL << 0) // Peripheral Enable
#define H563_AES_CR_CCFC (1UL << 7) // Computation Complete Flag Clear
#define H563_AES_SR_CCF (1UL << 0) // Computation Complete Flag
#define AES_TIMEOUT 1000UL // timeout for AES operation

bool isMCU_H573(void)
{

// Define 128-bit key and plaintext block arrays
uint32_t key[4] = {0x00010203, 0x04050607, 0x08090A0B, 0x0C0D0E0F};
uint32_t plaintext[4] = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
uint32_t ciphertext[4]= {0};
uint32_t TimeOut = AES_TIMEOUT;

// Enable the AES peripheral clock routing on the AHB2 bus
RCC->AHB2ENR |= H563_RCC_AHB2ENR_AESEN;

// Force a bus synchronization delay before accessing registers
__IO uint32_t bus_delay = RCC->AHB2ENR;
(void)bus_delay;

// Ensure peripheral is disabled before writing configuration
H563_AES->CR &= ~H563_AES_CR_EN;

// Configure for ECB Mode, 128-bit Key, Encryption, No byte swapping
H563_AES->CR = 0;

// Load the 128-bit key into the target key registers
H563_AES->KEYR0 = key[0];
H563_AES->KEYR1 = key[1];
H563_AES->KEYR2 = key[2];
H563_AES->KEYR3 = key[3];

// Enable the peripheral
H563_AES->CR |= H563_AES_CR_EN;

// Write input data. The 4th consecutive write triggers processing.
H563_AES->DINR = plaintext[0];
H563_AES->DINR = plaintext[1];
H563_AES->DINR = plaintext[2];
H563_AES->DINR = plaintext[3];

// Poll the Computation Complete Flag (CCF)
while (!(H563_AES->SR & H563_AES_SR_CCF) && (TimeOut != 0)) {
// wait for the flag, with timeout. Adjust AES_TIMEOUT value based on real performance of H573 in your setup. I did not bother, it is in us anyway :-)
TimeOut--;
}

// Clear the Computation Complete Flag for subsequent cycles
H563_AES->CR |= H563_AES_CR_CCFC;

if (TimeOut == 0)
{
// no results from AES, this is H563
return false;
}
else
{
// Read out the processed ciphertext block
ciphertext[0] = H563_AES->DOUTR;
ciphertext[1] = H563_AES->DOUTR;
ciphertext[2] = H563_AES->DOUTR;
ciphertext[3] = H563_AES->DOUTR;

// expected result is: 0x8A4433B4 0x4D961A0E 0x8FD38E55 0x45852AC1
if ((ciphertext[0] == 0x8A4433B4) && (ciphertext[1] == 0x4D961A0E) && (ciphertext[2] == 0x8FD38E55) && (ciphertext[3] == 0x45852AC1))
{
return true; // we have H573 and result is correct
}
else
{
return false; // result is not correct, assume H563
}
}
}

😎

waclawek.jan
Super User
August 4, 2026

Interesting. Don’t know why, but I was under the impression that the crypto disable is at the clock level. Thanks for letting us know.

JW