The STM32H7 firmware pack comes with a very nice encrypt / decrypt example.
This example uses the STM32 HAL libraries, and configures all periherals in source code.
I'm going to replicate the exact same example, using the CubeIDE MX STM32 Device Configuration Tool.
The goal is to maximise the use of MX, and to show how such a project is built.
Software versions:
- CubeIDE 1.4.2
- STM32CubeH7 Firmware Package V1.8.0
- STM32CubeMX Version: 6.0.1-RC3
Example Code
The example showcases the hardware cryptography support.
A known set of data is encrypted, and the output is then compared with the expected encryption result.
Then that encrypted data is decrypted and compared with the original.
Each time, the data should be bit-for-bit exact. If that's true, our test was successful.
The encryption and decryption run on the cryptography hardware accelerator, uses DMA to get source and write processed data.
Interrupts are used to flag that the processing is finished and data is ready.
If you want to check the original example: it's part of the STM32CubeH7 Firmware Package, called CRYP_AESModes_DMA and can be imported via the CubeIDE Information Center..
Start a Fresh Project
Enough intro, action now. In CubeIDE, start a new STM32 Project
Select either the controller or the board that we're using.
Selecting the board is easiest. Because then the clock settings will be done for us: STM32H7B3I-DK
Name the project. It's in C, executable, and STM32Cube style for full MX support.
You can choose your prefered solution here. I use reference because it generates a smaller .zip fil to add to the blog.
Let the wizard generate defaults for the drivers.
That's it. This generated a fresh project. With latest drivers and MX support.
Peripheral and Driver Setup with CubeMX
CubeIDE automatically loads the MX screen when the project is generated. Time to set up the modules.
All setup I show here is exactly mimicking what the original STM32 example does.
CPU Cache
Enable both ICache and DCache.
Here's the original code from the example. MX will generate compatible code for us.
static void CPU_CACHE_Enable(void) { /* Enable I-Cache */ SCB_EnableICache(); /* Enable D-Cache */ SCB_EnableDCache(); }
Cryptography Configuration
This is the main focus of the project.
Enable the module and set the attributes as in the picture below.
Check the original code extract at the end of this subsection for reference.
Set method, keys:
The DMA pipelines for reading and writing memory:
Interrupt settings:
Again the original example's code, for comparison:
CRYP_HandleTypeDef hcryp; /* AES Key */ uint32_t AESKey128[4] = {0x2B7E1516 ,0x28AED2A6 ,0xABF71588 ,0x09CF4F3C}; /* Set the CRYP parameters */ hcryp.Instance = CRYP; hcryp.Init.DataType = CRYP_DATATYPE_32B; hcryp.Init.KeySize = CRYP_KEYSIZE_128B; hcryp.Init.pKey = AESKey128; hcryp.Init.Algorithm = CRYP_AES_ECB; /* Configure the CRYP */ HAL_CRYP_Init(&hcryp); HAL_NVIC_SetPriority(CRYP_IRQn, 4, 0); HAL_NVIC_EnableIRQ(CRYP_IRQn); /* Enable DMA2 clocks */ __HAL_RCC_DMA2_CLK_ENABLE(); __HAL_RCC_DMA2_FORCE_RESET(); __HAL_RCC_DMA2_RELEASE_RESET(); /***************** Configure common DMA In parameters ***********************/ hdmaIn.Init.Request = DMA_REQUEST_CRYP_IN; hdmaIn.Init.Direction = DMA_MEMORY_TO_PERIPH; hdmaIn.Init.PeriphInc = DMA_PINC_DISABLE; hdmaIn.Init.MemInc = DMA_MINC_ENABLE; hdmaIn.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; hdmaIn.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; hdmaIn.Init.Mode = DMA_NORMAL; hdmaIn.Init.Priority = DMA_PRIORITY_HIGH; hdmaIn.Init.FIFOMode = DMA_FIFOMODE_ENABLE; hdmaIn.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdmaIn.Init.MemBurst = DMA_MBURST_SINGLE; hdmaIn.Init.PeriphBurst = DMA_PBURST_SINGLE; hdmaIn.Instance = DMA2_Stream6; /* Associate the DMA handle */ __HAL_LINKDMA(hcryp, hdmain, hdmaIn); /* De-initialize the Stream for new transfer */ HAL_DMA_DeInit(hcryp->hdmain); /* Configure the DMA Stream */ HAL_DMA_Init(hcryp->hdmain); /* NVIC configuration for DMA Input data interrupt */ HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn); /***************** Configure common DMA Out parameters **********************/ hdmaOut.Init.Request = DMA_REQUEST_CRYP_OUT; hdmaOut.Init.Direction = DMA_PERIPH_TO_MEMORY; hdmaOut.Init.PeriphInc = DMA_PINC_DISABLE; hdmaOut.Init.MemInc = DMA_MINC_ENABLE; hdmaOut.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; hdmaOut.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; hdmaOut.Init.Mode = DMA_NORMAL; hdmaOut.Init.Priority = DMA_PRIORITY_VERY_HIGH; hdmaOut.Init.FIFOMode = DMA_FIFOMODE_ENABLE; hdmaOut.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; hdmaOut.Init.MemBurst = DMA_MBURST_SINGLE; hdmaOut.Init.PeriphBurst = DMA_PBURST_SINGLE; hdmaOut.Instance = DMA2_Stream5; /* Associate the DMA handle */ __HAL_LINKDMA(hcryp, hdmaout, hdmaOut); /* De-initialize the Stream for new processing */ HAL_DMA_DeInit(&hdmaOut); /* Configure the DMA Stream */ HAL_DMA_Init(&hdmaOut); /* NVIC configuration for DMA output data interrupt */ HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA2_Stream5_IRQn);
DMA configuration
Nothing to do here. Our Crypto settings are automatically reflected in the DAM module.
You can click on the module and verify. No changes needed.
Interrupt Configuration
In this module, the priority and order are set ...
... and you flag what interrupt functions MX should pre-generate.
That's it. Save the configuration file and generate the code.
Source Code: the Encryption and Decryption Logic
This is exactly the main file of the original example, except the things that were auto-generated by MX.
I have used the same variable names, function names and look-and feel. To make comparison easier.
CrypCompleteDetected is a variable that is set high in the Crypt interrupt handler, when the work is done.
It is set to 0 before we perform a cryptographic function. The Crypt module and DMA will invoke the interrupt handler when the results are in memory.
__IO is STM32's way to flag it volatile.
// for memory comparison function memcmp() #include "string.h" /* USER CODE BEGIN PV */ __IO uint32_t CrypCompleteDetected = 0; /* Plaintext */ uint32_t Plaintext[16] = {0x6BC1BEE2 ,0x2E409F96 ,0xE93D7E11 ,0x7393172A , 0xAE2D8A57 ,0x1E03AC9C ,0x9EB76FAC ,0x45AF8E51 , 0x30C81C46 ,0xA35CE411 ,0xE5FBC119 ,0x1A0A52EF , 0xF69F2445 ,0xDF4F9B17 ,0xAD2B417B ,0xE66C3710}; /* Expected ECB Ciphertext with DataType 32 ( no swapping)*/ uint32_t Ciphertext[16] = {0x3AD77BB4 ,0x0D7A3660 ,0xA89ECAF3 ,0x2466EF97 , 0xF5D3D585 ,0x03B9699D ,0xE785895A ,0x96FDBAAF , 0x43B1CD7F ,0x598ECE23 ,0x881B00E3 ,0xED030688 , 0x7B0C785E ,0x27E8AD3F ,0x82232071 ,0x04725DD4}; /* Plaintext byte swapping (DataType 8)*/ uint32_t Plaintext_8[16] = {0xE2BEC16B ,0x969F402E ,0x117E3DE9 ,0x2A179373 , 0x578A2DAE ,0x9CAC031E ,0xAC6FB79E ,0x518EAF45 , 0x461CC830 ,0x11E45CA3 ,0x19C1FBE5 ,0xEF520A1A , 0x45249FF6 ,0x179B4FDF ,0x7B412BAD ,0x10376CE6}; /* Expected ECB Ciphertext with DataType 8 */ uint32_t Ciphertext_8[16] = {0xB47BD73A ,0x60367A0D ,0xF3CA9EA8 ,0x97EF6624 , 0x85D5D3F5 ,0x9D69B903 ,0x5A8985E7 ,0xAFBAFD96 , 0x7FCDB143 ,0x23CE8E59 ,0xE3001B88 ,0x880603ED , 0x5E780C7B ,0x3FADE827 ,0x71202382 ,0xD45D7204}; /* Plaintext half-word swapping (DataType 16) */ uint32_t Plaintext_16[16] = {0xBEE26BC1 ,0x9F962E40 ,0x7E11E93D ,0x172A7393 , 0x8A57AE2D ,0xAC9C1E03 ,0x6FAC9EB7 ,0x8E5145AF , 0x1C4630C8 ,0xE411A35C ,0xC119E5FB ,0x52EF1A0A , 0x2445F69F ,0x9B17DF4F ,0x417BAD2B ,0x3710E66C}; /* Expected ECB Ciphertext with DataType 16 */ uint32_t Ciphertext_16[16] = {0x7BB43AD7 ,0x36600D7A ,0xCAF3A89E ,0xEF972466 , 0xD585F5D3 ,0x699D03B9 ,0x895AE785 ,0xBAAF96FD , 0xCD7F43B1 ,0xCE23598E ,0x00E3881B ,0x0688ED03 , 0x785E7B0C ,0xAD3F27E8 ,0x20718223 ,0x5DD40472}; /* Plaintext bit swapping (DataType 1) */ uint32_t Plaintext_1[16] = {0x477D83D6 ,0x69F90274 ,0x887EBC97 ,0x54E8C9CE , 0xEA51B475 ,0x3935C078 ,0x35F6ED79 ,0x8A71F5A2 , 0x6238130C ,0x88273AC5 ,0x9883DFA7 ,0xF74A5058, 0xA224F96F ,0xE8D9F2FB ,0xDE82D4B5 ,0x08EC3667}; /* Expected ECB Ciphertext with DataType 1 */ uint32_t Ciphertext_1[16] = {0x2DDEEB5C, 0x066C5EB0, 0xCF537915 ,0xE9F76624 , 0xA1ABCBAF, 0xB9969DC0 ,0x5A91A1E7 ,0xF55DBF69 , 0xFEB38DC2 , 0xC473719A ,0xC700D811 ,0x1160C0B7 , 0x7A1E30DE , 0xFCB517E4 ,0x8E04C441 ,0x2BBA4E20}; /* Used for storing Encrypted text */ ALIGN_32BYTES (static uint32_t Encryptedtext[16])={0}; /* Used for storing Decrypted text */ ALIGN_32BYTES (static uint32_t Decryptedtext[16])={0}; /* USER CODE END PV */
Here is that interrupt handler. The name is critical, because if you have this wrong, the default handler will be called. Without compile or runtime errors.
Be careful.
void HAL_CRYP_OutCpltCallback(CRYP_HandleTypeDef *hcryp) { CrypCompleteDetected = 1; }
And this is the custom part of the main code. Our logic, where we let the Crypt module encrypt and decrypt data.
It's very small. That's because all the big work was generated by MX.
The code is exactly the same as in the original example.
int main(void) { // ... /* USER CODE BEGIN 2 */ /* Encryption, result in Encryptedtext buffer */ HAL_CRYP_Encrypt_DMA(&hcryp, Plaintext, 16, Encryptedtext); /*Wait until output transfer complete*/ while(CrypCompleteDetected == 0) { } /*Compare with expected result */ if(memcmp(Encryptedtext, Ciphertext, 64) != 0) { /* Not expected result, wrong on Encryptedtext buffer: Turn LED3 on */ Error_Handler(); } /* Reset Output Transfer Complete Detect */ CrypCompleteDetected = 0; /* Decryption, result in Decryptedtext buffer */ HAL_CRYP_Decrypt_DMA(&hcryp, Ciphertext , 16, Decryptedtext); /*Wait until output transfer complete*/ while(CrypCompleteDetected == 0) { } /*Compare with expected result */ if(memcmp(Decryptedtext, Plaintext, 64) != 0) { /* Not expected result, wrong on Decryptedtext buffer: Turn LED3 on */ Error_Handler(); } /* Reset Output Transfer Complete Detect */ CrypCompleteDetected = 0; // original code repeats this a few time, for other crypto algorithms /* USER CODE END 2 */ // ... }
Compile and Run
Set the dedicated load script for this board.
Optimisation
When I opted to let MX generate a project for this board, it configured and enabled a number of peripherals that aren't needed for this project.
I selected those and set them to disabled. In the end, these essential resources were kept active:
If you see more resources in this tab in your project, go to the unneeded resources and disable them.
This does not change the logic. It gives smaller code and less init time. I have not checked if power consumption is affected.
I hope that this post explains how to set up peripherals, drivers and APIs with the CubeIDE MX STM32 Device Configuration Tool.
Enjoy.
Top Comments