FreeRTOS+UDP: Reasons behind TICK_RATE limit

fb0 wrote on Friday, August 21, 2015:

Hello,

I am currently evaluating FreeRTOS+UDP to add IP functionality to an existing, FreeRTOS-based product.

I have now gotten to the point where the sources are ported over and our firmware compiles, so far so good.

One of the things I had to do in order to get there is lower the tick rate of our product from 1024Hz to 512Hz to meet the “< 1000Hz” limitation asserted in the source. Unfortunately, this has repercussions on other systems in our firmware and it is unlikely we will be able to ship with the lower tick rate.

With that in mind, I have a couple of questions on the topic:

  • What is the reason behind the limitation?
  • Is it a hard limit or could it be stretched to accomodate our 1024Hz tick rate?
  • Where would I start if I wanted to modify FreeRTOS+UDP to accomodate higher tick rates?

In the event that the information is relevant: we use a cortex M4 based STM32 MCU, and are looking at different solutions for the network connection itself (off-the-shell ethernet module for prototyping).

Thank you in advance,

François.

rtel wrote on Friday, August 21, 2015:

I am not aware of any reason why FreeRTOS+UDP would have a clock limitation, and in fact just checked the following project: http://www.freertos.org/Atmel_SAM4E_RTOS_Demo.html and that is uising a 1KHz tick.

I suspect somewhere in your code (or maybe our code?) a delay will be specified using either the pdMS_TO_CICKS() macro, or the pdTICK_RATE_MS macro (which was the old way of doing it) - and those will only work up to a 1KHz tick rate as they cannot specify a fraction of a millisecond. Once you go over 1KHz the macros no longer work, and will most likely either result in a divide by zero error, or simply a delay of 0.

Regards.

fb0 wrote on Friday, August 21, 2015:

That is very much in your code :). I am referring to these lines in FreeRTOS_UDP_IP.c:

#if configTICK_RATE_HZ > 1000
    #error configTICK_RATE_HZ must be less than 1000 to use FreeRTOS+UDP
#endif

François

heinbali01 wrote on Saturday, August 22, 2015:

Hi François,

If I were you I’d disable the configTICK_RATE_HZ sanity check and try it out.

PS. When working with 1024 Hz, please use pdMS_TO_TICKS for conversions:

    /* Converts a time in milliseconds to a time in ticks. */
    #define pdMS_TO_TICKS( xTimeInMs ) \
        ( ( ( TickType_t ) ( xTimeInMs ) * configTICK_RATE_HZ ) / ( TickType_t ) 1000 )

This older macro will fail if configTICK_RATE_HZ equals 1024:

    #define portTICK_PERIOD_MS \
        ( ( TickType_t ) 1000 / configTICK_RATE_HZ )

PS. Note that in the Labs section, you can find FreeRTOS+TCP: a TCP/IP stack which also contains UDP and much more. You can compile it for UDP-only:

#define ipconfigUSE_TCP    0

( see www.freertos.org/tcp )

Beside UDP, DHCP and some NameService protocols (LLMNR and NBNS) might be useful.

Regards.

rtel wrote on Saturday, August 22, 2015:

Ah right - the macros I referenced previously must be used inside the
stack itself.

FreeRTOS+UDP is designed to be as small as possible, and as such uses
FreeRTOS software timers to stimulate the periodic processing
necessitated by the protocols (ARP timer, etc.) - you could change the
code that specifies the frequency of these timers to not use the
portTICK_RATE_MS macro and in so doing prevent the potential for the
macro to generate a divide by zero and remove the #error.

Regards.

fb0 wrote on Saturday, August 22, 2015:

Thank you both!

I do see that portTICK_RATE_MS which is equivalent to portTICK_PERIOD_MS peppers the FreeRTOS+UDP code. I will take a stab at substituting the dozen-or-so calls to it with something based on pdMS_TO_TICKS.

Simply disabling the check is not an option as:

100 / portTICK_RATE_MS

and other such lines become divisions by zero.

I have briefly looked at FreeRTOS+TCP, but the “labs” label does not inspire confidence. How far is the project from being production-ready?

Cheers,

François.

heinbali01 wrote on Sunday, August 23, 2015:

Salut François,

…looked at FreeRTOS+TCP, but the “Labs” label does not inspire confidence.

Haha, I didn’t know that the word “Labs” could have such an effect.
The first release of +TCP was in September last year and that didn’t happen before more than a year of development and thorough testing. It was called “Labs” to reserve a bit more of freedom: features may still be added and documentation can still be completed.

Personally, I applied +TCP in an audio device (multi-channel amplifiers) where it serves HTML, FTP and streaming audio.
Many other users reported on this forum that they adopted +TCP in their projects.
Gradually we are extending the number of network interfaces for different platforms ( “/NetworkInterface.c” ).

How far is the project from being production-ready?

There will be a new release of +TCP and +FAT within 1 or 2 weeks, but you can as well start with the current one (version 150406 from http://www.freertos.org/tcp )

In the new release, +TCP will only have minor changes, most of them aesthetic:

     /* The socket type itself. */
-    typedef void *xSocket_t;
+    typedef void *Socket_t;

     /* The xSocketSet_t type is the equivalent to the fd_set type used by the
     Berkeley API. */
-    typedef void *xSocketSet_t;
-    typedef void *SocketSet_t;

+FAT will have some more structural ameliorations, and it now comes with more demo code.

Regards

heinbali01 wrote on Sunday, August 23, 2015:

Hi François,

Because it is Sunday, a little more about the tick- or clock-rate:

For a UDP stack, the clock-rate isn’t very important. The ARP tables and DHCP have relatively slow timers, in units of seconds.

For TCP however, time must be accurate: ACK’s are sometimes sent after a 200 ms delay, and retransmissions are also well-timed.
FreeRTOS+TCP has been tested mostly with a configTICK_RATE_HZ of 1000 Hz, but I assume that higher clock rates (including 1024) should work equally well.

( As you probably know… ) all FreeRTOS blocking functions have a parameter of type TickType_t. It defines the maximum number of clock ticks that a function should block.

In the TCP and UDP source code, you might find code like this:

	uint32_t ulDelayMs = 2;
    TickType_t xTickCount = pdMS_TO_TICKS( ulDelayMs );
	xSemaphoreTake( xSemaphore, xTickCount );

Waiting for 1 clock tick is tricky: the function might return within a few uS if the clock tick is about to come.
The above code will not work well with a 500 Hz clock, because pdMS_TO_TICKS(2) would return 1.
On a lower clock rate, pdMS_TO_TICKS(2) will even return 0, hence the API won’t block at all.

( NB. the prefix “pd” is an abbreviation of the source file name “projdefs.h” )

If you want to write portable code that runs with every clock speed, you might want to use the following pdMS_TO_MIN_TICKS() :

    #define pdMIN_TICKS( xTimeInTicks ) \
		( ( ( xTimeInTicks ) < ( TickType_t ) 2 ) ? ( ( TickType_t ) 2 ) : xTimeInTicks )
    #define pdMS_TO_MIN_TICKS( xTimeInMs ) \
        pdMIN_TICKS( ( ( ( TickType_t ) ( xTimeInMs ) * configTICK_RATE_HZ ) + 500 ) / ( TickType_t ) 1000 )

It rounds up and it returns a minimum value of 2. Sorry for the use of a “ternary conditional operator”, the “a?b:c” expression.

Regards.

richard_damon wrote on Sunday, August 23, 2015:

Actually, you get the same problem at higher ticks, If you want to be sure you delay at least 2 tick periods, you need to delay for 3, as a delay of 2 might return after a delay of only 1.0001 ticks. For bigger values the error is still there, it just isn’t as noticable.

Also, your routine doesn’t “Round Up” but Rounds, so with a 100 Hz tick, a request for 22ms delay will give you a 2 tick, or 20ms delay (actually a 10.0001 - 20 ms delay). If you want to be absolutely sure that you never delay less than the spec you need to really round up (add +999 and then divide by 1000) and then add 1 to account for the short first tick.

fb0 wrote on Friday, August 28, 2015:

Heins,

Thanks a lot for the help and explanations.

I spent the last few days porting FreeRTOS+TCP over, and got it mostly working using out 1024Hz tick rate. A few things worth noting however:

  1. The licensing is hazy as there is no option today to purchase a commercial license.
  2. The rate at which gratuitous ARP messages is sent is non configurable and very agressive (every 20s), causing unwarranted power drain.
  3. portTICK_PERIOD_MS is used in many place and causes problems (I had to address them manually). See below:

FreeRTOS_ARP.c:#define arpGRATUITOUS_ARP_PERIOD ( 20000 / portTICK_PERIOD_MS )

FreeRTOS_DHCP.c: #define dhcpINITIAL_TIMER_PERIOD ( 250 / portTICK_PERIOD_MS )

FreeRTOS_DHCP.c: #define dhcpINITIAL_DHCP_TX_PERIOD ( 5000 / portTICK_PERIOD_MS )

FreeRTOS_DHCP.c: #define dhcpMAX_TIME_TO_WAIT_FOR_ACK ( 5000 / portTICK_PERIOD_MS )

FreeRTOS_DHCP.c:#define dhcpDEFAULT_LEASE_TIME ( ( 48UL * 60UL * 60UL * 1000UL ) / portTICK_PERIOD_MS ) /* 48 hours in ticks. */

FreeRTOS_DHCP.c:#define dhcpMINIMUM_LEASE_TIME ( 60000UL / portTICK_PERIOD_MS ) /* 60 seconds in ticks. */

FreeRTOS_DHCP.c: (*1000) then ticks (/portTICK_PERIOD_MS). */

FreeRTOS_DHCP.c: xDHCPData.ulLeaseTime *= ( 1000UL / portTICK_PERIOD_MS );

FreeRTOS_DNS.c: xTimeout /= portTICK_PERIOD_MS;

FreeRTOS_DNS.c:TickType_t xTimeoutTime = 200 / portTICK_PERIOD_MS;

FreeRTOS_IP.c:#define ipINITIALISATION_RETRY_DELAY ( ( ( TickType_t ) 3000 ) / portTICK_PERIOD_MS )

FreeRTOS_IP.c: #define ipconfigMAX_IP_TASK_SLEEP_TIME ( 10000UL / portTICK_PERIOD_MS )

FreeRTOS_IP.c: prvIPTimerReload( &xTCPTimer, ipTCP_TIMER_PERIOD_MS / portTICK_PERIOD_MS );

FreeRTOS_IP.c: ulNextInitialSequenceNumber += ipINITIAL_SEQUENCE_NUMBER_FACTOR * ( ( xTimeNow - xStart ) * portTICK_PERIOD_MS );

FreeRTOS_IP.c: prvIPTimerReload( &xARPTimer, ipARP_TIMER_PERIOD_MS / portTICK_PERIOD_MS );

FreeRTOS_TCP_WIN.c: return ( ( xTaskGetTickCount() - pxTimer->ulBorn ) * portTICK_PERIOD_MS );

include/FreeRTOSIPConfigDefaults.h: #define ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS ( 20 / portTICK_PERIOD_MS )

include/FreeRTOSIPConfigDefaults.h: #define ipconfigMAXIMUM_DISCOVER_TX_PERIOD ( 999 / portTICK_PERIOD_MS )

include/FreeRTOSIPConfigDefaults.h: #define ipconfigMAXIMUM_DISCOVER_TX_PERIOD ( 30000 / portTICK_PERIOD_MS )

Cheers,

Francois

Edit: I cannot for the life of me get those lines to be correctly formatted, sorry :frowning:

heinbali01 wrote on Friday, August 28, 2015:

Hi François,

In the new release of FreeRTOS/Labs you’ll find the corrections made to eliminate the use of portTICK_PERIOD_MS. I hope they’re all correct now. There are only 3 occurrences of the macro left.

Please download the newest release from here: http://www.freertos.org/tcp
This ‘FreeRTOS_Labs_150825’ is updated silently: the official announcement will follow in September, when most people are back from their holidays. We should be on the beach right now :slight_smile:

NB Some type names lost their x-prefix: ‘xSocket_t’ is now called ‘Socket_t’. See also ipconfigENABLE_BACKWARD_COMPATIBILITY in include/FreeRTOS_IP.h

The rate at which gratuitous ARP messages is sent is
non configurable and very aggressive (every 20s), causing
unwarranted power drain.

I saw that too, now changed to:

#ifndef arpGRATUITOUS_ARP_PERIOD
	#define arpGRATUITOUS_ARP_PERIOD    ( pdMS_TO_TICKS( 20000 ) )
#endif

Twenty seconds is indeed extremely short. I like it while testing: after a reboot the devices make themselves known very quickly and all ARP tables are updated accordingly.
What default value should be reasonable? 5 or 10 minutes?

The licensing is hazy as there is no option today to
purchase a commercial license.

I’m sure that Richard can respond to this

Regards, Hein

fb0 wrote on Friday, August 28, 2015:

Hi Hein,

Thanks for the switf reply!

In the new release of FreeRTOS/Labs you’ll find the corrections made to eliminate the use of portTICK_PERIOD_MS. I hope they’re all correct now. There are only 3 occurrences of the macro left.

Two of those three are still troublesome (a division by 0, and a next ID incremented by 0). I will leave my typedef for portTICK_PERIOD_MS set to 1 for now.

We should be on the beach right now :slight_smile:

No rest for the wicked :wink:

I have brought the latest changes over and made the minor adjustements to my code that were needed.

Nice to see the style fall in line with the latest FreeRTOS code (one thing that did cause me some heartache though is the new FreeRTOS_errno stuff which defines success as 0, as opposed to other FreeRTOS APIs that return pdTRUE, or 1).

Also happy to see the GCC port included.

Twenty seconds is indeed extremely short. I like it while testing: after a reboot the devices make themselves known very quickly and all ARP tables are updated accordingly.
What default value should be reasonable? 5 or 10 minutes?

I think this really should be tuned on a case by case basis. Being very battery conscious, we would probably like it to happen on IP_Init, and every 10 minutes thereafter. Others I am sure would be fine with 20 seconds.

Cheers,

François.