[En-Nut-Discussion] Run-time or compile-time configurable devices?

Ulrich Prinz ulrich.prinz at googlemail.com
Wed Jan 4 16:34:05 CET 2012


To clarify that:

The devices need buffers, local variables and pointers and so on. They
are linked in the DCB and / or ICB structs for every device.

If you declare all this during compile time you have these structs
registered and reserved in RAM or at least their pointers.
This might be inconvenient if you have 6 USARTs but use only one of them.

So initialization during runtime not only enables to reserve the devices
resources only when you need them, you are able to unregister them later
if you not need them anymore.

In I2C and SPI drivers this is already a part of the way they work in
USART it is still missing cause the USART is hard linked to the stdio or
terminal driver.
Later only one USART driver should be linked into the code and the
respective USART hardware is linked by a dynamic entry in the DCB as a
register vector.
This then costs a few bytes RAM and frees kilobytes of FLASH.
But it requires runtime registration of the device.

Am 03.01.2012 21:02, schrieb Uwe Bonnes:
>>>>>> "Harald" == Harald Kipp <harald.kipp at egnite.de> writes:
> 
>     Harald> Hi Uwe, On 03.01.2012 15:48, Uwe Bonnes wrote:
> 
>     Harald> Including
> 
>     Harald>   NutRegisterDevice(&DEV_UART4, 0, 0);
> 
>     Harald> will add a dependency at compile time and thus add the UART
>     Harald> driver to the final binary.

Right, accessing the NutRegisterDevice( &DEV_UART4...
causes the linker to see that this device is addressed and therefore the
code must be linked.
> 
> My question was:
> 
> Are there application, that read a pointer to a device during runtime and
> then open that device during run-time. That way, the code has to do
> decisions caused on different device pointers during runtime.
> The NutOS examples I have looked at only define the pointer to the device at
> compile times. So these decisions can be done during compile time.
> 
Yes right. All devices get logical names and are addressed through this.
The names are made up as #defines as they are different accross chips
and architectures. It is easier to use DEV_XYZ instead of having to
remember dev_Stm32Usart1 or dev_at91Uart4 or something like that.

> The AVR is no good example. But the STM32 has e.g. zillions of timers in
> groups with same capabilities in the group and slightly different
> capabilities between groups. If the used timer is changable
> (let's assume only in the group) at runtime, we either have to carry code to
> handle all timers of that group or we need a wrapper that handles that group
> and has to do decisions based on the used timer during runtime.

Why not keeping it with the DCB?
If you need some local controls and a vector to the timer base register,
then
typedef struct {
  uint32_t base;
} DCB_STM32TIMER;

Then you write a code for one of the groups of timers and you can
register them with
NutRgeisterTimer( &DEV_TIM4...
while NutRegisterTimer then sets the base variable to the addressed
timers base register.

Benefit:
You need to write only one driver per group and you might be able to
recycle the same code for another group of timers by adding only some
#ifdef.
The user can register as much timers as he likes and only adds the flash
footprint for one timer group.

Benefit:
By collecting all devices variables in a DCB or ICB, you need not to
search like crasy to find all the used and hidden variables.
If you hardcode the variables as local variables in you C file and you
recycle that complete file for all devices of this type, you get grey
hear if you run a GDB session on that code while multiple of the devices
are active the same time.

Benefit:

If you add some configuration framework like I intend for the USARTs,
you spare a lot of RAM as you will forget to surround your local
variables with #ifdef depending on the enabled features.

> 
> If there are only use-cases that change the timer during compile time,
> either only _one_ timer has to be included in the code or the device group
> wrapper can do the decision during compile time.

I don't understand that question...
The register function causes the linker to add the timer for the first
time at all and it will add all referenced functions, variables....

May be you aask if there is a usecase that someone decides to use a
timer and then use it for something very different... I don't know. But
I guess that it would mot make much difference in the code size if you
keep it like I decribed it with thw USARTs:

In nutconf you select alle the features that a timer should present.
Based on that the appropriate code lines and functions are enabled for
compilation.
Then the user selects by the register or control function which timer
should do what.

Ok, practical example I need in near future:
On one timer I need PWM for DC/DC supply in software.
On a second Timer I need PWM for LED control.
On a third Timer I need a coloumb counter.

So in Nut/OS I like to select that I need the PWM feature, the dead-time
feature and the input counter feature.

Then I setup the DC/DC timer with PWM and deadtime, the LED timer for
PWM only and the coloumb-counter as input.

What was a discussion with harald last year:

Yes you might have a small footprint impact of the resulting code for
first driver that is linked and the code is not always easy to write and
to read as it gets a lot #ifdef.
But in the end the resulting Flash footprint is very much smaller as
there is only one USART, one SPI, one I2C and one Timer code in the flash.
And with 3 SPIs, 2 I2Cs, 5 USARTs... It saves a lot!

Best regards
Ulrich



More information about the En-Nut-Discussion mailing list