Giter Club home page Giter Club logo

Comments (15)

Floessie avatar Floessie commented on August 12, 2024 1

Now that I've disabled configSUPPORT_DYNAMIC_ALLOCATION in my copy, the pxCurrentTCB problem popped up again. I learned, this is due to LTO. The proposed #pragma GCC solution led to an ICE, so I prepared a platform.txt for my "ATmega328 on a breadboard" board, which disables LTO completely:

compiler.c.flags=-c -g -Os {compiler.warning_flags} -std=gnu11 -ffunction-sections -fdata-sections -MMD
compiler.c.elf.flags={compiler.warning_flags} -Os -g -fuse-linker-plugin -Wl,--gc-sections
compiler.S.flags=-c -g -x assembler-with-cpp -MMD
compiler.cpp.flags=-c -g -Os {compiler.warning_flags} -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD

That works fine. Together with the patch below my two-threads example saved ~250B SRAM and about 3kB flash. I'm now at ~6kB flash and 784B SRAM all statically allocated. 👍

diff --git a/src/FreeRTOSConfig.h b/src/FreeRTOSConfig.h
index 5422e2a..2c19f93 100644
--- a/src/FreeRTOSConfig.h
+++ b/src/FreeRTOSConfig.h
@@ -92,13 +92,13 @@
 #define configMAX_PRIORITIES                ( ( UBaseType_t ) 4 )
 #define configMINIMAL_STACK_SIZE            ( ( UBaseType_t ) 85 )
 #define configIDLE_STACK_SIZE               ( ( UBaseType_t ) 192 )
-#define configMAX_TASK_NAME_LEN             ( 8 )
+#define configMAX_TASK_NAME_LEN             ( 1 )
 #define configUSE_TRACE_FACILITY            0
 #define configUSE_16_BIT_TICKS              1
 #define configIDLE_SHOULD_YIELD             1
 
 #define configUSE_MUTEXES                   1
-#define configUSE_RECURSIVE_MUTEXES         1
+#define configUSE_RECURSIVE_MUTEXES         0
 #define configUSE_COUNTING_SEMAPHORES       1
 #define configUSE_QUEUE_SETS                0
 #define configQUEUE_REGISTRY_SIZE           0
@@ -106,11 +106,11 @@
 #define configCHECK_FOR_STACK_OVERFLOW      1
 #define configUSE_MALLOC_FAILED_HOOK        1
 
-#define configSUPPORT_DYNAMIC_ALLOCATION    1
+#define configSUPPORT_DYNAMIC_ALLOCATION    0
 #define configSUPPORT_STATIC_ALLOCATION     1
 
 /* Timer definitions. */
-#define configUSE_TIMERS                    1
+#define configUSE_TIMERS                    0
 #define configTIMER_TASK_PRIORITY           ( ( UBaseType_t ) 3 )
 #define configTIMER_QUEUE_LENGTH            ( ( UBaseType_t ) 10 )
 #define configTIMER_TASK_STACK_DEPTH        configMINIMAL_STACK_SIZE

HTH,
Flössie

from arduino_freertos_library.

feilipu avatar feilipu commented on August 12, 2024 1

Thanks @Floessie. I see your pull #19 and it looks good.
I'll make a comment about it on the README soon.

There is a somewhat philosophical question to answer. Does using static Task creation make FreeRTOS easier for beginners to use? Before committing it, I will need to test it on all of the relevant ATmega versions (328p, 32u2, 32u4, 1284p, 2560) to make sure that this is the case, and that it just works for everyone.

I'd also point out that reducing the Task Name Length to a single character, and turning off the timer and recursive mutex features will save quite a bit of memory anyway, so the net RAM and Flash saving cannot be solely attributed to changing to static task RAM allocation. It would be interesting to see the net saving just from static task allocation.

Something else to look at for building a fully static FreeRTOS is the heap allocation process. I'm sure that you've noted that Arduino_FreeRTOS uses heap_3.c for its pvPortMalloc() implementation. In a fully static implementation of FreeRTOS we'd need to move this to use the heap_1.c implementation. But doing this may be detrimental to the first principal, of Making FreeRTOS easier for beginners to use, by requiring heap management across device and individual projects.

I haven't worked out a way to do flexible heap assignment (i.e. just works across all ATmega hardware) without using the tunable variables from the avr-libc library, which are picked up by the standard malloc() function. If you know a way, it would be good to include it in the pull request, for completeness.

from arduino_freertos_library.

Floessie avatar Floessie commented on August 12, 2024 1

There is a somewhat philosophical question to answer. Does using static Task creation make FreeRTOS easier for beginners to use? Before committing it, I will need to test it on all of the relevant ATmega versions (328p, 32u2, 32u4, 1284p, 2560) to make sure that this is the case, and that it just works for everyone.

That's what bothers me, too (an that's why I suggested to not default configSUPPORT_STATIC_ALLOCATION to 1). Your Blink_AnalogRead takes 355B SRAM statically with configSUPPORT_DYNAMIC_ALLOCATION, but ~1000B with both configSUPPORT_DYNAMIC_ALLOCATION and configSUPPORT_STATIC_ALLOCATION. Obviously one has to decide for one or the other, and configSUPPORT_DYNAMIC_ALLOCATION is the proper default. I'll change that in my PR accordingly.

My plan is to provide my wrapper classes prepared for both cases (with static allocation taking precedence when configSUPPORT_STATIC_ALLOCATION is defined), and to also provide a stripped down fork of your Arduino_FreeRTOS_Library with static allocation only, if I manage to solve the LTO issue without disabling it globally. With those wrappers the details are hidden and easy to grok also for beginners.

I'd also point out that reducing the Task Name Length to a single character, and turning off the timer and recursive mutex features will save quite a bit of memory anyway, so the net RAM and Flash saving cannot be solely attributed to changing to static task RAM allocation. It would be interesting to see the net saving just from static task allocation.

Absolutely. I can easily provide you with the flash numbers, but I've not yet found out, how to get the SRAM numbers for the dynamic allocation case on run time: Both MemoryFree and the freeRAM() method yield -265 with Arduino_FreeRTOS. Maybe you've got a tip for me?

Something else to look at for building a fully static FreeRTOS is the heap allocation process. I'm sure that you've noted that Arduino_FreeRTOS uses heap_3.c for its pvPortMalloc() implementation. In a fully static implementation of FreeRTOS we'd need to move this to use the heap_1.c implementation. But doing this may be detrimental to the first principal, of Making FreeRTOS easier for beginners to use, by requiring heap management across device and individual projects.

For my fully static allocation test, I simply killed heap_3.c and provided no heap_* at all. As the manual for heap_1 says, "many small and deeply embedded applications create all the tasks, queues, semaphores, etc. required when the system boots, and then use all of these objects for the lifetime of program".

Let me propose this: I'll default configSUPPORT_STATIC_ALLOCATION to 0 in my PR, try to solve the LTO issue, and see what can be done to conditionally compile heap_3.c. Then Arduino_FreeRTOS is prepared for someone who wants to swap dynamic for static allocation. But really doing so is up to the user, and for everybody else it will stay the same. 😄

I haven't worked out a way to do flexible heap assignment (i.e. just works across all ATmega hardware) without using the tunable variables from the avr-libc library, which are picked up by the standard malloc() function. If you know a way, it would be good to include it in the pull request, for completeness.

Maybe this is not even needed, if one wants to do it all statically (which sure is beneficial for such a low memory device as the 328 and maybe not so much for the 1284)?

Best,
Flössie

from arduino_freertos_library.

Floessie avatar Floessie commented on August 12, 2024 1

Hi Phillip,

I think (just a wild guess) that the difference with the 2560 may be caused by an optimisation that is not in the Release, but is in Master. I was a little enthusiastic with the attribute((flatten)) attribute, since the 2560 has plenty of Flash to spare. I rolled that back but it is not yet in the Release. It could be that is what you're seeing.

That makes sense. 👍

Merged. #19.

Thanks. As far as I'm concerned you can close this now. Nice, we made it happen!

Best,
Flössie

from arduino_freertos_library.

feilipu avatar feilipu commented on August 12, 2024

First to the configUSE_TIMERS question.
I've used a number of different timer options in my code over the years. Earlier FreeRTOS releases didn't support timers, so I grabbed timer code from Adam Dunkels' contiki to use when implementing uIP. Later, when FreeRTOS was given software timers, I've used them too.

The FreeRTOS timer solution runs in its own auto implemented mechanism that is very efficient. This means that the user doesn't have to care about running their own Task to manage software timers. This answers your question of why use timers within FreeRTOS. It is simply for the convenience. As with most things FreeRTOS, you turn a feature on or off as per your own preference. Just as a note, remember the software timer can't be have higher resolution than your system Tick. Trying to measure single milliseconds, for example, with a 15ms Tick will not be effective.

For implementing Static RAM allocation within FreeRTOS, the key thing you seem to have missed is actually allocating RAM for the Task heap and taskTCB yourself. Since RAM for the taskTCB is not allocated automatically, in the static implementation, you need to pass the address of the RAM you've prepared in advance to the xTaskCreateStatic() function.

from arduino_freertos_library.

Floessie avatar Floessie commented on August 12, 2024

The FreeRTOS timer solution runs in its own auto implemented mechanism that is very efficient. This means that the user doesn't have to care about running their own Task to manage software timers. This answers your question of why use timers within FreeRTOS. It is simply for the convenience.

Thanks for the pointer. But I'm not concerned with how efficiently this feature is implemented code-wise but with the fact, that it takes SRAM for the stack and one of the four available priorities without being needed. As always, convenience doesn't come for free, it seems.

As with most things FreeRTOS, you turn a feature on or off as per your own preference.

I know. But there doesn't seem to be a way to do that with your code on a per project basis. If there was a #ifndef config* around the #defines one could override them before including Arduino_FreeRTOS.h.

Just as a note, remember the software timer can't be have higher resolution than your system Tick. Trying to measure single milliseconds, for example, with a 15ms Tick will not be effective.

Sure.

For implementing Static RAM allocation within FreeRTOS, the key thing you seem to have missed is actually allocating RAM for the Task heap and taskTCB yourself. Since RAM for the taskTCB is not allocated automatically, in the static implementation, you need to pass the address of the RAM you've prepared in advance to the xTaskCreateStatic() function.

No, I haven't. That's why I had to move #include "FreeRTOSVariant.h" to the end of Arduino_FreeRTOS.h to make StaticTask_t available before task.h is included.

We rolled out a FreeRTOS based project at work some years ago, and I was i.a. responsible for implementing the threading wrapper (porting over from pthreads and earlier from eCos). Now I'm writing a C++11 wrapper for Arduino_FreeRTOS for fun to put in some convenience with low overhead (using static polymorphism) and wanted to use the static variants, as stack size is always known at compile time. This would mean more control at compile time like the documentation says:

  • The maximum RAM footprint can be determined at link time, rather than run time.

Thanks for your quick feedback. Much appreciated.

Best,
Flössie

from arduino_freertos_library.

feilipu avatar feilipu commented on August 12, 2024

But I'm not concerned with how efficiently this feature is implemented code-wise but with the fact, that it takes SRAM for the stack and

Yes it does take RAM for the (auto generated) timer Task. But, as noted it is very simple to turn it off, and roll your own Timers.

uses one of the four available priorities without being needed.

Perhaps you're getting confused by the FreeRTOS priority mechanism? You can have as many priorities as you choose. There is no maximum. For conservation the of RAM on the Arduino platform, and based on my own experience, I've selected four as a sensible maximum.

Also, there is no limitation to the number of Tasks that can share the same priority. Tasks with the same priority will share execution time in round-robin fashion. So the number of separate Tasks is not linked to the number of different priorities.

As with most things FreeRTOS, you turn a feature on or off as per your own preference.
I know. But there doesn't seem to be a way to do that with your code on a per project basis.

It is actually quite easy to change the configuration on a compile by compile basis, but you have to know where to look. FreeRTOS configuration files are stored in the ~/Arduino/libraries/FreeRTOS/src directory in your Arduino installation. As every library is recompiled on each time you do an Arduino build, all you have to do is change the configuration in the FreeRTOSConfig.h file in src folder to get whatever you need.

I have enabled everything that is sensible, in line with the Arduino philosophy that things should just work for beginners. For personal projects I only turn on what I need. If useful features were turned off, then people would wonder why something wasn't working and complain. Up until today that policy seems to be working, as no one has ever asked "Why doesn't xxx work in Arduino_FreeRTOS?"

Good luck with the static polymorphism. Way outside my experience, I'm afraid.
I'm focused on learning Z80 assembler currently.

from arduino_freertos_library.

Floessie avatar Floessie commented on August 12, 2024

Perhaps you're getting confused by the FreeRTOS priority mechanism?

Perhaps I just sounded a bit silly. Let's stop the discussion about timers here.

It is actually quite easy to change the configuration on a compile by compile basis, but you have to know where to look.

I know where to look, otherwise I wouldn't have been able to change the configs (see my initial post).

As every library is recompiled on each time you do an Arduino build, all you have to do is change the configuration in the FreeRTOSConfig.h file in src folder to get whatever you need.

Yeah, but I don't want to edit the library code when switching from one project to the next. That's not how a library works. Thus my suggestion to #ifndef the configs, so one can optionally define them before including Arduino_FreeRTOS.h on a per project basis.

I have enabled everything that is sensible, in line with the Arduino philosophy that things should just work for beginners.

Having the means for overriding those defaults on a per project basis would also attract non-beginners.

If useful features were turned off, then people would wonder why something wasn't working and complain. Up until today that policy seems to be working, as no one has ever asked "Why doesn't xxx work in Arduino_FreeRTOS?"

Well, then let me be the first: Why doesn't static allocation work in Arduino_FreeRTOS?

Good luck with the static polymorphism.

Thanks. Together with LTO it has no overhead currently. virtual dispatch added 34B for two threads.

I'm focused on learning Z80 assembler currently.

Saw it on your blog. Impressive! Next stop monsterZ80... 😉

Thanks for your quick replies, although they are a bit too exegetical for me. I know some basics... 😁

Best,
Flössie

from arduino_freertos_library.

Floessie avatar Floessie commented on August 12, 2024

Sorry, didn't know there isn't a way to pass definitions from "sketch" to library in the Arduino IDE. 😖

Nevertheless, I was able to make it build with configSUPPORT_STATIC_ALLOCATION, see #19. Feel free to not merge it with #define configSUPPORT_STATIC_ALLOCATION 1, but maybe consider the other changes that were necessary to make it work.

HTH,
Flössie

from arduino_freertos_library.

feilipu avatar feilipu commented on August 12, 2024

Let me propose this: I'll default configSUPPORT_STATIC_ALLOCATION to 0 in my PR, try to solve the LTO issue, and see what can be done to conditionally compile heap_3.c.

Standing by.

I've not yet found out, how to get the SRAM numbers for the dynamic allocation case on run time: Both MemoryFree and the freeRAM() method yield -265 with Arduino_FreeRTOS. Maybe you've got a tip for me?

I can only suggest compiling with dynamic allocation using heap_1.c and then the heap management API of FreeRTOS is available. Use xPortGetMinimumEverFreeHeapSize() to see how much heap is free, from what you've allocated when declaring the heap in configTOTAL_HEAP_SIZE. This method ensures that you're capturing the entire heap RAM usage of the solution, provided that there are no direct calls to malloc() or similar sins.

This example below for 328p, but I have different definitions available depending on the ATmega platform, and whether or not extended RAM is used (for the 2560).
uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];

Very interested to see the comparisons.

from arduino_freertos_library.

Floessie avatar Floessie commented on August 12, 2024

I can only suggest compiling with dynamic allocation using heap_1.c and then the heap management API of FreeRTOS is available. Use xPortGetMinimumEverFreeHeapSize() to see how much heap is free, from what you've allocated when declaring the heap in configTOTAL_HEAP_SIZE. This method ensures that you're capturing the entire heap RAM usage of the solution, provided that there are no direct calls to malloc() or similar sins.

Unfortunately, you can't use heap_1.c without configSUPPORT_DYNAMIC_ALLOCATION. At least the source reads:

#if( configSUPPORT_DYNAMIC_ALLOCATION == 0 )
	#error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0
#endif

But here are the numbers for my two demo tasks (85B + 192B stack size) without LTO:

  • static: 9644 / 1047
  • dynamic: 10152/726

With defaults restored and heap_3.c.

from arduino_freertos_library.

Floessie avatar Floessie commented on August 12, 2024

Standing by.

Done in #19.

from arduino_freertos_library.

feilipu avatar feilipu commented on August 12, 2024

I'll have a look at the difference between heap_1.c and heap_3.c with dynamic allocation soon. That will provide a good view of the differences in RAM and Flash across those two options.

In my personal projects I never use heap_3.c because it sucks in all of the avr-libc malloc() code (and its friends calloc() and realloc()) which adds a quite few kB of Flash on any project. It is only used in this library because of the avr-libc auto variables enabling configuration free usage across ATmega devices.

Failing me getting to test #19 fully in the short term (holidays next week), is there any reason why it wouldn't work across all ATmega hardware, without further configuration? Can you check, please?

Also, since it there is a different usage model for using FreeRTOS features, with static allocation, which means the user has to be a lot more advanced and provide their own memory plan, I'm tempted to create a "Static AVR_FreeRTOS" branch and put #19 on that. But, I'll have to work on my very limited Gitfu to do that.

from arduino_freertos_library.

Floessie avatar Floessie commented on August 12, 2024

Failing me getting to test #19 fully in the short term (holidays next week), is there any reason why it wouldn't work across all ATmega hardware, without further configuration? Can you check, please?

No, that was my goal, but of course I can't prove that it works on every ATmega hardware. Anyway, here are some compile tests with Blink_AnalogRead.ino:

Board Vanilla flash/SRAM Vanilla *.hex MD5 #19 Vanilla flash/SRAM #19 *.hex MD5
Yun (atmega32u4) 11462/320 d0633a70625effeedb6ff45db36bdad1 11462/320 d0633a70625effeedb6ff45db36bdad1
Uno (atmega328p) 8930/356 746dbf2451179088f9354d54a489e355 8930/356 746dbf2451179088f9354d54a489e355
Nano (atmega168) 8930/356 40f205460509f2ddd3f32c9eadf0f23d 8930/356 40f205460509f2ddd3f32c9eadf0f23d
Mega (atmega2560) 30756/356 b1b9089148d7a8d570e7b8acd2e2cf00 9416/356 96a8c53d987f87cacff6b11db7613b06
Mega (atmega1280) 9584/356 33fe04467a18e7f0759146975fda787b 9584/356 33fe04467a18e7f0759146975fda787b
Mighty (atmega1284p) 9128/356 f0fd99e6b5066152f9b1e4fc176f318f 9128/356 f0fd99e6b5066152f9b1e4fc176f318f

I checked atmega2560 twice, but the difference is real. Don't know, what you'll do with that. Maybe you have a Mega to test with. Otherwise I'm pretty confident, that those changes don't have an impact. 😄

Best,
Flössie

from arduino_freertos_library.

feilipu avatar feilipu commented on August 12, 2024

I think (just a wild guess) that the difference with the 2560 may be caused by an optimisation that is not in the Release, but is in Master. I was a little enthusiastic with the __attribute__((flatten)) attribute, since the 2560 has plenty of Flash to spare. I rolled that back but it is not yet in the Release. It could be that is what you're seeing.

Merged. #19.

from arduino_freertos_library.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.