]> git.sur5r.net Git - freertos/blobdiff - FreeRTOS/Demo/CORTEX_M7_M4_AMP_STM32H745I_Discovery_IAR/CM7/main.c
Add M7/M4 AMP demo.
[freertos] / FreeRTOS / Demo / CORTEX_M7_M4_AMP_STM32H745I_Discovery_IAR / CM7 / main.c
diff --git a/FreeRTOS/Demo/CORTEX_M7_M4_AMP_STM32H745I_Discovery_IAR/CM7/main.c b/FreeRTOS/Demo/CORTEX_M7_M4_AMP_STM32H745I_Discovery_IAR/CM7/main.c
new file mode 100644 (file)
index 0000000..87b41ee
--- /dev/null
@@ -0,0 +1,500 @@
+/*\r
+ * FreeRTOS Kernel V10.2.0\r
+ * Copyright (C) 2019 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
+ * this software and associated documentation files (the "Software"), to deal in\r
+ * the Software without restriction, including without limitation the rights to\r
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
+ * the Software, and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included in all\r
+ * copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ *\r
+ * http://www.FreeRTOS.org\r
+ * http://aws.amazon.com/freertos\r
+ *\r
+ * 1 tab == 4 spaces!\r
+ */\r
+\r
+/*\r
+ * See https://www.freertos.org/STM32H7_Dual_Core_AMP_RTOS_demo.html for usage\r
+ * instructions (TBD, not available at the time of writing).\r
+ *\r
+ * Behavior\r
+ * --------\r
+ *\r
+ * This example stress tests a simple Asymmetric Multi Processing (AMP) core to\r
+ * core communication mechanism implemented using FreeRTOS message buffers:\r
+ * https://www.freertos.org/RTOS-stream-message-buffers.html  Message buffers\r
+ * are used to pass an ASCII representation of an incrementing number (so "0",\r
+ * followed by "1", followed by "2", etc.) from a single 'sending' task that\r
+ * runs on the Arm Cortex-M7 core (the M7 core) to two "receiving" tasks\r
+ * running on the Arm Cortex-M4 core (the M4 core).  There are two data message\r
+ * buffers, one for each receiving task.  To distinguish between the receiving\r
+ * tasks one is assigned the task number 0, and the other task number 1.\r
+ *\r
+ * The M7 task sits in a loop sending the ascii strings to each M4 task.  If a\r
+ * receiving task receives the next expected value in the sequence it prints its\r
+ * task number to the UART.  If a receiving task receives anything else, or its\r
+ * attempt to receive data times out, then it hits an assert() that prints an\r
+ * error message to the UART before stopping all further processing on the M4\r
+ * core.  If the example is running correctly you will see lots of "0"s (from\r
+ * the receiving task assigned task number 0) and "1"s (from the receiving task\r
+ * assigned task number 1) streaming from the UART.  The time taken to output\r
+ * characters from the UART is the only thing throttling the speed of the core\r
+ * to core communication as it causes the message buffers to become full - which\r
+ * would probably happen anyway as the M7 core is executing at twice the\r
+ * frequency of the M4 core.\r
+ *\r
+ *\r
+ * Implementation of sbSEND_COMPLETED()\r
+ * ------------------------------------\r
+ *\r
+ * sbSEND_COMPLETED is a macro called by FreeRTOS after data has been sent to a\r
+ * message buffer in case there was a task blocked on the message buffer waiting\r
+ * for data to become available - in which case the waiting task would be\r
+ * unblocked:  https://www.freertos.org/RTOS-message-buffer-example.html\r
+ * However, the default sbSEND_COMPLETED implementation assumes the sending task\r
+ * (or interrupt) and the receiving task are under the control of the same\r
+ * instance of the FreeRTOS kernel and run on the same MCU core.  In this AMP\r
+ * example the sending task and the receiving tasks are under the control of two\r
+ * different instances of the FreeRTOS kernel, and run on different MCU cores,\r
+ * so the default sbSEND_COMPLETED implementation won't work (each FreeRTOS\r
+ * kernel instance only knowns about the tasks under its control).  AMP\r
+ * scenarios therefore require the sbSEND_COMPLETED macro (and potentially the\r
+ * sbRECEIVE_COMPLETED macro, see below) to be overridden, which is done by\r
+ * simply providing your own implementation in the project's FreeRTOSConfig.h\r
+ * file.  Note this example has a FreeRTOSConfig.h file used by the application\r
+ * that runs on the M7 core and a separate FreeRTOSConfig.h file used by the\r
+ * application that runs on the M4 core.  The implementation of sbSEND_COMPLETED\r
+ * used by the M7 core simply triggers an interrupt in the M4 core.  The\r
+ * interrupt's handler (the ISR that was triggered by the M7 core but executes\r
+ * on the M4 core) must then do the job that would otherwise be done by the\r
+ * default implementation of sbSEND_COMPLETE - namely unblock a task if the task\r
+ * was waiting to receive data from the message buffer that now contains data.\r
+ * There are two data message buffers though, so first ISR must determine which\r
+ * of the two contains data.\r
+ *\r
+ * This demo only has two data message buffers, so it would be reasonable to\r
+ * have the ISR simply query both to see which contained data, but that solution\r
+ * would not scale if there are many message buffers, or if the number of\r
+ * message buffers was unknown.  Therefore, to demonstrate a more scalable\r
+ * solution, this example introduced a third message buffer - a 'control'\r
+ * message buffer as opposed to a 'data' message buffer.  After the task on the\r
+ * M7 core writes to a data message buffer it writes the handle of the message\r
+ * buffer that contains data to the control message buffer.  The ISR running on\r
+ * the M4 core then reads from the control message buffer to know which data\r
+ * message buffer contains data.\r
+ *\r
+ * The above described scenario contains many implementation decisions.\r
+ * Alternative methods of enabling the M4 core to know data message buffer\r
+ * contains data include:\r
+ *\r
+ *  1) Using a different interrupt for each data message buffer.\r
+ *  2) Passing all data from the M7 core to the M4 core through a single message\r
+ *     buffer, along with additional data that tells the ISR running on the M4\r
+ *     core which task to forward the data to.\r
+ *\r
+ *\r
+ * Implementation of sbRECEIVE_COMPLETED()\r
+ * ---------------------------------------\r
+ *\r
+ * sbRECEIVE_COMPLETED is the complement of sbSEND_COMPLETED.  It is a macro\r
+ * called by FreeRTOS after data has been read from a message buffer in case\r
+ * there was a task blocked on the message buffer waiting for space to become\r
+ * available - in which case the waiting task would be unblocked so it can\r
+ * complete its write to the buffer.\r
+ *\r
+ * In this example the M7 task writes to the message buffers faster than the M4\r
+ * tasks read from them (in part because the M7 is running faster, and in part\r
+ * because the M4 cores write to the UART), so the buffers become full, and the\r
+ * M7 task enters the Blocked state to wait for space to become available.  As\r
+ * with the sbSEND_COMPLETED macro, the default implementation of the\r
+ * sbRECEIVE_COMPLETED macro only works if the sender and receiver are under the\r
+ * control of the same instance of FreeRTOS and execute on the same core.\r
+ * Therefore, just as the application that executes on the M7 core overrides\r
+ * the default implementation of sbSEND_SOMPLETED(), the application that runs\r
+ * on the M4 core overrides the default implementation of sbRECEIVE_COMPLETED()\r
+ * to likewise generate an interrupt in the M7 core - so sbRECEIVE_COMPLETED()\r
+ * executes on the M4 core and generates an interrupt on the M7 core.  To keep\r
+ * things simple the ISR that runs on the M7 core does not use a control\r
+ * message buffer to know which data message buffer contains space, and instead\r
+ * simply sends a notification to both data message buffers.  Note however that\r
+ * this overly simplistic implementation is only acceptable because it is\r
+ * known that there is only one sending task, and that task cannot be blocked on\r
+ * both message buffers at the same time.  Also, sending the notification to the\r
+ * data message buffer updates the receiving task's direct to task notification\r
+ * state: https://www.freertos.org/RTOS-task-notifications.html which is only ok\r
+ * because it is known the task is not using its notification state for any\r
+ * other purpose.\r
+ *\r
+ */\r
+\r
+/* Standard includes. */\r
+#include "stdio.h"\r
+#include "string.h"\r
+\r
+/* FreeRTOS includes. */\r
+#include "FreeRTOS.h"\r
+#include "task.h"\r
+#include "message_buffer.h"\r
+#include "MessageBufferLocations.h"\r
+\r
+/* ST includes. */\r
+#include "stm32h7xx_hal.h"\r
+#include "stm32h745i_discovery.h"\r
+\r
+/* When the cores boot they very crudely wait for each other in a non chip\r
+specific way by waiting for the other core to start incrementing a shared\r
+variable within an array.  mainINDEX_TO_TEST sets the index within the array to\r
+the variable this core tests to see if it is incrementing, and\r
+mainINDEX_TO_INCREMENT sets the index within the array to the variable this core\r
+increments to indicate to the other core that it is at the sync point.  Note\r
+this is not a foolproof method and it is better to use a hardware specific\r
+solution, such as having one core boot the other core when it was ready, or\r
+using some kind of shared semaphore or interrupt. */\r
+#define mainINDEX_TO_TEST              0\r
+#define mainINDEX_TO_INCREMENT 1\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/*\r
+ * Implements the task that sends messages to the M7 core.\r
+ */\r
+static void prvM7CoreTasks( void *pvParameters );\r
+\r
+/*\r
+ * configSUPPORT_STATIC_ALLOCATION is set to 1, requiring this callback to\r
+ * provide statically allocated data for use by the idle task, which is a task\r
+ * created by the scheduler when it starts.\r
+ */\r
+void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, uint32_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );\r
+\r
+/*\r
+ * Just waits to see a variable being incremented by the M4 core to know when\r
+ * the M4 has created the message buffers used for core to core communication.\r
+ */\r
+static void prvWaitForOtherCoreToStart( uint32_t ulIndexToTest, uint32_t ulIndexToIncrement );\r
+\r
+/*\r
+ * Setup the hardware ready to run this demo.\r
+ */\r
+static void prvSetupHardware( void );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+static TaskHandle_t xM7AMPTask = NULL;\r
+\r
+int main( void )\r
+{\r
+BaseType_t x;\r
+\r
+       /*** See the comments at the top of this page ***/\r
+\r
+       prvSetupHardware();\r
+\r
+       /* Create the control and data message buffers, as described at the top of\r
+       this file.  The message buffers are statically allocated at a known location\r
+       as both cores need to know where they are.  See MessageBufferLocations.h. */\r
+       xControlMessageBuffer = xMessageBufferCreateStatic( /* The buffer size in bytes. */\r
+                                                                                                               mbaCONTROL_MESSAGE_BUFFER_SIZE,\r
+                                                                                                               /* Statically allocated buffer storage area. */\r
+                                                                                                               ucControlBufferStorage,\r
+                                                                                                               /* Message buffer handle. */\r
+                                                                                                               &xControlMessageBufferStruct );\r
+       for( x = 0; x < mbaNUMBER_OF_CORE_2_TASKS; x++ )\r
+       {\r
+               xDataMessageBuffers[ x ] = xMessageBufferCreateStatic( mbaTASK_MESSAGE_BUFFER_SIZE,\r
+                                                                                                                          &( ucDataBufferStorage[ x ][ 0 ] ),\r
+                                                                                                                          &( xDataMessageBufferStructs[ x ] ) );\r
+       }\r
+\r
+       /* The message buffers have been initialised so it is safe for both cores to\r
+       synchronise their startup. */\r
+       prvWaitForOtherCoreToStart( mainINDEX_TO_TEST, mainINDEX_TO_INCREMENT );\r
+\r
+       /* Start the task that executes on the M7 core. */\r
+       xTaskCreate( prvM7CoreTasks,                    /* Function that implements the task. */\r
+                                "AMPM7Core",                           /* Task name, for debugging only. */\r
+                                configMINIMAL_STACK_SIZE,  /* Size of stack (in words) to allocate for this task. */\r
+                                NULL,                                          /* Task parameter, not used in this case. */\r
+                                tskIDLE_PRIORITY,                      /* Task priority. */\r
+                                &xM7AMPTask );                         /* Task handle, used to unblock task from interrupt. */\r
+\r
+       /* Start scheduler */\r
+       vTaskStartScheduler();\r
+\r
+       /* Will not get here if the scheduler starts successfully.  If you do end up\r
+       here then there wasn't enough heap memory available to start either the idle\r
+       task or the timer/daemon task.  https://www.freertos.org/a00111.html */\r
+       for( ;; );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvM7CoreTasks( void *pvParameters )\r
+{\r
+BaseType_t x;\r
+uint32_t ulNextValue = 0;\r
+const TickType_t xDelay = pdMS_TO_TICKS( 25 );\r
+char cString[ 15 ];\r
+size_t xStringLength;\r
+\r
+       /* Remove warning about unused parameters. */\r
+       ( void ) pvParameters;\r
+\r
+       for( ;; )\r
+       {\r
+               /* Create the next string to send.  The value is incremented on each\r
+               loop iteration, and the length of the string changes as the number of\r
+               digits in the value increases. */\r
+               sprintf( cString, "%lu", ( unsigned long ) ulNextValue );\r
+               xStringLength = strlen( cString );\r
+\r
+               /* This task runs on the M7 core, use the message buffers to send the\r
+               strings to the tasks running on the M4 core.  This will result in\r
+               sbSEND_COMPLETED() being executed, which in turn will write the handle\r
+               of the message buffer written to into xControlMessageBuffer then\r
+               generate an interrupt in M4 core. */\r
+               for( x = 0; x < mbaNUMBER_OF_CORE_2_TASKS; x++ )\r
+               {\r
+                       while( xMessageBufferSend(      xDataMessageBuffers[ x ],\r
+                                                                               ( void * ) cString,\r
+                                                                               xStringLength,\r
+                                                                               portMAX_DELAY ) != xStringLength );\r
+\r
+                       /* Delay before repeating */\r
+//                     vTaskDelay( xDelay );\r
+               }\r
+\r
+               ulNextValue++;\r
+       }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vGenerateM7ToM4Interrupt( void * xUpdatedMessageBuffer )\r
+{\r
+MessageBufferHandle_t xUpdatedBuffer = ( MessageBufferHandle_t ) xUpdatedMessageBuffer;\r
+\r
+       /* Called by the implementation of sbSEND_COMPLETED() in FreeRTOSConfig.h.\r
+       See the comments at the top of this file.  Write the handle of the data\r
+       message buffer to which data was written to the control message buffer. */\r
+       if( xUpdatedBuffer != xControlMessageBuffer )\r
+       {\r
+               while( xMessageBufferSend( xControlMessageBuffer, &xUpdatedBuffer, sizeof( xUpdatedBuffer ), mbaDONT_BLOCK ) != sizeof( xUpdatedBuffer ) )\r
+               {\r
+                       /* Nothing to do here.  Note it is very bad to loop in an interrupt\r
+                       service routine.  If a loop is really required then defer the\r
+                       routine to a task. */\r
+               }\r
+\r
+               /* Generate interrupt in the M4 core. */\r
+               HAL_EXTI_D1_EventInputConfig( EXTI_LINE0, EXTI_MODE_IT, DISABLE );\r
+               HAL_EXTI_D2_EventInputConfig( EXTI_LINE0, EXTI_MODE_IT, ENABLE );\r
+               HAL_EXTI_GenerateSWInterrupt( EXTI_LINE0 );\r
+       }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, uint32_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )\r
+{\r
+/* If the buffers to be provided to the Idle task are declared inside this\r
+function then they must be declared static - otherwise they will be allocated on\r
+the stack and so not exists after this function exits. */\r
+static StaticTask_t xIdleTaskTCB;\r
+static uint32_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];\r
+\r
+       /* configUSE_STATIC_ALLOCATION is set to 1, so the application must provide\r
+       an implementation of vApplicationGetIdleTaskMemory() to provide the memory\r
+       that is used by the Idle task.\r
+       https://www.freertos.org/a00110.html#configSUPPORT_STATIC_ALLOCATION */\r
+\r
+       /* Pass out a pointer to the StaticTask_t structure in which the Idle task's\r
+       state will be stored. */\r
+       *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;\r
+\r
+       /* Pass out the array that will be used as the Idle task's stack. */\r
+       *ppxIdleTaskStackBuffer = uxIdleTaskStack;\r
+\r
+       /* Pass out the size of the array pointed to by *ppxIdleTaskStackBuffer.\r
+       Note that, as the array is necessarily of type StackType_t,\r
+       configMINIMAL_STACK_SIZE is specified in words, not bytes. */\r
+       *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvWaitForOtherCoreToStart( uint32_t ulIndexToTest, uint32_t ulIndexToIncrement )\r
+{\r
+volatile uint32_t ulInitialCount = ulStartSyncCounters[ ulIndexToTest ], x;\r
+const uint32_t ulCrudeLoopDelay = 0xfffffUL;\r
+\r
+       /* When the cores boot they very crudely wait for each other in a non chip\r
+       specific way by waiting for the other core to start incrementing a shared\r
+       variable within an array.  mainINDEX_TO_TEST sets the index within the array\r
+       to the variable this core tests to see if it is incrementing, and\r
+       mainINDEX_TO_INCREMENT sets the index within the array to the variable this\r
+       core increments to indicate to the other core that it is at the sync point.\r
+       Note this is not a foolproof method and it is better to use a hardware\r
+       specific solution, such as having one core boot the other core when it was\r
+       ready, or using some kind of shared semaphore or interrupt. */\r
+\r
+       /* Wait for the other core to reach the synchronisation point. */\r
+       while( ulStartSyncCounters[ ulIndexToTest ] == ulInitialCount );\r
+       ulInitialCount = ulStartSyncCounters[ ulIndexToTest ];\r
+\r
+       for( ;; )\r
+       {\r
+               ulStartSyncCounters[ ulIndexToIncrement ]++;\r
+               if( ulStartSyncCounters[ ulIndexToTest ] != ulInitialCount )\r
+               {\r
+                       ulStartSyncCounters[ ulIndexToIncrement ]++;\r
+                       break;\r
+               }\r
+\r
+               /* Unlike the M4 core, this core does not have direct access to the UART,\r
+               so simply toggle an LED to show its status. */\r
+               for( x = 0; x < ulCrudeLoopDelay; x++ ) __asm volatile( "NOP" );\r
+               BSP_LED_Off( LED2 );\r
+               for( x = 0; x < ulCrudeLoopDelay; x++ ) __asm volatile( "NOP" );\r
+               BSP_LED_On( LED2 );\r
+       }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void HAL_GPIO_EXTI_Callback( uint16_t GPIO_Pin )\r
+{\r
+BaseType_t xHigherPriorityTaskWoken = pdFALSE;\r
+uint32_t x;\r
+\r
+       configASSERT( xM7AMPTask );\r
+\r
+       HAL_EXTI_D1_ClearFlag( EXTI_LINE1 );\r
+\r
+       /* Task can't be blocked on both so just send the notification to both. */\r
+       for( x = 0; x < mbaNUMBER_OF_CORE_2_TASKS; x++ )\r
+       {\r
+               xMessageBufferReceiveCompletedFromISR( xDataMessageBuffers[ x ], &xHigherPriorityTaskWoken );\r
+       }\r
+\r
+       /* Normal FreeRTOS "yield from interrupt" semantics, where\r
+       xHigherPriorityTaskWoken is initialzed to pdFALSE and will then get set to\r
+       pdTRUE if the interrupt unblocks a task that has a priority above that of\r
+       the currently executing task. */\r
+       portYIELD_FROM_ISR( xHigherPriorityTaskWoken );\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvSetupHardware( void )\r
+{\r
+MPU_Region_InitTypeDef MPU_InitStruct;\r
+RCC_ClkInitTypeDef RCC_ClkInitStruct;\r
+RCC_OscInitTypeDef RCC_OscInitStruct;\r
+\r
+       /* Configure the MPU attributes as Not Cachable for Internal D3SRAM.  The\r
+       Base Address is 0x38000000 (D3_SRAM_BASE), and the size is 64K. */\r
+       HAL_MPU_Disable();\r
+       MPU_InitStruct.Enable = MPU_REGION_ENABLE;\r
+       MPU_InitStruct.BaseAddress = D3_SRAM_BASE;\r
+       MPU_InitStruct.Size = MPU_REGION_SIZE_64KB;\r
+       MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;\r
+       MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;\r
+       MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;\r
+       MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;\r
+       MPU_InitStruct.Number = MPU_REGION_NUMBER0;\r
+       MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;\r
+       MPU_InitStruct.SubRegionDisable = 0x00;\r
+       MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;\r
+       HAL_MPU_ConfigRegion(&MPU_InitStruct);\r
+       HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);\r
+\r
+       /* Enable I-Cache */\r
+       SCB_EnableICache();\r
+\r
+       /* Enable D-Cache */\r
+       SCB_EnableDCache();\r
+\r
+       HAL_Init();\r
+       BSP_LED_Init(LED1);\r
+\r
+\r
+       /*\r
+       System Clock Configuration:\r
+               System Clock source    = PLL (HSE)\r
+               SYSCLK(Hz)             = 400000000 (Cortex-M7 CPU Clock)\r
+               HCLK(Hz)               = 200000000 (Cortex-M4 CPU, Bus matrix Clocks)\r
+               AHB Prescaler          = 2\r
+               D1 APB3 Prescaler      = 2 (APB3 Clock  100MHz)\r
+               D2 APB1 Prescaler      = 2 (APB1 Clock  100MHz)\r
+               D2 APB2 Prescaler      = 2 (APB2 Clock  100MHz)\r
+               D3 APB4 Prescaler      = 2 (APB4 Clock  100MHz)\r
+               HSE Frequency(Hz)      = 25000000\r
+               PLL_M                  = 5\r
+               PLL_N                  = 160\r
+               PLL_P                  = 2\r
+               PLL_Q                  = 4\r
+               PLL_R                  = 2\r
+               VDD(V)                 = 3.3\r
+               Flash Latency(WS)      = 4\r
+       */\r
+\r
+       HAL_PWREx_ConfigSupply(PWR_DIRECT_SMPS_SUPPLY);\r
+\r
+       /* The voltage scaling allows optimizing the power consumption when the\r
+       device is clocked below the maximum system frequency, to update the voltage\r
+       scaling value regarding system frequency refer to product datasheet. */\r
+       __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);\r
+\r
+       while( !__HAL_PWR_GET_FLAG( PWR_FLAG_VOSRDY ) )\r
+       {\r
+               __asm volatile ( "NOP" );\r
+       }\r
+\r
+       /* Enable HSE Oscillator and activate PLL with HSE as source */\r
+       RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;\r
+       RCC_OscInitStruct.HSEState = RCC_HSE_ON;\r
+       RCC_OscInitStruct.HSIState = RCC_HSI_OFF;\r
+       RCC_OscInitStruct.CSIState = RCC_CSI_OFF;\r
+       RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;\r
+       RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;\r
+\r
+       RCC_OscInitStruct.PLL.PLLM = 5;\r
+       RCC_OscInitStruct.PLL.PLLN = 160;\r
+       RCC_OscInitStruct.PLL.PLLFRACN = 0;\r
+       RCC_OscInitStruct.PLL.PLLP = 2;\r
+       RCC_OscInitStruct.PLL.PLLR = 2;\r
+       RCC_OscInitStruct.PLL.PLLQ = 4;\r
+\r
+       RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;\r
+       RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;\r
+       configASSERT( HAL_RCC_OscConfig( &RCC_OscInitStruct ) == HAL_OK );\r
+\r
+       /* Select PLL as system clock source and configure  bus clocks dividers */\r
+       RCC_ClkInitStruct.ClockType = ( RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_D1PCLK1 | RCC_CLOCKTYPE_PCLK1 | \\r
+                                                                   RCC_CLOCKTYPE_PCLK2  | RCC_CLOCKTYPE_D3PCLK1 );\r
+\r
+       RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;\r
+       RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;\r
+       RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;\r
+       RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;\r
+       RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;\r
+       RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;\r
+       RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;\r
+       configASSERT( HAL_RCC_ClockConfig( &RCC_ClkInitStruct, FLASH_LATENCY_4 ) == HAL_OK );\r
+\r
+       /* AIEC Common configuration: make CPU1 and CPU2 SWI line0 sensitive to\r
+       rising edge. */\r
+       HAL_EXTI_EdgeConfig( EXTI_LINE0, EXTI_RISING_EDGE );\r
+\r
+       /* Interrupt used for M4 to M7 notifications. */\r
+       HAL_NVIC_SetPriority( EXTI1_IRQn, 0xFU, 0U );\r
+       HAL_NVIC_EnableIRQ( EXTI1_IRQn );\r
+}\r
+\r