Using Rpmsg library on WinCE 6 it’s quite easy to load a simple firmware in M4 core and start the execution (Colibri Vf61 module).
If the firmware uses FreeRTOS, it enables and uses (at least) SysTick and PendSV.
When A5 starts M4 for the first time, the secondary core starts from a “clean” condition and (probably) everything works fine.
But this is not 100% sure, because if (for any reason) M4 is in a “dirty state” (as an example: either with some pending interrupts or SysTick enabled) a lot of problems can happen. As an example SysTick_Handler can call xTaskIncrementTick() before the whole FreeRTOS has been fully initialized.
This is only an example, but it’s hard to say which kind of “dirty states” can be generated for M4.
Another possible scenario: I want to load a firmware to M4, run it, then load another firmware. I must be sure that M4 core start from a known condition.
Reading this topic on NXP Community it seems that it’s not possible to reset M4 core from A5 and complicates really much the situation.
Which is the right way to have a “clean start” of M4 core?
Which registers should I set to a known state before start the M4 clock?
Does Rpmsg library take care of this need?
Probably this is ad advanced usage of the Vybrid, and it’s not easy to explain what happens, but if you need you can send me a private message and I can give more details.
Hello @luka.tx,
I’m not saying I need to load a different firmware.
I only want to know how to be sure that M4 starts in a “clean” way (i.e. from a known, predictable, condition).
I rebuilt the rpmsg_pingpong_example that Toradex provides with Keil DS-5, but this can’t be loaded from A5 and executed on M4 without hardware fault.
I verified that the prebuilt bin works as expected, but the sources can’t be rebuilt in a working application using DS-5 (or DS-MDK). Everything works fine if I load the application through a debugger (ULINK Pro).
I’ve been spending a lot of time investigating the issue, but I need help from Toradex understanding what happens.
I can share the sources (it’s the Toradex example) if needed.
We do not specifically clear any clocking or registers. We just load firmware and start M4. For everything else you will have to look into reference manual if there are some registers specific to your implementation that you need to set.
Ok. This is a first step to start the investigation.
I add 3 screenshots showing the HardFault handler when rpmsg_pingpong_example is loaded from A5.
[upload|kM7EJgfcFhLGKoCMz5At5k/v580=]
[upload|RycOZ08Qn+u2IWHjAhqJQg5sj+w=]
[upload|XC7v8SANc1nzuEvBOcpC/cZRB9U=]
You can see that PRECISERR and BFARVALID are set (see ARM documentation). BFAR register contains an invalid address 0xE3A01004; maybe this has been produced by dereferencing a null pointer (or something like that).
Following this documentation should I add any specific FreeRTOS configuration to the supplied rpmsg_pingpong_example to build it using Keil Ds-5?
Stacked PC points to 0x1F004804 and this is inside xTaskIncrementTick() functions - it should be line 1994
Hi @luka.tx
I wonder if FreeRTOS is properly configured for VF61 as it is supplied by Toradex here.
As described in FreeRTOS documentation
FreeRTOS functions that end in
“FromISR” are interrupt safe, but even
these functions cannot be called from
interrupts that have a logical
priority above the priority defined by
configMAX_SYSCALL_INTERRUPT_PRIORITY
(configMAX_SYSCALL_INTERRUPT_PRIORITY
is defined in the FreeRTOSConfig.h
header file). Therefore, any interrupt
service routine that uses an RTOS API
function must have its priority
manually set to a value that is
numerically equal to or greater than
the
configMAX_SYSCALL_INTERRUPT_PRIORITY
setting. This ensures the interrupt’s
logical priority is equal to or less
than the
configMAX_SYSCALL_INTERRUPT_PRIORITY
setting.
Cortex-M interrupts default to having
a priority value of zero. Zero is the
highest possible priority value.
Therefore, never leave the priority of
an interrupt that uses the interrupt
safe RTOS API at its default value.
In function xPortStartScheduler() I find a comment saying
/* Make PendSV and SysTick the lowest priority interrupts. */
and this is what is expected.
But the implementation is
and this seems strange to me, because I don’t find any documentation on portNVIC_SYSPRI2_REG register in VFxxx HRM.
Are you sure this configuration is correct for VF61?
If I call
NVIC_SetPriority (SysTick_IRQn, 15);
other registers are set to change the priority for SysTick.
I wonder if a wrong initialization of interrupt priorities could bring to HardFault as described here:
Despite the numerous RTOS support
requests from people explaining that,
when using the RTOS kernel, their
application ends up in the hard fault
handler, when the issue has been
worked through, it is always shown
that the cause of the hardware fault
is not the kernel, but one of the
following:
A misunderstanding of interrupt priorities on the ARM Cortex-M core
(easy to do!), or a misunderstanding
of how to use the FreeRTOS interrupt
nesting model.
Most of the FreeRTOS BSP for Colibri VF61 has been adopted from the BSP for i.MX7. The NVIC and its priority settings are a Cortex-M4 feature, and both Vybrid and the i.MX 7 have a Cortex-M4, so we did not change any of those settings when porting to VF61.
In general, the FreeRTOS port works on Colibri VF61 when using Linux, so I don’t think it is a problem of FreeRTOS itself…
As for the clean state: Yes, there is no M4 only reset, so the only way to get to a clean slate is using the System wide SoC reset. But with that you should have a clean state for A5 as well as M4.
Hello @stefan.tx
I trust you, so I believe that the porting is ok.
The problem is that when I rebuild the sources of rpmsg_pingpong_example with DS-5 and I load the bin from core A5, the application jumps to HardFault (it’s a Bus Error promoted to HardFault).
If I don’t start the FreeRTOS scheduler the HardFault doens’t happen.
I don’t know why but it seems that the screenshots have been removed from my previous message.
I can share them, and I need help from your side to understand and fix this issue.
What is visible from your screenshots is that the xPSR register is set to 0xA1000003, the last three indicating a Hard Fault. I have seen this on i.MX 7, there it is an issue when we enable the M4 it immediately faults since no valid firmware was loaded. When we then load the firmware into memory, we had to issue another core reset to reset that register. In the i.MX 7 case we can use the M4 core reset of the SoC SRC module. However, as noted above, Vybrid does not have such a reset.
Maybe DS-5 can somehow reset “manually” all registers through debug or similar?
Otherwise, what should help is loading just a valid firmware first (e.g. a known to not crashing Hello World) through the A5 before debugging the first time.
@stefan.tx thank you very much for your valuable information. Everything is useful to narrow down the possible reasons for the issue.
I have good news!
The hardware fault arises because during the load of the firmware, my “DATA” section was not loaded (or loaded and then overwritten for some reason). And so I have some global variables that should be initialized to non-zero values that are set to zero.
This happens when I put my “DATA” section starting from 0x3F060000.
If I put “DATA” starting from 0x3F040000, everything works fine.
I load the firmware with the following instructions
//Firmware load address, must use system bus address, please go through this link(http://developer.toradex.com/knowledge-base/freertos-on-the-cortex-m4-of-a-colibri-vf61) for more information.
returnValue = Rpmsg_SetConfigInt(hRpmsg, L"LoadAddr", 0x3f000000, StoreVolatile);
//Firmware code block size
returnValue = Rpmsg_SetConfigInt(hRpmsg, L"CodeSize", 512*1024, StoreVolatile);
and I didn’t expect to have this kind of problems.
Is there any bug in the function that loads the firmware from A5?
If you need, I can share two .bin files where the difference is the starting address of “DATA” (0x3F060000 not working vs 0x3F040000 working)
Maybe set a breakpoint to Reset_Handler to make sure the initialization code really gets executed properly?
WinCE should just copy the bin file to the the location specified in m_text (potentially translated to A5 addresses, and cache flushed), and then the startup code should take care of copying the .data section…
@luka.tx, maybe you can comment on whether WinCE is somehow using this areas?
Since I use Keil DS-5, I created my own scatter file (it’s the linker for Keil environment).
Moreover I must use a different version of startup_VF6XX_M4.s, since this one uses GCC-compliant assembly.
In my Reset_Handler I don’t explicitly copy anything, I just jump to my SystemInit() (which basically relocates the vector table), then jump to main().
I opened the generated .bin files with a hex editor and I see they include my DATA section at the proper address, so I don’t see any reason why the library should not copy it to the destination address.
Since Toradex doesn’t provide DS-5 files for Vybrid example, I need to know if the linker and/or the startup code must do something special, so that I can create proper files for DS-5.
To get a C runtime up and running it needs some coordination between linker, loader and startup code. There are different variants how the C .data/.bss sections get initialized, but typically for Cortex-M4 microcontroller you would store .data in the code section and add a symbol so the startup code can copy into RAM (in Vybrids case this symbols are called __DATA_ROM for the location of the init data in the “ROM” and __data_start__ for the location in RAM). Also bss section has to be cleared.
We cannot really support all linker/loader/runtimes out there, so we opted for GCC…
Note that in Vybrids/i.MX 7 case this relocation seems somewhat silly since there is no ROM, and we end up copy from one region in RAM to another. However, it allows to create a continuous binary with code and initialized data section concatenated and relocating into a RAM region accessible through system bus which should make the firmware somewhat faster (using the data bus of the M4’s modified Harvard architecture).
I understand that having a C runtime up and runnign needs some steps, but I don’t understand why moving my DATA section should change something.
The .bin file doesn’t contain the name of the sections, but only addressess and data to load.
As I said, I can share the two .bin files (where the difference is the address of DATA section) and my scatter file.
At least with the GCC linker file the firmware has to copy/move the the data section because the linker expects variables at a different location than where it is stored.
E.g. if you create a global variable with initialized value of 5, it will get linked to an address in m_data (lets say 0x20001234). However, the initial value is stored somewhere in m_text, after the code, lets just say 0x1FFF9320. (.data : AT(__DATA_ROM) is doing this). The code expect the value at 0x20001234 (e.g. a ldr would get the value from there), but the initial value is stored in 0x1FFF9320. The startup code has to copy the value to the right location… There are symbols for all this relocation, and the code I pointed you to is doing this for the GCC built firmwares.
This could all be different in your case, since you use a different linker file/startup code. I am just saying, your statement “And so I have some global variables that should be initialized to non-zero values that are set to zero.” smells a lot like C runtime initialization issue…
If application code and/or data are ina non-root region (i.e. load-time address different from execution-time addresse), it’s necessary to copy them.
For this reason, Keil/ARM linker adds some predefined functions (__main, __scatterload, …) to do this job. See here - “3 Entry point to the C library” some info on these functions.
These functions are properly executed when I debug the application through JTAG, so I think it’s the same when the application is loaded from A5.
If the problem is not in the parsing and loading of bin file the only conclusion is that WinCE somehow overwrites the region above 0x3f060000.
Only @luka.tx can clarify this point, I think.
We do not over write any of this ram. I do write at 0x3F000000 to 0x3F003800. After that I do not touch any of the ram located at 0x3F000000. Even that I only do at boot.