Timestamp synchronization across Cortex A and Cortex M

Hello,
I have Torizon running on the Cortex A53 and freeRTOS running on the Cortex M4. Currently the timestamp generated on the Cortex M4 through xTaskGetTickCount() is different from the timestamp I generate on the Cortex A53.

does the Cortex M4 use a different RTC from the Cortex A53? I thought they were both using the svns-rtc-lp

on the IMX8MM reference manual , SNVS section it explains that we have two submodules HP and LP, with two different RTC. RTC_LP is the one exposed in the device tree and is not accessible by the entire system; the RTC_HP instead is in a different (non-secure) domain and can be access by all the system.

Interestingly paragraph 6.4.3.1.1 states:

does it means :

  1. the M4 can’t access directly the RTC_LP?
  2. do I need to “mirror” the RTC_LP on the RTC_HP?
  3. are RTC_HP and RTC_LP two independent counters running with the same clock source or once RTC_HP is synchronized with RTC_LP it will automatically mirror it?
  4. is it possible to access RTC_HP from Torizon?

do you now other strategies to synchronize the timestamps on Cortex A and Cortex M?

thanks for any help!

Hi @RoccoBr ,

we’re looking into it.

We reach out to you as soon as possible.

Best Regards
Kevin

Hi @RoccoBr !

Taking a look at the documentation for xTaskGetTickCount(), it simply returns the interruption counter from FreeRTOS. It is not related to RTC. In other words, it does not give you a timestamp. From the documentation, the value can even overflow.

From the Linux side, could you please elaborate on how you are obtaining the timestamp?

Also, could you please specify:

  • BSP version
  • Linux Kernel version
  • Verdin iMX8M Mini version
  • MCUXpresso version

Reference: https://freertos.org/fr-content-src/uploads/2018/07/FreeRTOS_Reference_Manual_V10.0.0.pdf


Best regards,

Hi @henrique.tx
thank you for your replying
these are my info from VS code:
image

we use MCUXpresso SDK v2.11.0

the timestamp on the Linux side is generated in the python application by datetime.datetime.now()

what do you reckon is the best way to get the timestamp on the M4

Regards,
Rocco

Hi @RoccoBr !

To convert the output from xTaskGetTickCount() to time, please refer to FreeRTOS’s documentation: specifically section 2.11 of https://freertos.org/fr-content-src/uploads/2018/07/FreeRTOS_Reference_Manual_V10.0.0.pdf

Which Verdin iMX8MM are you using?

Best regards,

Hi @henrique.tx ,
we are using Verdin iMX8MM Q 2GB IT v1.1A.

I’m trying to access rtc1 from the M4 as SVNS device, but RTC_LP is always 0. This is the dump of all registers:
SNVS_HP Command Register: 80002100
SNVS_HP Status Register: 80009b00
SNVS_HP Control Register: 0
SNVS_HP Real Time Counter MSB Register: 0
SNVS_HP Real Time Counter LSB Register: 0

SNVS_LP Control Register: 21
SNVS_LP Status Register: 40000000
SNVS_LP Lock Register: 0
SNVS_LP Real Time Counter MSB Register: 0
SNVS_LP Real Time Counter LSB Register: 0

I can enable SNVS_HP RTC writing 1 to SNVS_HP Control Register, but then I have just another independent real timer counter running.
reading SNVS_LP Real Time Counter always return zero on the M4, while if I read it on Linux side as cat /sys/class/rtc/rtc1/time I can see the time since power up.
according to the reference manual bit 31 (NPSWA_EN) in SNVS_HP Status Register allows non-privileged software to access all SNVS registers, including those that are privileged
software read/write access only. As you can see from the values above, that bit is set in my configuration, but I cant read the SNVS_LP Real Time Counter.

as I was saying in my first post, my ultimate goal is to read the same time/counter on both processors.

any suggestions?

Regards,
Rocco

Hi @RoccoBr !

Unfortunately, we are not aware of how to deal with it.

I recommend you ask at NXP forum if you want/need to specifically do it.

(I found this question that seems related, but there is no answer until now: Access to RTC from Cortex-M4 on i.MX8M Mini - NXP Community)

On the other hand, you could share the time from Linux to Cortex-M upon boot (via RPMsg) and after that, make use of the tick counter to keep track of time from the Cortex-M side. If it is suitable for you, of course.

Best regards,

While this response is probably too late, I am still answering as I was trying to solve the same problem and found this post that eventually led me to the solution. In my case I am using the I.MX8m Plus, the reference manual in section 6.4.2.2.1.1, also mentions the ability to synchronize the RTC1 clock into the SVNS HP clock such that unprivileged applications can read it. The reference manual also mentions to toggle the HP_TS bit without further clarifying where this bit is located. It seems that bit 16 of the SVNS HP Control Register is this HP_TS bit.

Setting the 16th bit of the SVNS_HP Control Registers copies the RTC1 clock (SVNS LP), simultaneously setting the 0th bit will then also enable the clock. Torizon OS by default uses the external rtc as its main rtc. This means that RTC1 (the internal RTC in the SVNS LP domain) will not contain a valid value by default and will thus reset at each power on reset.

First ensure that the system clock is synchronized using ntp or by manually setting it. Then issue the following command to write the Linux system clock to rtc1:

sudo hwclock -s -f /dev/rtc1

You can also decide to configure Linux to use the internal RTC as its primary, see the Toradex documentation: Real-Time Clock / RTC (Linux) | Toradex Developer Center

Now to read the wall clock in the Cortex M7 you can use the following c++ code or equivalent c code:

void enable_timer() noexcept {
    SNVS->HPCR = (1U << 16U) | (1U << 0U);
}

uint64_t read_time() noexcept
{
    /* According to section 6.4.2.2.1.3 of the i.MX 8M Pluse Applications Processor Reference
       Manual the time resgisters must be read twice and compared. If the value are the same
       the retrieved time was correct, else retry for a maximum of three times after which the value
       should be correct
    */

    uint32_t msb_first = SNVS->HPRTCMR;
    uint32_t lsb_first = SNVS->HPRTCLR;

    uint32_t msb_second = SNVS->HPRTCMR;
    uint32_t lsb_second = SNVS->HPRTCLR;

    while (msb_first != msb_second || lsb_first != lsb_second)
    {
        msb_first = SNVS->HPRTCMR;
        lsb_first = SNVS->HPRTCLR;

        msb_second = SNVS->HPRTCMR;
        lsb_second = SNVS->HPRTCLR;
        ++i;
    }
    uint64_t ret = (msb_first << 32 )|(lsb_first);
    // the clock counts at 32 khz, divide by 32khz to get the "real" unix timestamp in seconds.
    ret = ret / 32768;
    return ret;
}