FreeRTOS on iMX7.D M4 stuck in prvPortStartFirstTask()

Hi all,

I am trying to run my first FreeRTOS example (hello_world_ocram from the master branch of
GitHub - toradex/FreeRTOS-Colibri-iMX7: FreeRTOS for Colibri iMX7) using Eclipse and the Segger J-Link probe on a Colibri i.MX7D 1GB module.
The linker file is MCIMX7D_M4_ocram.ld.

The code execution gets stuck at the function prvPortStartFirstTask() and won’t advance when clicking ‘run’ or ‘step over’.

Having done a little search on the web, it may have something to do with the vector table not being initialized but I do not have an idea of how to check this.

When I just call the HelloTask() after hardware_init(), the message appears on UART B and all characters that I type are echoed. In other words, the program runs correctly and the development environment seems OK as well.

Do any of you have any clues where this problem is coming from ?

Best regards,
Jeroen

Hi

I you didn’t modify anything, then vectors table should load to RAM along with the rest of the code. Perhaps your debugger starts execution from wrong location. It should start from Reset_Handler defined in startup_MCIMX7D_M4.S. It is OK to start from Reset_Handler and automatically execute until main(), but startup routines have to be executed any way.

Hello @Edward,

I did indeed not change anything except replacing the textual names for the sectors in the MEMORY section of the linker file.

Originally (for the ocram variant), the linker file looks like this (master branch of your GitHub repository):

MEMORY
{
  m_interrupts  (RX)  : ORIGIN = OCRAM_S_code,         LENGTH = 0x00000240
  m_text        (RX)  : ORIGIN = OCRAM_code + 0x10000, LENGTH = 0x00010000
  m_data        (RW)  : ORIGIN = OCRAM_EPDC_system,    LENGTH = 0x00020000
}

I replaced it with

MEMORY
{
  m_interrupts  (RX)  : ORIGIN = 0x00000000,           LENGTH = 0x00000240		/* OCRAM_S_code */
  m_text        (RX)  : ORIGIN = 0x00900000 + 0x10000, LENGTH = 0x00010000		/* OCRAM_code + 0x10000 */
  m_data        (RW)  : ORIGIN = 0x20220000,           LENGTH = 0x00020000		/* OCRAM_EPDC_system */
}

Because the gcc linker doesn’t seen to understand the constants and complains:

MCIMX7D_M4_ocram.ld:102: nonconstant expression for origin

I am using gcc-arm-none-eabi-4_9-2015q3 and the linker file is the one located at …platform/devices/MCIMX7D/linker/gcc.

In the same linker file, Reset_Handler is specified as the code entry point:

/* Code execution after reset starts here: */
ENTRY(Reset_Handler)

When I set a breakpoint at Reset_Handler in startup_MCIMX7D_M4.S, it nicely stops there so this seems ok. It also enters SystemInit() in system_MCIMX7D_M4.c, setting SCB-VTOR to 0 for instance (which is what is also specified in the linker file for the vector table.

I hope this helps to understand what is going on…
Is it the fact that I am executing from OCRAM ? I tried DDR and this gives exactly the same result

Jeroen

I see it is different repository.
What about this repository

freertos-toradex.git - FreeRTOS for the Cortex M4 core of Heterogeneous Multicore modules

and these ld scripts:

gcc « linker « MCIMX7D « devices « platform - freertos-toradex.git - FreeRTOS for the Cortex M4 core of Heterogeneous Multicore modules

I changed the repository (with correct linker scripts :slight_smile: ) and recompiled everything.
Unfortunately, I still have the same error…
When I do not start FreeRTOS and use e.g. the polling version of the I2C driver example (as a basis for access a I2C device on my custom board), the code works correctly.
However, adding the code for the ecspi_interrupt example causes trouble again.

Some observations that may help:

  1. When I set a breakpoint at Reset_Handler and start debugging, the processor stops here.
    Since I’m using OCRAM, the vector table is supposed to be at address 0 (m_interrupts in the linker file).
    When I look at the data at address 0 and further, I see 0x00002420 and 0xD1009100 at the first two double words (initial stack pointer value and reset address). Most of the next double words are 0xF1009100: )

According to the linker file, the code should execute in a region starting at 0x00910000 (m_text region) and with the code still at the initial breakpoint, the disassembly shows indeed 0x009100d2 for the address of the next instruction.

This means that the vector table at address 0 is correct, if you read the bytes LSByte first: the second entry, 0xD1009100, corresponds to 0x009100D1 which is the address at which Reset_Handler starts plus one. Actually, all addresses in the vector table are the interrupt handler addresses + 1. I suppose this is OK ? This is the Linker map file (2.2 MB) where you can find the vector table at line 20812 and further.
So I think we can conclude that the vector table is correctly placed and initialized…

So the problem remains in port.c, prvPortStartFirstTask() which contains a number of assembly instructions and is supposed to start the first task.
One of the instructions is SVC which triggers an exception for which the vector is at the 12th long word in the vector table. It reads 0x00910371 and the map file shows 0x00910370 for “SVC_Handler”.
A search for SVC_Handler learns that there is a #define in FreeRTOSConfig.h:

#define vPortSVCHandler SVC_Handler

So in the end, I conclude that vPortSVCHandler(),. defined in port.c, should be called but setting a breakpoint at that function never results in a hit…

I hope this will give you an idea of what goes wrong ! I’m sorry for the long text…

Thanks in advance,
Jeroen

PS: I suppose the repository that you mention is the “official one” ? It would be nice to remove any reference to the other one on GitHub because it is confusing (I don’t know where I found it but it was on the Toradex site)

Did you run compiled application as is instructed in Toradex Knowledge Base? Does’t it run? If it runs, then we back again to issues with your debugger. Isn’t it? I didn’t try all applications, just only 1 or 2 about RPMSG for quick start. RPMSG echo or something like that. It worked. It worked with some hair picking with DS-5 debugger, to make GCC ELF compatible, I had to either (load and) launch ELF using bootaux / imxfwloader, else GCC compiled ELF image had to be converted for DS-5. DS-5 ELF doesn’t agree with GCC ELF regarding, ehh, something about vma/lma. I had to use script like this, don’t remember details, please don’t ask:

LMA=`arm-none-eabi-readelf -l $1 | sed '10q;d' | cut -d " " -f 16`
arm-none-eabi-objcopy --change-section-vma .data=$LMA $1 $2

So, are you sure your debugger is compatible with your compiler?

And what PC address is it? Does disassembly agree with highlighted location in source code?

Not at all. iMX7 M4 initial address can be supplied only via vectors at 0. There are no other means, like for example on Vybrid, which has dedicated register for initial M4 PC address. This is how all iMX7 M4 launchers work, they write those locations to make initial PC value loaded from there. To put or not to put vectors table to 0 addresses is up to you and debugger, you are using. Once booted, vectors table pointer register can be moved to TCM / OCRAM /etc. So normally vectors table is “code locations” of application, which is not at 0 - addresses. Scripts with _bin in the name are imxfwloader compatible. Both _bin and non _bin should work with bootaux and remoteproc driver as launcher.

I think these are 9100f1, just byte reversed for some reason.
1 in least bit location is right bit setting for thumb mode. I see you as well noticed byte swap. What does PC point to? 91xxxx or F1xxxxxx?

Hi Edward,

Do you mean this article ?
(that’s where the link to the (wrong?) GitHub version of FreeRTOS comes from by the way)

I compiled the hello_world example as described (the gcc version used in the article is the same as I have) and copied the .elf file to the eMMC storage of the i.mx7D 1G which was mounted to /mnt/vfat_partition

Then, on the U-Boot prompt:

Colibri iMX7 # setenv m4boot ‘ext4load mmc 0:1 ${loadaddr} hello_world.elf && bootaux ${loadaddr}’
Colibri iMX7 # saveenv
Colibri iMX7 # run m4boot
156242 bytes read in 16 ms (9.3 MiB/s)
## Starting auxiliary core stack = 0x00000000, pc = 0x1FFF80D1…

So this seems to work. But I still could not debug the program.

After a lot of trial & error, I am now able to debug in TCM with eclipse, using MCIMX7D_M4_tcm_bin.ld
It is important to use the ‘_bin’ version which assigns the correct address to the vector table.
However, It does not always work and for some reasons I have not yet eluded, running in the debugger sometimes leads to HardFault errors. Simply restarting the Colibri module (and stopping in U-boot in order to first launch a dummy application) solves the problem most of the time (sometimes I have to repeat it).

Resuming, starting the debug is not stable but when it starts correctly I can set breakpoints, read variables etc.

For me it is not clear what version to use. The difference is the location of the interrupt sector which is 0 for the non-bin version and 0x1FFF8000 for the bin version, which seems more logical to me and is what works for me.
Could you explain the reason to use 0x0 for the vector address ?

Jeroen

Yeah, sometimes it happens, and there’s no direct path to find what causes it.

I don’t know why FSL made it so, but M4 reset vector at 0 is iMX7 feature. There are no other means to specify initial PC address for M4 FW. Like I said, _bin.ld is imxfwloader compatible. imxfwloader needs single contiguous code section. It doesn’t support ELF files and only raw binary files. How would you tell imxfwloader you have piece of code at this address, then another piece of code at that address. And you still need at least reset vector at ~0 and code few MB’s higher. _bin.ld links to single section, then imxfwloader takes reset vector from bottom of your binary and initializes vector at ~0 for you. In contrast bootaux and remoteproc allow using multisegment ELF binaries, so you may have separate vectors section at 0, plus some code in TCM, plus some code in SRAM, some const data somewhere else. Chose *.ld, which works for you.

Edward

Hi Edward,

Thanks for your answers.
One other thing that I am still wondering about (I admit not having searched for existing topics…) is the startup.
As I said, I stop U-boot in order to be able to debug. Just stopping is not enough to start debugging (j-Link cannot connect to the target). As far as I understand, this is because the M4 is initially held in reset. executing “run m4boot” with a simple .elf executable will start the M4 after which the debugger can take control. My first question is:

Is there a way to release the M4 from reset without having to manually start an “application.elf” ?

For now, I either debugged the Linux application or the M4 code but never the two at the same time, but I need to combine the two as well. Since I debug the M4 after halting the U-boot, my second question is:

How can I resume the U-boot after I start the M4 part? Is adding the “run m4boot” to the bootcmd variable the only way to both start the M4 and Linux ?

Best regards,
Jeroen

Hi Jeroen,

I know nothing about J-Link and tools you are using. Looking at iMX7D RM, MDM-AP seems supporting all cores halt and restart requests. run m4boot issues U-Boot’s bootaux command. It is one of the ways to normally launch M4 FW. bootaux doesn’t stop U-Boot, it only launches M4.

Regards,
Edward

Just for those who came here for a similar problem:

The cause of the problems came from the Segger J-Link script that does not correctly reset the M4.
On this page, you can find a modified script that basically implements ResetTarget() in a correct way.
Since then, I am able to debug and restart the M4 without having to (hard) reset the module.

Besides OCRAM, I also tested TCM and this also works flawlessly.

Cheers,
Jeroen