You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@mynewt.apache.org by Christopher Collins <cc...@apache.org> on 2016/08/29 21:23:50 UTC

Mynewt system initialization

Hello all,

This will be a long email about changes to the way Mynewt performs system
initialization.  Any and all feedback is greatly appreciated.

Proposal:
    1. Move system initialization out of apps and into BSPs and
       system-level packages.
    2. Specify system settings in config file.

Motivations:
    1. Simplify process of creating an app (no more boilerplate code).
    2. Simplify system configuration (don't specify config within C
       code).
    3. Better code organization; cputime, msys, etc. "feel" more like
       system-level modules than application-level, so put
       initialization code where it belongs.

Examples of "system modules" are:
    * mbufs / msys.
    * Network stacks (e.g., NimBLE host, controller, and HCI transport).
    * newtmgr
    * console
    * logging
    * drivers

The biggest challenge is: ensuring the developer maintains precise
control over the system configuration.  The current Mynewt scheme gives
control to the developer by pushing configuration up to the application
level where he is forced to deal with it.

### Proposal Summary

System initialization happens in two stages:
    1. BSP init (bsp_init())
    2. sysinit (sysinit())

(1) BSP init sets up everything that is BSP specific:
    * UARTs
    * Flash map
    * Other hardware drivers

(2) Sysinit sets up all the libraries which aren't BSP or hardware
dependent (os, msys, network stacks, etc).  Sysinit is implemented in
the sys/sysinit package.

The system knows what to initialize and how to initialize it based on
the contents of the following header file:

    <target-path>/include/syscfg/syscfg.h

For example:

    targets/bleprph-nrf52dk/include/syscfg/syscfg.h

This header file is generated by newt during the build process.  Newt
arranges for all packages to have access to this header file.

The syscfg.h header file consists of three sections:
    * Settings
    * Indication of which packages are present
    * Indication of which APIs are available

Here is an excerpt from a sample syscfg.h file (the actual file is much
larger):

    /*** Settings */

    #ifndef MYNEWT_CLOCK_FREQ
    #define MYNEWT_CLOCK_FREQ (1000000)
    #endif

    #ifndef MYNEWT_MSYS_1_BLOCK_COUNT
    #define MYNEWT_MSYS_1_BLOCK_COUNT (15)
    #endif

    #ifndef MYNEWT_MSYS_1_BLOCK_SIZE
    #define MYNEWT_MSYS_1_BLOCK_SIZE (260)
    #endif

    /*** Packages */

    #ifndef MYNEWT_PKG_LIBS_NEWTMGR
    #define MYNEWT_PKG_LIBS_NEWTMGR (1)
    #endif

    #ifndef MYNEWT_PKG_LIBS_NEWTMGR_TRANSPORT_BLE
    #define MYNEWT_PKG_LIBS_NEWTMGR_TRANSPORT_BLE (1)
    #endif

    #ifndef MYNEWT_PKG_SYS_CONFIG
    #define MYNEWT_PKG_SYS_CONFIG (1)
    #endif

    /*** APIs */

    #ifndef MYNEWT_API_BLE_DRIVER
    #define MYNEWT_API_BLE_DRIVER (1)
    #endif

    #ifndef MYNEWT_API_BLE_TRANSPORT
    #define MYNEWT_API_BLE_TRANSPORT (1)
    #endif

    #ifndef MYNEWT_API_CONSOLE
    #define MYNEWT_API_CONSOLE (1)
    #endif


### Syscfg header file generation

Newt generates the syscfg.h file by parsing a series of "syscfg.yml"
files.  Any package may contain a syscfg.yml file in its directory.
A syscfg.yml file defining the three settings shown above might look
like this:

    MYNEWT_CLOCK_FREQ:          1000000
    MYNEWT_MSYS_1_BLOCK_COUNT:  12
    MYNEWT_MSYS_1_BLOCK_SIZE:   260

If several packages define the same setting, the tie is broken according
to package priority.  Packages are prioritized as follows (lowest
priority first, highest last):

    1. Library
    2. BSP
    3. App
    4. Target

The expectation is that libraries would define sensible defaults in
their syscfg.yml files, and BSPs, apps, and targets would override those
defaults.

To prevent unnecessary rebuilds, newt only overwrites the header file if
there are any configuration changes since the previous build.

### newt target config command

Newt would have a new command: newt target config <target-name>.  This
command shows the following information about each setting for the
specified target:

    * Default value and package which specifies it.
    * Actual value and package which specifies it.

### System init C code

The system init code conditionally initializes modules in sequence.
Here is an example of how this might look:

    #if MYNEWT_PKG_SYS_ID
        id_init();
    #endif

    #if MYNEWT_API_CONSOLE
        rc = console_init(NULL);
        assert(rc == 0);
    #endif

    #ifdef MYNEWT_REBOOT_LOG_0_TYPE
        reboot_init_handler(MYNEWT_REBOOT_LOG_0_TYPE,
                            MYNEWT_REBOOT_LOG_0_ENTRY_COUNT);
    #endif

    #if MYNEWT_PKG_LIBS_SHELL
        shell_task_init();
    #endif

Each module's init function (e.g., shell_task_init()) also reads the
syscfg.h header to know how to configure the module.

### Questions / issues

Here are some issues that I'm struggling with:

1. Settings lack structure.

Do the syscfg.yml files need more information?  Ideally (in my opinion),
each setting would be defined by its package with the following
information:
    * Name
    * Default value
    * Description
    * Restrictions

By "restrictions", I mean prerequisites for enabling a particular
setting.  For example, it is illegal to configure the config module to
use nffs if the fs/nffs package is not pulled into the build.  It would
be nice if newt could catch this configuration error with a helpful
message rather than letting the compiler and linker generate
incomprehensible errors.

Thinking about this "richer" syscfg format is what got me worrying that
this feature may be getting way too complicated.  Even though it might
be messy to change the syscfg format in the future, I don't think it is
a good idea to try to implement the super-complicated option for 1.0.

2. Default task priorities.

A package's default settings reside in the package's syscfg.yml file.
Among these settings is default task priority.  I don't think it is
possible to assign meaningful default priorities.  I see two options:

    * Don't specify default priorities.  Force the user to specify all
      priorities in the bsp/app/target syscfg.yml file.

    * Allow an "any" priority value to be specified.  While generating
      the syscfg.h file, newt would assign unused values to priorities
      marked as "any".  Tasks with strict timing requirements (e.g., the
      nimble controller) would specify actual values.  Other tasks
      (e.g., shell) would use "any".

I think the second option is better, but requires more work.

3. General flash map problems.

Currently it is assumed that all parts of a BSP (C code, linker script,
and bash scripts) have the same flash map definition.  It would be nice
if the flash map could be specified once in a single place.

Another problem concerns initialization of some flash-dependent
packages.  For example, if the sys/config package is configured to use
FCB, something needs to indicate where in flash the FCB goes.  With the
flash map definition living in C code, is not really feasible to specify
this in the system configuration yaml files.

Even though I see this as a problem, I am OK with keeping flash-specific
stuff in BSP C code for now.

Thanks,
Chris

Re: Mynewt system initialization

Posted by will sanfilippo <wi...@runtime.io>.
All sounds good. I am fine with 0 seeing as you detect conflicts but we have had discussions about allowing tasks to have the same priority and doing some form of time slicing/round-robin/whatever. If we did that we would need a way of saying “my task is special and you cant have a priority the same as mine”. We can punt on that for now too :-)

IRQ stuff all good.
 

> On Sep 2, 2016, at 8:49 AM, Christopher Collins <cc...@apache.org> wrote:
> 
> Hi Will,
> 
> Oops, somehow I missed this response.  Your feedback is greatly
> appreciated.  I have some comments below.
> 
> Thanks,
> Chris
> 
> On Tue, Aug 30, 2016 at 09:23:45AM -0700, will sanfilippo wrote:
>> Just a few comments. Overall I think it looks great.
>> 
>> 1) Task priorities
>> I like the concept of “any” and I also wonder if there should be the
>> concept of “highest”. The user should have the ability to see all the
>> tasks and the priorities they have been assigned  and they should be
>> able to override them (it seems from your mail that all this would be
>> the case). If two packages are included that have priority “highest”,
>> that is a conflict that the developer should have to resolve. I dont
>> know if there is also a need to reserve some priority numbers for the
>> OS itself but I would say not to start.
> 
> (I realize you wrote this email before I sent my most recent proposal,
> so my comments should be read in that context.  I apologize for any
> confusion I cause here.
> 
> By the way, after some thought, I don't think the task priority system I
> described in my last email is very good.  Rather than using special
> "PRIORITY_" values to distinguish these settings from others, the
> setting definition should indicate that it is a task priority setting.
> The setting values would then just be plain numbers or "any".  This
> would leave more room for additional "special" settings, rather than
> assuming that task priorities are the only ones.)
> 
> Would the "highest" value be different from a value of 0?  The task
> priority system that I described in my most recent email detects
> duplicate values among all priorities.  I can think of at least one real
> benefit for a "highest" value: it eliminates the uncertainty of whether
> priorities start at 0 or 1.  I am not sure this is a good enough
> justification for another special value, though!
> 
>> 2) Interrupt priorities
>> This is always a fun thing and I think sort of belongs in the init
>> discussion. I think they are like task priorities in a way and I think
>> it is difficult to assign deault priorities to them. Well, that is not
>> entirely true. We could have something like highest, lowest, etc and
>> the tool, knowing the architecture and the possible priorities,
>> assigns them. And just like tasks, the users can override them.
> 
> I completely agree - syscfg should specify interrupt priorities.  I
> think the mechanism here could be mostly identical to how task
> priorities are specified, with one exception: no prohibition against
> using the same priority twice.  The system would allow multiple
> interrupts to be configured with the same priority.


Re: Mynewt system initialization

Posted by Christopher Collins <cc...@apache.org>.
Hi Will,

Oops, somehow I missed this response.  Your feedback is greatly
appreciated.  I have some comments below.

Thanks,
Chris

On Tue, Aug 30, 2016 at 09:23:45AM -0700, will sanfilippo wrote:
> Just a few comments. Overall I think it looks great.
> 
> 1) Task priorities
> I like the concept of \u201cany\u201d and I also wonder if there should be the
> concept of \u201chighest\u201d. The user should have the ability to see all the
> tasks and the priorities they have been assigned  and they should be
> able to override them (it seems from your mail that all this would be
> the case). If two packages are included that have priority \u201chighest\u201d,
> that is a conflict that the developer should have to resolve. I dont
> know if there is also a need to reserve some priority numbers for the
> OS itself but I would say not to start.

(I realize you wrote this email before I sent my most recent proposal,
so my comments should be read in that context.  I apologize for any
confusion I cause here.

By the way, after some thought, I don't think the task priority system I
described in my last email is very good.  Rather than using special
"PRIORITY_" values to distinguish these settings from others, the
setting definition should indicate that it is a task priority setting.
The setting values would then just be plain numbers or "any".  This
would leave more room for additional "special" settings, rather than
assuming that task priorities are the only ones.)

Would the "highest" value be different from a value of 0?  The task
priority system that I described in my most recent email detects
duplicate values among all priorities.  I can think of at least one real
benefit for a "highest" value: it eliminates the uncertainty of whether
priorities start at 0 or 1.  I am not sure this is a good enough
justification for another special value, though!

> 2) Interrupt priorities
> This is always a fun thing and I think sort of belongs in the init
> discussion. I think they are like task priorities in a way and I think
> it is difficult to assign deault priorities to them. Well, that is not
> entirely true. We could have something like highest, lowest, etc and
> the tool, knowing the architecture and the possible priorities,
> assigns them. And just like tasks, the users can override them.

I completely agree - syscfg should specify interrupt priorities.  I
think the mechanism here could be mostly identical to how task
priorities are specified, with one exception: no prohibition against
using the same priority twice.  The system would allow multiple
interrupts to be configured with the same priority.

Re: Mynewt system initialization

Posted by will sanfilippo <wi...@runtime.io>.
Just a few comments. Overall I think it looks great.

1) Task priorities
I like the concept of “any” and I also wonder if there should be the concept of “highest”. The user should have the ability to see all the tasks and the priorities they have been assigned  and they should be able to override them (it seems from your mail that all this would be the case). If two packages are included that have priority “highest”, that is a conflict that the developer should have to resolve. I dont know if there is also a need to reserve some priority numbers for the OS itself but I would say not to start.

2) Interrupt priorities
This is always a fun thing and I think sort of belongs in the init discussion. I think they are like task priorities in a way and I think it is difficult to assign deault priorities to them. Well, that is not entirely true. We could have something like highest, lowest, etc and the tool, knowing the architecture and the possible priorities, assigns them. And just like tasks, the users can override them.


> On Aug 29, 2016, at 7:29 PM, Christopher Collins <cc...@apache.org> wrote:
> 
> On Tue, Aug 30, 2016 at 12:35:31AM +0000, paul@wrada.com wrote:
>> Would you expect that everyone that writes a new core package, would add
>> their init code to sys/sysinit package and include their package in the
>> dependencies for sys/sysinit?  Would that mean that if I include
>> sys/sysinit as a dependency in my app (every app probably would), it
>> automatically becomes dependent on all packages, or would this be special
>> somehow and not be necessary.
> 
> I think the sysinit package should have access to all packages in the
> build.  Newt would have to do some magic to arrange for this (perhaps a
> setting in the pkg.yml file).  The sysinit package wouldn't explicitly
> depend on these packages, it would just use whatever is already getting
> pulled in.
> 
>> Does it make sense to have the sysinit.yml file include the "init method
>> name² for the package and have newt build that sysintit.c file special for
>> your target?  This has the advantage that we are not coupling these
>> projects together or using tons of ifdefs which make the code hard to read
>> (its hard to teach my idea about defines that newt creates). It raises the
>> issue of order though since we can¹t enforce an order if its generated
>> like this. We would also have to make these a fixed prototype to make it
>> easy (I think it would be easy to make the int foo(void)).
> 
> That is not a bad idea at all.  It does bother me that sysinit needs
> code to initialize every core package, and this seems like a good
> solution to that problem.
> 
> You are right about the ordering issue.  One option is for each system
> package cto have a numeric "stage" setting.  The sysinit code would
> initialize every package in stage 1, before those in stage 2, and so on.
> Within a stage, packages would be initialized in alphabetical order (or
> whatever).
> 
>> Would non-core packages put their init code in sys/sysinit?  Or is this
>> just for os-core stuff?
> 
> I think if non-core packages can leverage the sysinit feature, then they
> should.  I was thinking it would just be core packages because sysinit
> would need special code for each package.  However, you init function
> idea might be a way to solve this issue and allow other packages to use
> it.
> 
>> My preference is to put some thought into the yml file format for future.
>> We¹ve got tons of packages and its may be a pain to make a drastic change
>> later.  We don¹t need to add the features now, just make sure the yml file
>> has the hierarchy to support the things we are thinking about now.
> 
> Thanks,
> Chris


Re: Mynewt system initialization

Posted by Christopher Collins <cc...@apache.org>.
On Tue, Aug 30, 2016 at 12:35:31AM +0000, paul@wrada.com wrote:
> Would you expect that everyone that writes a new core package, would add
> their init code to sys/sysinit package and include their package in the
> dependencies for sys/sysinit?  Would that mean that if I include
> sys/sysinit as a dependency in my app (every app probably would), it
> automatically becomes dependent on all packages, or would this be special
> somehow and not be necessary.

I think the sysinit package should have access to all packages in the
build.  Newt would have to do some magic to arrange for this (perhaps a
setting in the pkg.yml file).  The sysinit package wouldn't explicitly
depend on these packages, it would just use whatever is already getting
pulled in.

> Does it make sense to have the sysinit.yml file include the "init method
> name for the package and have newt build that sysintit.c file special for
> your target?  This has the advantage that we are not coupling these
> projects together or using tons of ifdefs which make the code hard to read
> (its hard to teach my idea about defines that newt creates). It raises the
> issue of order though since we cant enforce an order if its generated
> like this. We would also have to make these a fixed prototype to make it
> easy (I think it would be easy to make the int foo(void)).

That is not a bad idea at all.  It does bother me that sysinit needs
code to initialize every core package, and this seems like a good
solution to that problem.

You are right about the ordering issue.  One option is for each system
package cto have a numeric "stage" setting.  The sysinit code would
initialize every package in stage 1, before those in stage 2, and so on.
Within a stage, packages would be initialized in alphabetical order (or
whatever).

> Would non-core packages put their init code in sys/sysinit?  Or is this
> just for os-core stuff?

I think if non-core packages can leverage the sysinit feature, then they
should.  I was thinking it would just be core packages because sysinit
would need special code for each package.  However, you init function
idea might be a way to solve this issue and allow other packages to use
it.

> My preference is to put some thought into the yml file format for future.
> Weve got tons of packages and its may be a pain to make a drastic change
> later.  We dont need to add the features now, just make sure the yml file
> has the hierarchy to support the things we are thinking about now.

Thanks,
Chris

Re: Mynewt system initialization

Posted by "paul@wrada.com" <pa...@wrada.com>.
Thanks Chris,

I think this is a great step in the right direction. I¹m looking forward
to seeing it.

Just a few quick questions/comments about sysinit.

Would you expect that everyone that writes a new core package, would add
their init code to sys/sysinit package and include their package in the
dependencies for sys/sysinit?  Would that mean that if I include
sys/sysinit as a dependency in my app (every app probably would), it
automatically becomes dependent on all packages, or would this be special
somehow and not be necessary.

Does it make sense to have the sysinit.yml file include the "init method
name² for the package and have newt build that sysintit.c file special for
your target?  This has the advantage that we are not coupling these
projects together or using tons of ifdefs which make the code hard to read
(its hard to teach my idea about defines that newt creates). It raises the
issue of order though since we can¹t enforce an order if its generated
like this. We would also have to make these a fixed prototype to make it
easy (I think it would be easy to make the int foo(void)).


Would non-core packages put their init code in sys/sysinit?  Or is this
just for os-core stuff?

My preference is to put some thought into the yml file format for future.
We¹ve got tons of packages and its may be a pain to make a drastic change
later.  We don¹t need to add the features now, just make sure the yml file
has the hierarchy to support the things we are thinking about now.

Paul

On 8/29/16, 4:53 PM, "marko kiiskila" <ma...@runtime.io> wrote:

>Hi,
>
>for bootloader I¹d like to have different set of devices than the app.
>What did you have in mind for that? Different bsp_init() routines?
>Or would bsp_init() have conditional parts depending on syscfg.h says?
>
>> On Aug 29, 2016, at 2:23 PM, Christopher Collins <cc...@apache.org>
>>wrote:
>> 
>> Hello all,
>> 
>> This will be a long email about changes to the way Mynewt performs
>>system
>> initialization.  Any and all feedback is greatly appreciated.
>> 
>> Proposal:
>>    1. Move system initialization out of apps and into BSPs and
>>       system-level packages.
>>    2. Specify system settings in config file.
>> 
>> Motivations:
>>    1. Simplify process of creating an app (no more boilerplate code).
>>    2. Simplify system configuration (don't specify config within C
>>       code).
>>    3. Better code organization; cputime, msys, etc. "feel" more like
>>       system-level modules than application-level, so put
>>       initialization code where it belongs.
>> 
>> Examples of "system modules" are:
>>    * mbufs / msys.
>>    * Network stacks (e.g., NimBLE host, controller, and HCI transport).
>>    * newtmgr
>>    * console
>>    * logging
>>    * drivers
>> 
>> The biggest challenge is: ensuring the developer maintains precise
>> control over the system configuration.  The current Mynewt scheme gives
>> control to the developer by pushing configuration up to the application
>> level where he is forced to deal with it.
>> 
>> ### Proposal Summary
>> 
>> System initialization happens in two stages:
>>    1. BSP init (bsp_init())
>>    2. sysinit (sysinit())
>> 
>> (1) BSP init sets up everything that is BSP specific:
>>    * UARTs
>>    * Flash map
>>    * Other hardware drivers
>> 
>> (2) Sysinit sets up all the libraries which aren't BSP or hardware
>> dependent (os, msys, network stacks, etc).  Sysinit is implemented in
>> the sys/sysinit package.
>> 
>> The system knows what to initialize and how to initialize it based on
>> the contents of the following header file:
>> 
>>    <target-path>/include/syscfg/syscfg.h
>> 
>> For example:
>> 
>>    targets/bleprph-nrf52dk/include/syscfg/syscfg.h
>> 
>> This header file is generated by newt during the build process.  Newt
>> arranges for all packages to have access to this header file.
>> 
>> The syscfg.h header file consists of three sections:
>>    * Settings
>>    * Indication of which packages are present
>>    * Indication of which APIs are available
>> 
>> Here is an excerpt from a sample syscfg.h file (the actual file is much
>> larger):
>> 
>>    /*** Settings */
>> 
>>    #ifndef MYNEWT_CLOCK_FREQ
>>    #define MYNEWT_CLOCK_FREQ (1000000)
>>    #endif
>> 
>>    #ifndef MYNEWT_MSYS_1_BLOCK_COUNT
>>    #define MYNEWT_MSYS_1_BLOCK_COUNT (15)
>>    #endif
>> 
>>    #ifndef MYNEWT_MSYS_1_BLOCK_SIZE
>>    #define MYNEWT_MSYS_1_BLOCK_SIZE (260)
>>    #endif
>> 
>>    /*** Packages */
>> 
>>    #ifndef MYNEWT_PKG_LIBS_NEWTMGR
>>    #define MYNEWT_PKG_LIBS_NEWTMGR (1)
>>    #endif
>> 
>>    #ifndef MYNEWT_PKG_LIBS_NEWTMGR_TRANSPORT_BLE
>>    #define MYNEWT_PKG_LIBS_NEWTMGR_TRANSPORT_BLE (1)
>>    #endif
>> 
>>    #ifndef MYNEWT_PKG_SYS_CONFIG
>>    #define MYNEWT_PKG_SYS_CONFIG (1)
>>    #endif
>> 
>>    /*** APIs */
>> 
>>    #ifndef MYNEWT_API_BLE_DRIVER
>>    #define MYNEWT_API_BLE_DRIVER (1)
>>    #endif
>> 
>>    #ifndef MYNEWT_API_BLE_TRANSPORT
>>    #define MYNEWT_API_BLE_TRANSPORT (1)
>>    #endif
>> 
>>    #ifndef MYNEWT_API_CONSOLE
>>    #define MYNEWT_API_CONSOLE (1)
>>    #endif
>> 
>> 
>> ### Syscfg header file generation
>> 
>> Newt generates the syscfg.h file by parsing a series of "syscfg.yml"
>> files.  Any package may contain a syscfg.yml file in its directory.
>> A syscfg.yml file defining the three settings shown above might look
>> like this:
>> 
>>    MYNEWT_CLOCK_FREQ:          1000000
>>    MYNEWT_MSYS_1_BLOCK_COUNT:  12
>>    MYNEWT_MSYS_1_BLOCK_SIZE:   260
>> 
>> If several packages define the same setting, the tie is broken according
>> to package priority.  Packages are prioritized as follows (lowest
>> priority first, highest last):
>> 
>>    1. Library
>>    2. BSP
>>    3. App
>>    4. Target
>> 
>> The expectation is that libraries would define sensible defaults in
>> their syscfg.yml files, and BSPs, apps, and targets would override those
>> defaults.
>> 
>> To prevent unnecessary rebuilds, newt only overwrites the header file if
>> there are any configuration changes since the previous build.
>> 
>> ### newt target config command
>> 
>> Newt would have a new command: newt target config <target-name>.  This
>> command shows the following information about each setting for the
>> specified target:
>> 
>>    * Default value and package which specifies it.
>>    * Actual value and package which specifies it.
>> 
>> ### System init C code
>> 
>> The system init code conditionally initializes modules in sequence.
>> Here is an example of how this might look:
>> 
>>    #if MYNEWT_PKG_SYS_ID
>>        id_init();
>>    #endif
>> 
>>    #if MYNEWT_API_CONSOLE
>>        rc = console_init(NULL);
>>        assert(rc == 0);
>>    #endif
>> 
>>    #ifdef MYNEWT_REBOOT_LOG_0_TYPE
>>        reboot_init_handler(MYNEWT_REBOOT_LOG_0_TYPE,
>>                            MYNEWT_REBOOT_LOG_0_ENTRY_COUNT);
>>    #endif
>> 
>>    #if MYNEWT_PKG_LIBS_SHELL
>>        shell_task_init();
>>    #endif
>> 
>> Each module's init function (e.g., shell_task_init()) also reads the
>> syscfg.h header to know how to configure the module.
>> 
>> ### Questions / issues
>> 
>> Here are some issues that I'm struggling with:
>> 
>> 1. Settings lack structure.
>> 
>> Do the syscfg.yml files need more information?  Ideally (in my opinion),
>> each setting would be defined by its package with the following
>> information:
>>    * Name
>>    * Default value
>>    * Description
>>    * Restrictions
>> 
>> By "restrictions", I mean prerequisites for enabling a particular
>> setting.  For example, it is illegal to configure the config module to
>> use nffs if the fs/nffs package is not pulled into the build.  It would
>> be nice if newt could catch this configuration error with a helpful
>> message rather than letting the compiler and linker generate
>> incomprehensible errors.
>> 
>> Thinking about this "richer" syscfg format is what got me worrying that
>> this feature may be getting way too complicated.  Even though it might
>> be messy to change the syscfg format in the future, I don't think it is
>> a good idea to try to implement the super-complicated option for 1.0.
>> 
>> 2. Default task priorities.
>> 
>> A package's default settings reside in the package's syscfg.yml file.
>> Among these settings is default task priority.  I don't think it is
>> possible to assign meaningful default priorities.  I see two options:
>> 
>>    * Don't specify default priorities.  Force the user to specify all
>>      priorities in the bsp/app/target syscfg.yml file.
>> 
>>    * Allow an "any" priority value to be specified.  While generating
>>      the syscfg.h file, newt would assign unused values to priorities
>>      marked as "any".  Tasks with strict timing requirements (e.g., the
>>      nimble controller) would specify actual values.  Other tasks
>>      (e.g., shell) would use "any".
>> 
>> I think the second option is better, but requires more work.
>> 
>> 3. General flash map problems.
>> 
>> Currently it is assumed that all parts of a BSP (C code, linker script,
>> and bash scripts) have the same flash map definition.  It would be nice
>> if the flash map could be specified once in a single place.
>> 
>> Another problem concerns initialization of some flash-dependent
>> packages.  For example, if the sys/config package is configured to use
>> FCB, something needs to indicate where in flash the FCB goes.  With the
>> flash map definition living in C code, is not really feasible to specify
>> this in the system configuration yaml files.
>> 
>> Even though I see this as a problem, I am OK with keeping flash-specific
>> stuff in BSP C code for now.
>> 
>> Thanks,
>> Chris
>


Re: Mynewt system initialization

Posted by Christopher Collins <cc...@apache.org>.
On Mon, Aug 29, 2016 at 04:53:12PM -0700, marko kiiskila wrote:
> Hi,
> 
> for bootloader I\u2019d like to have different set of devices than the app.
> What did you have in mind for that? Different bsp_init() routines?
> Or would bsp_init() have conditional parts depending on syscfg.h says?

I know you already know the following, but I wanted to summarize how
Mynewt currently handles this for other readers.

Currently, each BSP in apache-mynewt-core is intended to be used with
both the boot loader and with "real" apps.  When a BSP setting needs to
have a different value for the boot loader, this is indicated via the
"bootloader" feature.  Here is an example from the nrf52dk BSP's pkg.yml
file:

    pkg.linkerscript: "nrf52dk.ld"
    pkg.linkerscript.bootloader.OVERWRITE: "boot-nrf52dk.ld"

I hadn't considered it, but it should be possible to do the same thing
for settings in syscfg.yml files.  That said, I think it might better to
have two different BSPs.  My feeling is that the BSP should specify
which peripherals get initialized, and this should not be dependent on
which app you are building.  

Another option is to disable peripherals in the boot target's syscfg.yml
file, since the target's configuration overrides the BSP's. I don't
think it's possible to do this in the app's file, because the app
doesn't know which BSP it is being built for.  I don't think this is a
very good solution, because one of the primary goals of this feature is
to move system configuration out of the way so that the app developer
doesn't need to worry about it (at first).

Thanks,
Chris

Re: Mynewt system initialization

Posted by marko kiiskila <ma...@runtime.io>.
Hi,

for bootloader I’d like to have different set of devices than the app.
What did you have in mind for that? Different bsp_init() routines?
Or would bsp_init() have conditional parts depending on syscfg.h says?

> On Aug 29, 2016, at 2:23 PM, Christopher Collins <cc...@apache.org> wrote:
> 
> Hello all,
> 
> This will be a long email about changes to the way Mynewt performs system
> initialization.  Any and all feedback is greatly appreciated.
> 
> Proposal:
>    1. Move system initialization out of apps and into BSPs and
>       system-level packages.
>    2. Specify system settings in config file.
> 
> Motivations:
>    1. Simplify process of creating an app (no more boilerplate code).
>    2. Simplify system configuration (don't specify config within C
>       code).
>    3. Better code organization; cputime, msys, etc. "feel" more like
>       system-level modules than application-level, so put
>       initialization code where it belongs.
> 
> Examples of "system modules" are:
>    * mbufs / msys.
>    * Network stacks (e.g., NimBLE host, controller, and HCI transport).
>    * newtmgr
>    * console
>    * logging
>    * drivers
> 
> The biggest challenge is: ensuring the developer maintains precise
> control over the system configuration.  The current Mynewt scheme gives
> control to the developer by pushing configuration up to the application
> level where he is forced to deal with it.
> 
> ### Proposal Summary
> 
> System initialization happens in two stages:
>    1. BSP init (bsp_init())
>    2. sysinit (sysinit())
> 
> (1) BSP init sets up everything that is BSP specific:
>    * UARTs
>    * Flash map
>    * Other hardware drivers
> 
> (2) Sysinit sets up all the libraries which aren't BSP or hardware
> dependent (os, msys, network stacks, etc).  Sysinit is implemented in
> the sys/sysinit package.
> 
> The system knows what to initialize and how to initialize it based on
> the contents of the following header file:
> 
>    <target-path>/include/syscfg/syscfg.h
> 
> For example:
> 
>    targets/bleprph-nrf52dk/include/syscfg/syscfg.h
> 
> This header file is generated by newt during the build process.  Newt
> arranges for all packages to have access to this header file.
> 
> The syscfg.h header file consists of three sections:
>    * Settings
>    * Indication of which packages are present
>    * Indication of which APIs are available
> 
> Here is an excerpt from a sample syscfg.h file (the actual file is much
> larger):
> 
>    /*** Settings */
> 
>    #ifndef MYNEWT_CLOCK_FREQ
>    #define MYNEWT_CLOCK_FREQ (1000000)
>    #endif
> 
>    #ifndef MYNEWT_MSYS_1_BLOCK_COUNT
>    #define MYNEWT_MSYS_1_BLOCK_COUNT (15)
>    #endif
> 
>    #ifndef MYNEWT_MSYS_1_BLOCK_SIZE
>    #define MYNEWT_MSYS_1_BLOCK_SIZE (260)
>    #endif
> 
>    /*** Packages */
> 
>    #ifndef MYNEWT_PKG_LIBS_NEWTMGR
>    #define MYNEWT_PKG_LIBS_NEWTMGR (1)
>    #endif
> 
>    #ifndef MYNEWT_PKG_LIBS_NEWTMGR_TRANSPORT_BLE
>    #define MYNEWT_PKG_LIBS_NEWTMGR_TRANSPORT_BLE (1)
>    #endif
> 
>    #ifndef MYNEWT_PKG_SYS_CONFIG
>    #define MYNEWT_PKG_SYS_CONFIG (1)
>    #endif
> 
>    /*** APIs */
> 
>    #ifndef MYNEWT_API_BLE_DRIVER
>    #define MYNEWT_API_BLE_DRIVER (1)
>    #endif
> 
>    #ifndef MYNEWT_API_BLE_TRANSPORT
>    #define MYNEWT_API_BLE_TRANSPORT (1)
>    #endif
> 
>    #ifndef MYNEWT_API_CONSOLE
>    #define MYNEWT_API_CONSOLE (1)
>    #endif
> 
> 
> ### Syscfg header file generation
> 
> Newt generates the syscfg.h file by parsing a series of "syscfg.yml"
> files.  Any package may contain a syscfg.yml file in its directory.
> A syscfg.yml file defining the three settings shown above might look
> like this:
> 
>    MYNEWT_CLOCK_FREQ:          1000000
>    MYNEWT_MSYS_1_BLOCK_COUNT:  12
>    MYNEWT_MSYS_1_BLOCK_SIZE:   260
> 
> If several packages define the same setting, the tie is broken according
> to package priority.  Packages are prioritized as follows (lowest
> priority first, highest last):
> 
>    1. Library
>    2. BSP
>    3. App
>    4. Target
> 
> The expectation is that libraries would define sensible defaults in
> their syscfg.yml files, and BSPs, apps, and targets would override those
> defaults.
> 
> To prevent unnecessary rebuilds, newt only overwrites the header file if
> there are any configuration changes since the previous build.
> 
> ### newt target config command
> 
> Newt would have a new command: newt target config <target-name>.  This
> command shows the following information about each setting for the
> specified target:
> 
>    * Default value and package which specifies it.
>    * Actual value and package which specifies it.
> 
> ### System init C code
> 
> The system init code conditionally initializes modules in sequence.
> Here is an example of how this might look:
> 
>    #if MYNEWT_PKG_SYS_ID
>        id_init();
>    #endif
> 
>    #if MYNEWT_API_CONSOLE
>        rc = console_init(NULL);
>        assert(rc == 0);
>    #endif
> 
>    #ifdef MYNEWT_REBOOT_LOG_0_TYPE
>        reboot_init_handler(MYNEWT_REBOOT_LOG_0_TYPE,
>                            MYNEWT_REBOOT_LOG_0_ENTRY_COUNT);
>    #endif
> 
>    #if MYNEWT_PKG_LIBS_SHELL
>        shell_task_init();
>    #endif
> 
> Each module's init function (e.g., shell_task_init()) also reads the
> syscfg.h header to know how to configure the module.
> 
> ### Questions / issues
> 
> Here are some issues that I'm struggling with:
> 
> 1. Settings lack structure.
> 
> Do the syscfg.yml files need more information?  Ideally (in my opinion),
> each setting would be defined by its package with the following
> information:
>    * Name
>    * Default value
>    * Description
>    * Restrictions
> 
> By "restrictions", I mean prerequisites for enabling a particular
> setting.  For example, it is illegal to configure the config module to
> use nffs if the fs/nffs package is not pulled into the build.  It would
> be nice if newt could catch this configuration error with a helpful
> message rather than letting the compiler and linker generate
> incomprehensible errors.
> 
> Thinking about this "richer" syscfg format is what got me worrying that
> this feature may be getting way too complicated.  Even though it might
> be messy to change the syscfg format in the future, I don't think it is
> a good idea to try to implement the super-complicated option for 1.0.
> 
> 2. Default task priorities.
> 
> A package's default settings reside in the package's syscfg.yml file.
> Among these settings is default task priority.  I don't think it is
> possible to assign meaningful default priorities.  I see two options:
> 
>    * Don't specify default priorities.  Force the user to specify all
>      priorities in the bsp/app/target syscfg.yml file.
> 
>    * Allow an "any" priority value to be specified.  While generating
>      the syscfg.h file, newt would assign unused values to priorities
>      marked as "any".  Tasks with strict timing requirements (e.g., the
>      nimble controller) would specify actual values.  Other tasks
>      (e.g., shell) would use "any".
> 
> I think the second option is better, but requires more work.
> 
> 3. General flash map problems.
> 
> Currently it is assumed that all parts of a BSP (C code, linker script,
> and bash scripts) have the same flash map definition.  It would be nice
> if the flash map could be specified once in a single place.
> 
> Another problem concerns initialization of some flash-dependent
> packages.  For example, if the sys/config package is configured to use
> FCB, something needs to indicate where in flash the FCB goes.  With the
> flash map definition living in C code, is not really feasible to specify
> this in the system configuration yaml files.
> 
> Even though I see this as a problem, I am OK with keeping flash-specific
> stuff in BSP C code for now.
> 
> Thanks,
> Chris


Re: Mynewt system initialization

Posted by Christopher Collins <cc...@apache.org>.
Hello all,

This is a follow-up email regarding Mynewt system initialization.  I
took all of your feedback (very helpful, thanks!) and did some more
thinking.  As always, all comments, criticisms, and suggestions are
appreciated.

Thanks,
Chris

### Recap

    Proposal:
        1. Move system initialization out of apps and into BSPs and
           auto-generated C files.
        2. Specify system settings in config files.

    Motivations:
        1. Simplify process of creating an app (no more boilerplate
           code).
        2. Simplify system configuration (don't specify config within C
           code).
        3. Better code organization; cputime, msys, etc. "feel" more
           like system-level modules than application-level, so put
           initialization code where it belongs.

    Examples of "system modules" are:
        * mbufs / msys.
        * Network stacks (e.g., NimBLE host, controller, and HCI
          transport).
        * newtmgr
        * console
        * logging
        * drivers

        The biggest challenge is: ensuring the developer maintains
        precise control over the system configuration.  The current
        Mynewt scheme gives control to the developer by pushing
        configuration up to the application level where he is forced to
        deal with it.

### Changes from previous proposal

* Settings go in pkg.yml files; elimination of syscfg.yml files.
* Initialization is done by generated C files; init functions specified
  in pkg.yml files.
* Removal of newt "features" (pkg.features).

### Summary

System initialization happens in two stages:
    1. BSP init (bsp_init())
    2. sysinit (sysinit())

(1) BSP init sets up everything that is BSP specific:
    * UARTs
    * Flash map
    * Other hardware drivers

(2) Sysinit sets up all the libraries which aren't BSP or hardware
dependent (os, msys, network stacks, etc).  The initialization code is
implemented in the appropriate package.  Newt generates C code which
executes each package's initialization function.

### Syscfg header file generation

The system knows what to initialize and how to initialize it based on
the contents of the following header file:

    <target-path>/include/syscfg/syscfg.h

For example:

    targets/bleprph-nrf52dk/include/syscfg/syscfg.h

This header file is generated by newt during the build process.  Newt
arranges for all packages to have access to this header file.

The syscfg.h header file consists of three sections:
    * Settings
    * Indication of which packages are present
    * Indication of which APIs are available

Here is an abbreviated syscfg.h:

    /*** Settings */

    #ifndef MYNEWT_VAL_CLOCK_FREQ
    #define MYNEWT_VAL_CLOCK_FREQ (1000000)
    #endif

    #ifndef MYNEWT_VAL_MSYS_1_BLOCK_COUNT
    #define MYNEWT_VAL_MSYS_1_BLOCK_COUNT (15)
    #endif

    #ifndef MYNEWT_VAL_MSYS_1_BLOCK_SIZE
    #define MYNEWT_VAL_MSYS_1_BLOCK_SIZE (260)
    #endif

    /*** Packages */

    #ifndef MYNEWT_PKG_LIBS_NEWTMGR
    #define MYNEWT_PKG_LIBS_NEWTMGR (1)
    #endif

    #ifndef MYNEWT_PKG_LIBS_NEWTMGR_TRANSPORT_BLE
    #define MYNEWT_PKG_LIBS_NEWTMGR_TRANSPORT_BLE (1)
    #endif

    #ifndef MYNEWT_PKG_SYS_CONFIG
    #define MYNEWT_PKG_SYS_CONFIG (1)
    #endif

    /*** APIs */

    #ifndef MYNEWT_API_BLE_DRIVER
    #define MYNEWT_API_BLE_DRIVER (1)
    #endif

    #ifndef MYNEWT_API_BLE_TRANSPORT
    #define MYNEWT_API_BLE_TRANSPORT (1)
    #endif

    #ifndef MYNEWT_API_CONSOLE
    #define MYNEWT_API_CONSOLE (1)
    #endif

Newt generates the syscfg.h file from information read from pkg.yml
files.  This proposal adds two items to the pkg.yml structure:

    1. pkg.syscfg_defs
    2. pkg.syscfg_vals

Both of these items are optional.

(1) pkg.syscfg_defs is an associative array of setting definitions.  The
setting name is indicated by the element key.  A setting definition has
the following fields:
    * description ("description")
    * default value ("value")
    * requirements (TBD)

Here is an example from net/nimble/host/pkg.yml:

    pkg.syscfg_defs:
        BLE_SM:
            description: 'Security manager legacy pairing.'
            value: 1
        BLE_SM_SC:
            description: 'Security manager secure connections (4.2).'
            value: 0

A setting can only be defined once.  If a setting with the same name is
defined more than once, in any number of pkg.yml files, newt complains
and aborts the build.

(2) pkg.syscfg_vals is also an associative array keyed by setting name.
However, it only contains setting values.  This array is used to
override default setting values.

Here is a hypothetical example:

    pkg.syscfg_vals:
        MSYS_1_BLOCK_COUNT: 12
        MSYS_1_BLOCK_SIZE: 260
        CLOCK_FREQ: 1000000

If a package attempts to override an undefined setting, newt complains
and the build is aborted.  If several packages override the same
setting, the tie is broken according to package priority.  Packages are
prioritized as follows (lowest priority first, highest last):

    1. Library
    2. BSP
    3. App
    4. Target

The expectation is that libraries would define sensible defaults in
their pkg.yml files, and BSPs, apps, and targets would override those
defaults.

A library is not allowed to override settings; it can only define them.

Within the syscfg.h file:
    * Each setting macro is named as follows:
        MYNEWT_VAL_<escaped-setting-name>

    * Each package-presence macro is named as follows:
        MYNEWT_PKG_<escaped-package-name>

    * Each API-presence macro is named as follows:
        MYNEWT_API_<escaped-API-name>

A name is escaped by converting it to upper case and replacing all
non-alphanumeric characters with underscores.

To prevent unnecessary rebuilds, newt only overwrites the syscfg.h file
if there are any configuration changes since the previous build.

### Task priorities

Settings that specify task priority should not have plain numeric
values.  Instead, the value should be one of the following forms:

    (1) PRIORITY_<number>
    (2) PRIORITY_ANY

Newt converts these strings to numeric values as follows:

    1. All explicit priorities (form 1) are assigned as specified (i.e.,
       the "PRIORITY_" prefix is removed)
    2. All remaining priority settings are iterated alphabetically by
       name; each setting is assigned a value that is one greater than
       the current greatest priority.

Newt raises an error and aborts if it runs out of priority values
(increases beyond 255).

Newt raises an error if multiple packages are assigned the same
priority.

### newt target config command

Newt would have a new command: newt target config <target-name>.  This
command shows the following information about each setting for the
specified target:

    * Setting name
    * Setting description
    * Actual value and package which specifies it.
    * Default value and package which defines it.
    * Corresponding C macro name.

### sysinit pkg.yml items

The following two optional items are added to the pkg.yml structure:

    1. pkg.init_function
    2. pkg.init_stage

(1) pkg.init_function specifies the name of a C function which
initializes the package.

(2) pkg.init_stage is a numeric value indicating when the package should
get initialized, relative to other packages.

Initialization stages are executed in ascending order.  Initialization
of packages within a stage are ordered alphabetically by package name.

### sysinit C code

At build time, newt generates the following target-specific C file:

    <target-path>/src/sysinit.c

This file initializes each package whose pkg.yml file specifies a
pkg.init_function item.  This file does not depend on any other
packages, and it does not include any headers.

Here is an abbreviated example of how this file might look:

    void bootutil_pkg_init(void);
    void console_pkg_init(void);
    void log_init(void);
    void log_reboot_pkg_init(void);
    void nffs_pkg_init(void);
    void os_pkg_init(void);
    void shell_pkg_init(void);
    void stats_module_init(void);

    void
    sysinit(void)
    {
        /*** Stage 0 */
        /* 0.0: libs/os */
        os_pkg_init();
        /* 0.1: sys/stats */
        stats_pkg_init();

        /*** Stage 1 */
        /* 1.0: sys/log */
        log_pkg_init();

        /*** Stage 2 */
        /* 2.0: fs/nffs */
        nffs_pkg_init();
        /* 2.1: sys/reboot */
        log_reboot_pkg_init();

        /*** Stage 5 */
        /* 5.0: libs/bootutil */
        bootutil_pkg_init();
        /* 5.1: libs/console/full */
        console_pkg_init();
        /* 5.2: libs/shell */
        shell_pkg_init();
    }

An init function takes no parameters and returns void.  The parameter
list is empty because the function is expected to read its configuration
from the syscfg.h file.  The void return type is a bit harder to
justify, but here is my thinking.  If init functions all returned error
codes, the generated sysinit code would always handle a failure return
code in the same manner: abort the initialization sequence and perform
some emergency behavior (reboot, enter rescure mode, whatever).  This
repeated checking of return codes would result in extra code (text) with
no real benefit.  It would be better for initialization to call a
panic function on error.  The panic function could either assert(0),
or use longjmp to break out of the init sequence.  Mynewt would need to
allow the application to assign semantics to the panic function.

### Removal of features

First, here is a summary of how the Mynewt features mechanism works.  A
feature is a system-wide boolean setting defined by a package.  A
package's configuration can be dependent on which features the system
enables.  For example, a feature being enabled by cause a package to
specify additional dependencies, cflags, or even other features.

I believe features are currently used to solve the following problems:

1. Allow package A to depend on package B, but only if package B is
already part of the system.  I.e., use it if it's already there, but
don't pull it into the build if it isn't.  Here is an example of this
usage from fs/fs/pkg.yml:

    pkg.deps.SHELL:
        - libs/shell

I.e., the file system package depends on shell only if the SHELL feature
is enabled (presumably by the shell package).  The reason for the
conditional dependency is the existence of file system shell commands
(e.g., ls).

2. Allow package A's C code to know if package B is part of the build.
This has a similar use case to (1) above.  The fs/fs package does this
as follows:

    pkg.cflags.SHELL: -DSHELL_PRESENT

The fs C code registers its shell commands if the SHELL_PRESENT macro is
defined.

3. Allow the BSP download and debug scripts to know if a boot loader is
being uploaded to the board.  The scripts need to know this information
so that they know the correct flash address to write the binary.  Newt
conveys this information to the scripts by passing the list of enabled
features as command line arguments to the scripts.  If a script detects
the "bootloader" argument, it knows it is dealing with a boot loader.

I believe the sysinit mechanism handles all of these cases at least as
well as the features mechanism does.  To simplify usage, I think it is
best to remove features entirely.  Below I will explain how I expect
sysinit to handle the above three cases.

(1) Conditional dependencies

The contitional "deps" syntax remains the same, except a setting name is
appended to "deps." rather than a feature name.  If the named setting
is defined an non-zero, the dependency is processed.

I think this is better than the feature-based method, because usually
the simple presence of a package should not determine whether another
package depends on it.  For example, it is not hard to imagine an
application which uses both the fs and shell packages, but which should
not contain the fs shell commands (e.g., on a space constrained
platform).  Currently it is not possible to arrange for this.  With
syscfg, fs would define a setting indicating whether the shell commands
should be included.  Example (fs/fs/pkg.yml):

    pkg.syscfg_defs:
        FS_CLI:
            description: 'Enable file system shell commands'
            value: 1

    pkg.deps.FS_CLI: libs/shell

(2) Allow package A's C code to know if package B is part of the build.

This is trivially handled via inclusion of the syscfg.h header.

(3) Allow the BSP download and debug scripts to know if a boot loader is
being uploaded to the board.

I think the right answer here is to use a separate BSP for the
bootloader.  Then, the scripts don't have to know what settings are
defined because the boot loader BSP scripts are hardcoded to write to
address 0.  Alternatively, newt could just pass the name of every
non-zero setting to the scripts, and the boot app could define a
"bootloader" setting.

There is one thing that features can do that syscfg cannot:
conditionally override other settings.  In other words, you can't do
something like this:

    pkg.syscfg_vals:
        FS_CLI: 1
        FS_CLI.I_DONT_WANT_CLI: 0

I don't view this as an issue - I think the appropriate package will
know the exact value to assign to a setting.

Removal of features would also mean removal of the pkg.feature_blacklist
and pkg.feature_whitelist configuration items.  While I understand why
these are necessary today, I think they have the potential to cause a
lot of confusion when trying to nail down exactly which settings your
build is configured with.

### Questions / issues

1. Do we need second-stage initialization?  This would occur after the
OS has been started but before the scheduler is running.  This might be
tricky to arrange; it isn't clear which stack this code should run in
since not all packages have a dedicated stack.

2. Setting profiles.  It might be useful for NimBLE to have a
peripheral-only configuration, for example.  In the system I outlined
above, the user would need to override a bunch of BLE settings in his
app or target to achieve this.

3. Should libraries be able to override another libraries settings?  In
this proposal, a package's settings can only be overridden by something
above it in the hierarchy (i.e., bsp, app, or target).  I couldn't think
of a compelling reason to allow this, and it could make builds harder to
understand and debug.