on 2024-09-10 05:00 AM
This article shows how to implement inter-processor communication between CM7 and CM4 cores on the STM32H7 series using the hardware semaphore (HSEM).
If you're looking for inter-processor communication using two SPIs or OpenAMP, we have articles on these use cases as well.
In this article, we implement a bare metal inter-processor communication via hardware semaphore (HSEM) with STM32H7 series hardware resources. In case your application needs RTOS, there is another article that details the usage of HSEM with FreeRTOS™:
The HSEM can also be a hardware resource, used to ensure synchronization between different processes running between different cores. They have the same concept as binary semaphores typically used in RTOS applications but are built on hardware level.
First, we need to create a new project on STM32CubeIDE for the NUCLEO-H745ZI-Q board. This ensures that the base hardware settings are already set. Click on [File] → [New] → [STM32 Project], go to the [Board Selector] and create the project for the Nucleo board. The initial pinout configuration uses the high and low speed clock as crystal/ceramic resonator, the user push button as GPIO input, and the board’s LEDs as GPIO output. We can disregard and manually disable the Ethernet and USB peripherals, as they are not needed or used.
To validate the code, the user’s LED is used to establish an intercommunication between both cores. The LD1 (PB0) is assigned to Arm Cortex®-M7 and the LD3 (PB14) is assigned to Arm Cortex®-M4. This can be done in the [GPIO].
The next step is enabling the HSEM NVIC interrupt for each core:
The clock configuration can be kept as default and as a recommendation. An extra configuration that could be a powerful tool to organize the code workspace is to check the option "Generate peripheral initialization as a pair of '.c/.h' files per peripheral" in Project Manager → Code Generator:
Now that the basic peripherals and settings are configured, we can generate the code and jump to programming.
The code is implemented only within the “main.c” files for both cores. Looking at the “stm32h7xx_hal_hsem.h” file, you can check the main functions to use the HSEM, such as take and release:
Another thing to mention is that the HSEM is already used by the code for boot purposes. Its ID is already defined at the "Private define" section in “main.c” and this is what we use:
Starting with the CM7, which is what leads the semaphore, add the following code lines to each section described below at the “main.c” file:
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* ############### CM7 LD1 Blinking ############### */
/* Take the semaphore as 2-step lock */
HAL_HSEM_Take(HSEM_ID_0, HSEM_PROCESSID_MIN);
/* Check if the semaphore was taken */
while(HAL_HSEM_IsSemTaken(HSEM_ID_0) == 0);
/* The LED will toggle 3 times */
for(uint8_t i = 0; i < 6; i++)
{
/* CM7 LD1 will blink every 500ms */
HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);
HAL_Delay(500);
}
/* Release the semaphore (will generate an interruption) */
HAL_HSEM_Release(HSEM_ID_0, HSEM_PROCESSID_MIN);
}
/* USER CODE END 3 */
Now, for the CM4 part, add the following code:
/* USER CODE BEGIN PV */
/* Create the flag status variable for notification */
FlagStatus uFlagNotif = RESET;
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
/* Activate the HSEM notification for Semaphore 0 only */
HAL_HSEM_ActivateNotification(__HAL_HSEM_SEMID_TO_MASK(HSEM_ID_0));
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/* ############### CM4 LD3 Blinking every notification ############### */
/* Check if the uFlagNotif was set - (Free Semaphore) */
if(uFlagNotif)
{
/* Toggle the LD3 status every notification */
HAL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin);
/* Clear the uFlagNotif */
uFlagNotif = RESET;
}
}
/* USER CODE END 3 */
/* USER CODE BEGIN 4 */
/* Callback triggered when a semaphore is released */
void HAL_HSEM_FreeCallback(uint32_t SemMask)
{
/* Reactivate the HSEM notification for Semaphore 0 */
HAL_HSEM_ActivateNotification(__HAL_HSEM_SEMID_TO_MASK(HSEM_ID_0));
uFlagNotif = SET;
}
/* USER CODE END 4 */
After adding these lines, we can position the M4 NVIC to start at FLASH BANK 2. The M7’s NVIC will be positioned at the start of FLASH BANK 1. This can be edited by uncommenting the
#define USER_VECT_TAB_ADDRESS
in the system_stm32h7xx_dualcore_boot_cm4_cm7.c.
With these additions, all the work is done.
Basically, the CM7 takes a semaphore and blink the LD1 (Green LED) three times every 500 ms before releasing it. When it is released, the CM4 receives the notification and sets the uFlagNotif, which toggles the LD3 (Red LED) status. Then, the uFlagNotif is cleared and the process loops.
At the end, you can right-click the CM7 project and go to [Run as] → [Run Configurations…] and open the [Debugger] tab. Follow the configurations below:
Scrolling down on the [Debugger] tab, make sure that the [Shared ST-LINK] box is checked:
The next thing is to configure the Startup tab. At the Load Image and Symbols section, click on [Add…] and link the CM4 Project just as below:
It should look like this:
Finally, you can check the board’s LED’s status for validation.
The example is fairly simple and showcases how to use the HSEM. You can create a shared memory region in the RAM and use the HSEM to indicate whenever there is any data ready to be used between the cores. This is covered in this article: How to use FreeRTOS message buffers on STM32H7 dual-core devices.
In case you need more details on the linker script, you can watch this video series for more details.
Specifically the part 5 will show how to create shareable sections in the linker script: How to create a super simple bootloader, Part 5: Sharing the API
Happy coding!