Considerations for Infrastructure Development in Embedded Software Architecture Design

Click the above“Embedded and Linux Matters”

Select“Pin/Star Public Account”

Welfare and valuable content delivered promptly

In ancient times, writing emphasized the importance of intention. Today, engineers working on projects and products should also approach software issues from the perspective of software architecture, believing that their understanding of software will become deeper.

In embedded software development, there is also the issue of unified infrastructure, which pertains to situations where multiple people collaborate on a project or multiple projects share the same system architecture.

(If your current project does not involve collaboration with others and there will be no subsequent related projects, you can actually skip this step regarding software infrastructure.)

Infrastructure is divided into hardware infrastructure and software infrastructure. Hardware infrastructure includes commonly used component libraries, packaging libraries, schematic libraries, and hardware reference designs, etc.; while today we focus mainly on software infrastructure. Software infrastructure includes the following:

  • • Basic data structures.

  • • Low-level libraries. Such as the C standard library, cryptographic libraries, checksum libraries, tool libraries, etc.

  • • Operating systems/scheduling mechanisms. Including operating systems and scheduling-related services.

  • • Middleware. Such as file systems, protocol stacks, databases, etc.

  • • Frameworks and mechanisms. Such as message communication mechanisms, event-driven mechanisms, state machine frameworks, behavior tree frameworks.

  • • Tool support.

  • • Unified programming toolchain.

  • • Unified coding style and programming specifications.

In some small companies with a loose development model, there are no regulations on the software platforms, hardware platforms, and tools that engineers rely on; instead, engineers decide for themselves. Many engineers also prefer this freewheeling development model, believing that only in such an environment can they unleash their creativity. This perception is biased, and we will find opportunities to discuss this in detail later.

As the R&D capabilities of small companies improve, it is almost inevitable to impose constraints and regulations on software infrastructure. The essence of software, which distinguishes it from other technologies, lies in its reusability.

The higher the degree of software reuse, the higher the quality, and the greater the improvement in development efficiency and quality. From both the perspective of cost reduction and efficiency improvement for the company and scientific management, there is a strong motivation to unify software infrastructure. Once the software infrastructure is unified, the following advantages will arise:

  • • Improved software quality and high consistency in style
  • • Software reusability will reach a new level
  • • Abstract reusable functions as much as possible to the infrastructure layer, reducing software redundancy and improving development efficiency.
  • • Provide constraints and discipline for higher-level modules
  • • Facilitate technical accumulation and inheritance within the team
  • • Facilitate technical training within the team
  • • It is a prerequisite for the team to conduct unit testing, test-driven development, and cross-platform development.

Therefore, whether to unify is not a controversial issue; how to unify is our focus today.

Considerations for Infrastructure Development in Embedded Software Architecture Design

1. Basic Types and Macro Definitions

The premise of a unified software infrastructure is to declare unified basic data types and macros to overcome the differences between different hardware platforms and compilers.

For example, below is a code snippet I extracted from the open-source project EventOS, which may not be complete but represents my requirements in the project.

#include <stdbool.h>

typedef unsigned int eos_u32_t;
typedef signed int eos_s32_t;
typedef unsigned short eos_u16_t;
typedef signed short eos_s16_t;
typedef unsigned char eos_u8_t;
typedef signed char eos_s8_t;
typedef bool eos_bool_t;

#define EOS_NULL ((void *)0)

#define EOS_U32_MAX (0xffffffffU)
#define EOS_U32_MIN (0U)
#define EOS_U16_MAX (0xffffU)
#define EOS_U16_MIN (0U)
#define EOS_U8_MAX (0xffU)
#define EOS_U8_MIN (0U)

Compiler-related macro definitions. Using macros to shield compiler differences will

/* Compiler Related Definitions */
#if defined(__ARMCC_VERSION)           /* ARM Compiler */

    #define eos_section(x)              __attribute__((section(x)))
    #define eos_used                    __attribute__((used))
    #define eos_align(n)                __attribute__((aligned(n)))
    #define eos_weak                    __attribute__((weak))
    #define eos_inline                  static __inline

#elif defined (__GNUC__)                /* GNU GCC Compiler */

    #define eos_section(x)              __attribute__((section(x)))
    #define eos_used                    __attribute__((used))
    #define eos_align(n)                __attribute__((aligned(n)))
    #define eos_weak                    __attribute__((weak))
    #define eos_inline                  static __inline

#elif defined (__IAR_SYSTEMS_ICC__)     /* for IAR Compiler */

    #define eos_section(x)              @ x
    #define eos_used                    __root
    #define eos_align(n)                PRAGMA(data_alignment=n)
    #define eos_weak                    __weak
    #define eos_inline                  static inline

#else
    #error "The current compiler is not supported. "
#endif

Some commonly used data structures. These data structures are independent of hardware and compilers, frequently used in code, and shared among multiple modules. It is necessary to elevate them to the infrastructure level to avoid data conversion issues caused by different definitions of the same data type in various modules.

These data structures are closely related to the product, and different product types have their own variations. For example, the following definitions.

typedef struct eos_date
{
    eos_u32_t year               : 16;
    eos_u32_t month              : 8;
    eos_u32_t day                : 8;
} eos_date_t;

typedef struct eos_time
{
    eos_u32_t hour               : 8;
    eos_u32_t minute             : 8;
    eos_u32_t second             : 6;
    eos_u32_t ms                 : 10;
} eos_time_t;

typedef struct eos_imu_data
{
    float acc[3];
    float gyr[3];
    float mag[3];
} eos_imu_data_t;

2. Operating Systems

Some chips have too few resources to run an operating system. Generally, these chips cannot establish a rigorous embedded software architecture, which we will discuss separately in “Software Development Platforms for Low-Resource Chips” later. Here we only discuss chips.

Different chips can run different operating systems. However, to establish software infrastructure, it is advisable to select the same operating system as much as possible.

Among existing operating systems, FreeRTOS and the domestic RT-Thread support a wide range of hardware architectures and can be considered as the first choice for RTOS.

When the product line is exceptionally rich, especially when using certain niche chips or operating systems provided by chip vendors, it becomes impossible to establish a unified software infrastructure. In this case, there are two ways to solve this problem:

  • • When writing high-level modules, use macro definitions and conditional compilation to select the corresponding RTOS API. This is generally used when the operating systems in use are few, such as only two or three.
static void *task_handler = NULL;

static void task_func_module_one(void *parameter);

void module_one_init(void)
{
    /* Newly creating a task to run the module. */
#if (EOS_RTOS_NAME == EOS_RTOS_NAME_FREERTOS)
    xTaskCreate(task_func_module_one,
                "TaskModule", 2048, NULL, 2,
                (TaskHandle_t *)&amp;task_handler);
#elif (EOS_RTOS_NAME == EOS_RTOS_NAME_RTTHREAD)
    task_handler = rt_thread_create("led1", task_func_module_one, NULL,
                                    2048, 2, 20);
#else
    eos_assert(false);
#endif

    eos_assert(task_handler != NULL);
}

/* The task function of the module one. */
static void task_func_module_one(void *parameter)
{
    (void)parameter;

    /* Initialization. */

    while (1)
    {
        /* Add the task function. */
    }
}
  • • Establish an Operating System Abstraction Layer (OSAL) to shield the differences between operating systems, allowing high-level modules to depend on OSAL. This situation is suitable for resource-rich scenarios.
  • The famous POSIX standard was established to create OSAL, and both FreeRTOS and RT-Thread support POSIX standards to varying degrees; in the embedded field, CMSIS_OS is also aimed at establishing a unified interface for operating systems; however, the extent to which POSIX and CMSIS_OS are supported by various RTOSs differs. Therefore, if we need to establish a rigorous embedded software architecture in our products, we still need to create our own OSAL to shield the differences brought by different operating systems.

3. Middleware

There are many types of middleware, including file systems, various protocol stacks, databases, logging modules, and shell modules, all of which fall under the category of middleware. However, in most cases, these also belong to the realm of software infrastructure.

Once we choose a certain middleware, generally speaking, there is no need to change it. Due to this stability, middleware can also be included in the software infrastructure category. Here are some open-source middleware I frequently use:

  • • FatFS

  • • LwIP

  • • FlashDB

  • • uC/Modbus

  • • CAN Festival

  • • letter-shell

Open-source middleware only occupies a small part. In actual products, most middleware is proprietary code for products or projects. The main ones I use daily include: logging module data acquisition module communication transport layer protocol communication application layer protocol file transfer protocol OTA functionality * time synchronization

Middleware occupies a large part of the software infrastructure. In product development, the increasing software reusability is significantly attributed to the accumulation of middleware.

4. Frameworks and Mechanisms

When developing embedded software on different products, in addition to RTOS, many products also require support from certain frameworks. Common frameworks include: Peripheral and driver frameworks device frameworks message frameworks state machine frameworks behavior tree frameworks event-driven frameworks

The use of these frameworks is related to the characteristics of the product and is determined by the product and requirements. For example, in home service robots, state machine frameworks and behavior tree frameworks are needed to handle complex application layer logic. In contrast, products with simpler application layer logic do not require the use of state machines and behavior trees.

Relationship Between Software Infrastructure and Hardware

Embedded software has an important characteristic that distinguishes it from other software fields: it directly depends on hardware. Many aspects of software infrastructure also need hardware to manifest and support. For example, when specifying a certain source code, such as FatFS, as its file system solution, the accompanying hardware driver programs and recommended hardware designs are often solidified to facilitate reuse in the next project and save time.

For some important and complex software infrastructures, such as file systems and networks, due to the time-consuming nature of debugging and testing, it is generally recommended to solidify hardware designs. Hardware engineers should prioritize allocating hardware resources to these important and complex software infrastructures, while other hardware projects, such as IO, ADC, etc., can be allocated later.

Conclusion

Embedded software infrastructure is very important, and its content varies depending on the project and product. Generally, at the start of a project, some software infrastructure components, such as RTOS, protocol stacks, file systems, etc., will be preliminarily selected.

It should be noted that software infrastructure is not immutable; rather, as product development progresses, new components and elements will continuously be added to the software infrastructure, and old components may also be removed, similar to biological metabolism.

The metabolism of software infrastructure should be gentle and relatively stable, with additions and deletions executed with caution.

Original text: https://zhuanlan.zhihu.com/p/601075563

Source from the internet, copyright belongs to the original author. If there is any infringement, please contact for deletion.

end

Previous recommendations

Essential classic books for embedded Linux

Recommended learning path for embedded systems

A reader’s logically clear question

Successful transition from mechanical engineering to embedded systems

A reader’s experience of landing a job in the audio and video field during the autumn recruitment

Considerations for Infrastructure Development in Embedded Software Architecture DesignConsiderations for Infrastructure Development in Embedded Software Architecture Design

Scan to add me on WeChat

Join the technical exchange group

Considerations for Infrastructure Development in Embedded Software Architecture Design

Considerations for Infrastructure Development in Embedded Software Architecture Design

Share

Considerations for Infrastructure Development in Embedded Software Architecture Design

Collect

Considerations for Infrastructure Development in Embedded Software Architecture Design

Like

Considerations for Infrastructure Development in Embedded Software Architecture Design

View

Leave a Comment