Sending out data packets in 2ms intervals - burst problem

Hello,

Using a colibri imx6 512Mb module, we tried sending (periodically) 127 byte packets over uart in 2ms time intervals.
|------2ms------|------2ms-----|-----2ms-----|

Baud rate is 1Mbit and as long as there is there is no overlap to the next 2ms interval, we are fine with data transmission starting anywhere in the 2ms time interval.
Below is the code snippet for sending data.

// https://stackoverflow.com/questions/24259640/writing-a-full-buffer-using-write-system-call
int writen(const int sd, guint8 * b, const size_t s, const int retry_on_interrupt) {
    size_t n = s;
    while (0 < n) {
        ssize_t result = write(sd, b, n);
        if (-1 == result) {
            if ((retry_on_interrupt && (errno == EINTR)) || (errno == EWOULDBLOCK) || (errno == EAGAIN)) { continue; } 
            else { break; }
        }
        n -= result;
        b += result;
    }
    return (0 < n) ?-1 :0;
}

void * appsink_thread(void *tdata_void)
{
    ThreadData * tdata = (ThreadData*) tdata_void;
    struct timeval tv0, tv1;
    guint32 tv0_residue, tv1_residue;
    
    int tty = open(tdata->tty.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
    if (!tty) {
        return NULL;
    }

    while(1){
        if(packets_to_send[pcks_start] > 0){
            gettimeofday (&tv0, NULL);
            tv0_residue = tv0.tv_usec % 2000;
            if (tv0_residue > 1900) {
                guint8 *data = serialize_pack(pck, (guint16*)&tx_size);
                do {
                    gettimeofday (&tv1, NULL);
                    tv1_residue = tv1.tv_usec % 2000;
                } while (tv1_residue > tv0_residue);

                int rc = writen(tty, data, tx_size, false);
                
                // ... cleanup resources etc
            }
        }
        
        // ... receive more packs here
        
        usleep(50);
    }
    close(tty);
}

We used a test setup to measure the latency and saw that after some time, packets come right after each other and fail to meet the 2 ms interval.

We suspect this is something to do with the Linux OS, failing to wake up the thread so we are considering applying the real time patch to Colibri

Can you please comment about this?

Thanks

Hi @rifo

Could you provide the version of the hardware and software of your module?

As you already suspected, Linux is not a real-time Operating System. During the run of thread and tasks, there will be a jitter depending on caching and load of the CPU and other things. Applying the Real Time patch will make Linux more deterministic.

Please read the following developer site for further Information.

Best regards, Jaski

Hi,
We are working with @rifo. I am trying with:

  • Colibri iMXS 256MB V1.1A
  • Viola 1.2B
  • console-tdx-image derived custom image at LinuxImage2.8. Cloned a few days ago. Commit of .repo/manifests.git is 60d5600163bf31057efb0d7656f7356a75f236f4.
  • Can found a simplified custumization at my previous question.

Here some of my findings:

Tried both linux-toradex and linux-toradex-rt. Applied pthread priority attributes at this example for RT tests. RT enhanced waking up times. I am only getting a few >3 with this debug output:

        gettimeofday (&tv0, NULL);
        usleep(50);
        gettimeofday (&tv1, NULL);
        if (tv1.tv_usec - tv0.tv_usec > 500) printf(">5 ");
        else if (tv1.tv_usec - tv0.tv_usec > 400) printf(">4 ");
        else if (tv1.tv_usec - tv0.tv_usec > 300) printf(">3 ");

Here the pthread initialization:

        if (ok) {
            /* Initialize pthread attributes (default values) */
            ok = (0 == pthread_attr_init(&pck_mngr_thread_attr));
            if ( ! ok) cerr << "Failed to init pthread attr!" << endl;
        }
        if (ok) {
            /* Set a specific stack size */
            ok = (0 == pthread_attr_setstacksize(&pck_mngr_thread_attr, PTHREAD_STACK_MIN));
            if ( ! ok) cerr << "Failed to set pthread attr stack size!" << endl;
        }
        if (ok) {
            /* Set scheduler policy */
            ok = (0 == pthread_attr_setschedpolicy(&pck_mngr_thread_attr, SCHED_FIFO));
            if ( ! ok) cerr << "Failed to set pthread attr sched policy!" << endl;
        }
        if (ok) {
            struct sched_param param;
            param.sched_priority = 80;
            /* Set scheduler priority */
            ok = (0 == pthread_attr_setschedparam(&pck_mngr_thread_attr, &param));
            if ( ! ok) cerr << "Failed to set pthread attr sched priority!" << endl;
        }
        if (ok) {
            /* Use scheduling parameters of attr */
            ok = (0 == pthread_attr_setinheritsched(&pck_mngr_thread_attr, PTHREAD_EXPLICIT_SCHED));
            if ( ! ok) cerr << "Failed to set pthread attr sched param!" << endl;
        }
        if (ok) {
            /* create packet thread */
            ok = (0 == pthread_create(&pck_mngr_thread, &pck_mngr_thread_attr, appsink_pck_mngr_thread, (void*)&tdata));
            if ( ! ok) cerr << "Failed to create appsink_pck_mngr_thread!" << endl;
        }

Tried to disable DMA, but it made it worse. I thing, DMA is not affecting context switching and it is a good thig : ^ )

Also, found a previous question about topic at here. Looked at imx.c but did not see any related stuff. It’s little complicated for me.

I have attached a screenshot from logic analyzer.

What can we do then ?

hi @huseyinkozan

Thanks for the findings.

What can we do then ?

It depends on your requirements. Which jitter is allowed for your application?

To reduce jitter you can write your own kernel module for your application.

If you want to have a very low jitter, you should run your code on m4 which is included in VF61 and iMX6.

Side note: Could you try to rewrite your C-code and check every 2ms, if there are data to send and send them? This will give you a more regular and deterministic code?
You could code like this:

t1 = initial time;

while (1)
{

    t2 = actual time;
    dt = actual time - initial time;

    if dt > 2ms
    {
        initial time = actual time;
        send data;
    }
usleep(100);
}