{"id":5185,"date":"2019-09-04T18:06:28","date_gmt":"2019-09-05T01:06:28","guid":{"rendered":"https:\/\/visualgdb.com\/w\/?p=5185"},"modified":"2019-09-04T18:06:28","modified_gmt":"2019-09-05T01:06:28","slug":"using-openamp-for-cross-core-communication-on-stm32mp1","status":"publish","type":"post","link":"https:\/\/visualgdb.com\/tutorials\/arm\/stm32\/stm32mp1\/openamp\/","title":{"rendered":"Using OpenAMP for Cross-core Communication on STM32MP1"},"content":{"rendered":"<p>This tutorial shows how to use the <a href=\"https:\/\/github.com\/OpenAMP\/open-amp\">OpenAMP<\/a> library to communicate between multiple cores of the STM32MP1 device. We will start with a basic project that creates a virtual COM port that can be used to send data between the Linux running on the Cortex-A core and the embedded firmware running on the Cortex-M4 core. We will then modify the firmware to respond to basic commands sent from the Linux side.<\/p>\n<p>Before you begin, install VisualGDB 5.4R11 or later.<\/p>\n<ol>\n<li>Start Visual Studio and select the VisualGDB Embedded Project Wizard:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/01-newprj-3.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5165\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/01-newprj-3.png\" alt=\"\" width=\"1024\" height=\"710\" \/><\/a><\/li>\n<li>Enter the name and pick the location of the project that will be created by VisualGDB:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/01-prjname-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5186\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/01-prjname-1.png\" alt=\"\" width=\"1024\" height=\"710\" \/><\/a><\/li>\n<li>Proceed with the default settings (Embedded binary -&gt; MSBuild) on the first page of the wizard:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/02-newprj.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5187\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/02-newprj.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>On the next page of the wizard select the ARM toolchain and pick the STM32MP1 device that is installed on your development board:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/03-device.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5188\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/03-device.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>On the Sample Selection page switch to the &#8220;STM32 CubeMX Samples&#8221; view and pick the <strong>OpenAMP_TTY_echo<\/strong> sample:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/04-echo.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5189\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/04-echo.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>Ensure your board is in the production mode (i.e. boots Linux from the SD card) and connect power, Ethernet and ST-Link (see <a href=\"https:\/\/visualgdb.com\/tutorials\/arm\/stm32\/stm32mp1\">this tutorial<\/a> for more details), then let VisualGDB detect the on-board ST-Link and ensure <strong>STM32MP1xx (with PMIC)<\/strong> is selected as the debugged device:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/05-debug.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5190\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/05-debug.png\" alt=\"\" width=\"886\" height=\"693\" \/><\/a><\/li>\n<li>Press the &#8220;Test&#8221; button to verify the JTAG connectivity:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/06-test.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5191\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/06-test.png\" alt=\"\" width=\"786\" height=\"593\" \/><\/a><\/li>\n<li>Finally, press &#8220;Finish&#8221; to create the project. VisualGDB will clone the <strong>OpenAMP_TTY_Echo<\/strong> example so that you will be able to build and debug it. Build it via Build-&gt;Build Solution and check the outgoing calls from main() to quickly get a list of virtual UART-related functions provided by OpenAMP:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/07-uart.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5192\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/07-uart.png\" alt=\"\" width=\"1238\" height=\"863\" \/><\/a><\/li>\n<li>Sending and receiving packets from Linux running on the Cortex-A core involves calling the following functions from the Cortex-M4 firmware:\n<ol>\n<li><strong>MX_IPCC_Init()<\/strong> needs to be called in order to initialize the inter-core communication hardware.<\/li>\n<li><strong>MX_OPENAMP_Init()<\/strong> initializes the OpenAMP library.<\/li>\n<li><strong>VIRT_UART_Init()<\/strong> creates a new virtual UART port (available as <strong>\/dev\/ttyRPMSGx<\/strong> on the Linux side)<\/li>\n<li><strong>VIRT_UART_RegisterCallback()<\/strong> registers a callback that will be invoked by OpenAMP when Linux-side code sends a message via the virtual UART port.<\/li>\n<li><strong>VIRT_UART_Transmit()<\/strong> sends a message back to the Linux side.<\/li>\n<li><strong>OPENAMP_check_for_message()<\/strong> needs to be called from the main loop in order to poll for the incoming messages and call the previously registered RX callbacks.<\/li>\n<\/ol>\n<p>&nbsp;<\/li>\n<li>Before you can begin debugging the program, it needs to be registered with the Linux system by uploading it to <strong>\/lib\/firmware<\/strong> and writing to virtual files under <strong>\/sys\/class\/remoteproc<\/strong>. Follow <a href=\"https:\/\/visualgdb.com\/tutorials\/arm\/stm32\/stm32mp1\">this tutorial<\/a> to to understand the necessary steps or simply paste the following custom actions to custom debug steps before launching the debugger (requires the Custom edition of VisualGDB):\n<pre class=\"\">&lt;?xml version=\"1.0\" encoding=\"utf-16\"?&gt;\r\n&lt;ArrayOfCustomActionBase xmlns:xsi=\"http:\/\/www.w3.org\/2001\/XMLSchema-instance\" xmlns:xsd=\"http:\/\/www.w3.org\/2001\/XMLSchema\"&gt;\r\n  &lt;CustomActionBase xsi:type=\"CommandLineAction\"&gt;\r\n    &lt;SkipWhenRunningCommandList&gt;false&lt;\/SkipWhenRunningCommandList&gt;\r\n    &lt;RemoteHost&gt;\r\n      &lt;HostName&gt;stm32mp1&lt;\/HostName&gt;\r\n      &lt;Transport&gt;SSH&lt;\/Transport&gt;\r\n      &lt;UserName&gt;root&lt;\/UserName&gt;\r\n    &lt;\/RemoteHost&gt;\r\n    &lt;Command&gt;echo&lt;\/Command&gt;\r\n    &lt;Arguments&gt;start &amp;gt; \/sys\/class\/remoteproc\/remoteproc0\/state&lt;\/Arguments&gt;\r\n    &lt;WorkingDirectory&gt;\/&lt;\/WorkingDirectory&gt;\r\n    &lt;BackgroundMode&gt;false&lt;\/BackgroundMode&gt;\r\n  &lt;\/CustomActionBase&gt;\r\n  &lt;CustomActionBase xsi:type=\"FileTransferAction\"&gt;\r\n    &lt;SkipWhenRunningCommandList&gt;false&lt;\/SkipWhenRunningCommandList&gt;\r\n    &lt;SourceHost&gt;\r\n      &lt;HostName&gt;BuildMachine&lt;\/HostName&gt;\r\n      &lt;Transport&gt;BuiltinShortcut&lt;\/Transport&gt;\r\n    &lt;\/SourceHost&gt;\r\n    &lt;DestinationHost&gt;\r\n      &lt;HostName&gt;stm32mp1&lt;\/HostName&gt;\r\n      &lt;Transport&gt;SSH&lt;\/Transport&gt;\r\n      &lt;UserName&gt;root&lt;\/UserName&gt;\r\n    &lt;\/DestinationHost&gt;\r\n    &lt;SourceFilePath&gt;$(TargetPath)&lt;\/SourceFilePath&gt;\r\n    &lt;DestinationFilePath&gt;\/lib\/firmware\/$(TargetFileName)&lt;\/DestinationFilePath&gt;\r\n    &lt;OverwriteTrigger&gt;Always&lt;\/OverwriteTrigger&gt;\r\n  &lt;\/CustomActionBase&gt;\r\n  &lt;CustomActionBase xsi:type=\"CommandLineAction\"&gt;\r\n    &lt;SkipWhenRunningCommandList&gt;false&lt;\/SkipWhenRunningCommandList&gt;\r\n    &lt;RemoteHost&gt;\r\n      &lt;HostName&gt;stm32mp1&lt;\/HostName&gt;\r\n      &lt;Transport&gt;SSH&lt;\/Transport&gt;\r\n      &lt;UserName&gt;root&lt;\/UserName&gt;\r\n    &lt;\/RemoteHost&gt;\r\n    &lt;Command&gt;echo&lt;\/Command&gt;\r\n    &lt;Arguments&gt;stop &amp;gt; \/sys\/class\/remoteproc\/remoteproc0\/state || echo \"Already stopped\"&lt;\/Arguments&gt;\r\n    &lt;WorkingDirectory&gt;\/&lt;\/WorkingDirectory&gt;\r\n    &lt;BackgroundMode&gt;false&lt;\/BackgroundMode&gt;\r\n  &lt;\/CustomActionBase&gt;\r\n  &lt;CustomActionBase xsi:type=\"CommandLineAction\"&gt;\r\n    &lt;SkipWhenRunningCommandList&gt;false&lt;\/SkipWhenRunningCommandList&gt;\r\n    &lt;RemoteHost&gt;\r\n      &lt;HostName&gt;stm32mp1&lt;\/HostName&gt;\r\n      &lt;Transport&gt;SSH&lt;\/Transport&gt;\r\n      &lt;UserName&gt;root&lt;\/UserName&gt;\r\n    &lt;\/RemoteHost&gt;\r\n    &lt;Command&gt;echo&lt;\/Command&gt;\r\n    &lt;Arguments&gt;$(TargetFileName) &amp;gt; \/sys\/class\/remoteproc\/remoteproc0\/firmware&lt;\/Arguments&gt;\r\n    &lt;WorkingDirectory&gt;\/&lt;\/WorkingDirectory&gt;\r\n    &lt;BackgroundMode&gt;false&lt;\/BackgroundMode&gt;\r\n  &lt;\/CustomActionBase&gt;\r\n&lt;\/ArrayOfCustomActionBase&gt;<\/pre>\n<p><a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/steps-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5199\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/steps-1.png\" alt=\"\" width=\"1175\" height=\"753\" \/><\/a><\/li>\n<li>Set a breakpoint in <strong>VIRT_UART0_RxClptCallback()<\/strong> and press F5 to begin debugging the program:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/08-bkpt.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5193\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/08-bkpt.png\" alt=\"\" width=\"1238\" height=\"863\" \/><\/a><\/li>\n<li>Connect to the STM32MP1 board via SSH and run the &#8220;cat \/dev\/ttyRPMSG0&#8221; command to begin displaying the output from the virtual COM port:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/09-cat.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5194\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/09-cat.png\" alt=\"\" width=\"1051\" height=\"496\" \/><\/a><\/li>\n<li>In another tab run &#8220;echo test &gt; \/dev\/ttyRPMSG0&#8221;:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/10-echo.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5195\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/10-echo.png\" alt=\"\" width=\"1051\" height=\"496\" \/><\/a><\/li>\n<li>The breakpoint will trigger, showing the message received from the Linux side:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/11-hit.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5196\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/11-hit.png\" alt=\"\" width=\"1238\" height=\"863\" \/><\/a><\/li>\n<li>Use the call stack to navigate to the <strong>MAILBOX_Poll()<\/strong> function and take a note of its logic. The function uses the <strong>msg_received_ch1<\/strong> and <strong>msg_received_ch2<\/strong> global variables to detect when a new message has arrived to the mailbox and then calls the previously registered callback function with it:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/13-poll.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5200\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/13-poll.png\" alt=\"\" width=\"1238\" height=\"863\" \/><\/a><\/li>\n<li>Go to the definition of <strong>msg_received_ch2<\/strong> and use the Code\/Data Relations view of CodeJumps to quickly locate the function that sets the variable:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/14-data.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5201\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/14-data.png\" alt=\"\" width=\"1238\" height=\"863\" \/><\/a><\/li>\n<li>Go to the <strong>IPCC2_channel_callback()<\/strong> function and set a breakpoint there. Then press F5 to continue execution. Check the console running the &#8220;cat&#8221; command. The message received via the virtual COM port will get echoed there:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/12-echoed.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5197\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/12-echoed.png\" alt=\"\" width=\"1051\" height=\"496\" \/><\/a><\/li>\n<li>Run the &#8220;echo test &gt; \/dev\/ttyRPMSG0&#8221; command again to send another message. The breakpoint in <strong>IPCC_channel2_callback()<\/strong> will trigger. Check the call stack to see how the function was invoked by the interrupt handler for the <strong>IPCC_RX1<\/strong> interrupt:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/15-bpt.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5202\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/15-bpt.png\" alt=\"\" width=\"1238\" height=\"863\" \/><\/a><\/li>\n<li>Now we will modify the Cortex-M firmware to continuously blink the on-board LEDs and update the Linux firmware to change the blinking frequency by writing messages to <strong>\/dev\/ttyRPMSG0<\/strong>. Update the <strong>VIRT_UART0_RxCpltCallback()<\/strong> function to append the incoming data to the buffer instead of overwriting it each time:\n<pre class=\"\">void VIRT_UART0_RxCpltCallback(VIRT_UART_HandleTypeDef *huart)\r\n{\r\n    log_info(\"Msg received on VIRTUAL UART0 channel:  %s \\n\\r\", (char *) huart-&gt;pRxBuffPtr);\r\n    size_t todo = MAX_BUFFER_SIZE - VirtUart0ChannelRxSize - 1;\r\n\r\n    if (todo &gt; huart-&gt;RxXferSize)\r\n        todo = huart-&gt;RxXferSize;\r\n\r\n    memcpy(VirtUart0ChannelBuffRx + VirtUart0ChannelRxSize, huart-&gt;pRxBuffPtr, todo);\r\n    VirtUart0ChannelRxSize += todo;\r\n    VirtUart0ChannelBuffRx[VirtUart0ChannelRxSize] = 0;\r\n}<\/pre>\n<p>Then add the following function to read the data from the buffer line-by-line:<\/p>\n<pre class=\"\">size_t ReadLineFromVirtualUART(char *pBuffer, size_t maxSize)\r\n{\r\n    OPENAMP_check_for_message();\r\n\r\n    int pos = 0;\r\n    for (pos = 0; pos &lt; VirtUart0ChannelRxSize; pos++)\r\n    {\r\n        if (VirtUart0ChannelBuffRx[pos] == '\\n')\r\n        {\r\n            size_t todo = pos;\r\n            if (todo &gt;= maxSize)\r\n                todo = maxSize - 1;\r\n            memcpy(pBuffer, VirtUart0ChannelBuffRx, todo);\r\n            pBuffer[todo] = 0;\r\n\r\n            pos++;\r\n            memmove(VirtUart0ChannelBuffRx, VirtUart0ChannelBuffRx + pos, VirtUart0ChannelRxSize - pos);\r\n            VirtUart0ChannelRxSize -= pos;\r\n            return pos - 1;\r\n        }\r\n    }\r\n\r\n    return 0;\r\n}<\/pre>\n<p>Finally, update main() to blink the LED continuously, updating the frequency when a new message is received via ReadLineFromVirtualUART():<\/p>\n<pre class=\"\">\u00a0 __HAL_RCC_GPIOH_CLK_ENABLE();\r\n  GPIO_InitTypeDef GPIO_InitStructure;\r\n  GPIO_InitStructure.Pin = GPIO_PIN_7;\r\n  GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;\r\n  GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;\r\n  GPIO_InitStructure.Pull = GPIO_NOPULL;\r\n  HAL_GPIO_Init(GPIOH, &amp;GPIO_InitStructure);\r\n\r\n  uint32_t toggleTime;\r\n  uint32_t period = 1000;\r\n  char buf[64];\r\n\r\n  while (1)\r\n  {\r\n      if (HAL_GetTick() &gt;= toggleTime)\r\n      {\r\n          HAL_GPIO_TogglePin(GPIOH, GPIO_PIN_7);\r\n          toggleTime = HAL_GetTick() + period;\r\n      }\r\n\r\n      if (ReadLineFromVirtualUART(buf, sizeof(buf)))\r\n      {\r\n          period = atoi(buf);\r\n      }\r\n  }<\/pre>\n<\/li>\n<li>Press F5 to build and start the new version of the program. Observe how the on-board LED is being toggled each second:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/led-cut.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5205\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/led-cut.jpg\" alt=\"\" width=\"1280\" height=\"248\" \/><\/a><\/li>\n<li>Run &#8220;echo 100 &gt; \/dev\/ttyRPMSG0&#8221; from an SSH terminal and observe how the LED blinking frequency changes:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/echo.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5204\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/echo.png\" alt=\"\" width=\"1051\" height=\"411\" \/><\/a><\/li>\n<li>You can set a breakpoint inside main() to step through the logic responsible for parsing the commands from the Linux side:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/callback.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-5206\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2019\/07\/callback.png\" alt=\"\" width=\"1238\" height=\"863\" \/><\/a><\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial shows how to use the OpenAMP library to communicate between multiple cores of the STM32MP1 device. We will<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[90],"tags":[61,187],"_links":{"self":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/5185"}],"collection":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/comments?post=5185"}],"version-history":[{"count":2,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/5185\/revisions"}],"predecessor-version":[{"id":5207,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/5185\/revisions\/5207"}],"wp:attachment":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media?parent=5185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/categories?post=5185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/tags?post=5185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}