单板控制领域模型设计与实现

现状与问题

BSP团队负责多个项目单板的BSP开发维护,目前共96块板卡之多,而且板卡因为改版,往往还存在多个版本。每块板卡都有相应的单板控制,负责板卡的设备注册、板上芯片初始化、外部中断初始化、EPLD资源管理、BSP回调及IOCMD接口实现等等。

在老的软件架构和开发模式下,新板卡的单板控制开发调试流程一般如下:

  • 复制一份老的单板控制模块,解决编译问题,保证编译通过;
  • 对照硬件功能调试清单,对复制的代码进行修修补补,进行调试。

这种开发模式下,存在如下问题:

  • 各单板单板控制模块各个独立,代码规模跟随单板成线性增长(O(n)
  • 调试清单式验收把关松散,缺少软件架构上的保护,存在遗漏功能点、打印错误、单板间实现差异等问题,导致在联调时大量问题才暴露,影响单板联调进度;
  • 单板控制代码重复度为80%以上,因为同类板卡往往只是更换了某个芯片;
  • 板卡硬件设计的升级、改进、演进是渐进的,但是在软件设计架构上,体现不出、也不支持这种渐进式的演进;
  • 大量的重复代码给开发和维护带来一定的挑战,也带来了巨大的人力浪费和软件管理难度。

设计的目标

项目单板主要分为PFUSFU、MPU三大类,每类单板的硬件框架标准相同,核心功能一致,对外部呈现接口统一。如何在标准化的硬件框架上,构建可扩展性、可重用性、可维护性、可测试性良好的单板控制领域模型是本次设计的重要目标,具体来说,包括:

  • 提供统一的模型框架,降低新增单板的单控控制模块开发复杂度;
  • 提供统一的EPLD寄存器视图,方便与寄存器手册进行直接映射;
  • 提供统一的接口视图;
  • 提供统一的单板控制功能测试;
  • 友好支持单元测试中硬件仿制等需求;
  • 能清晰的体现单板硬件设计上的渐进式演进;
  • 能清晰的体现单板改版的差异;
  • 消除新增单板控制模块的重复代码。原来每新增一块同类新单板,要新增约15000~20000行代码,新模型的设计目标是控制在1000行以内。

 

 

设计与实现

领域建模

领域模型设计,从某个一个角度来看,就是做好差异性管理。这其中包含2个信息:1什么是核心业务,什么是差异;2)如何管理。

我们根据单板控制领域的核心业务,以及单板间的差异性需求,抽象出3个对象(boardepldops),并建立领域模型(如下)。

 

模型实现中运用的主要技术方法:

1) DSL:构建DSL语法对寄存器进行描述;

2) 继承:C语言不支持继承语法,通过模拟C++  vtable虚函数表指针,实现对象的继承功能;

3) 覆盖: C99中,若数据结构体中的某个字段被重复初始化,最后一次初始化有效。根据这个特点,实现对象的覆盖功能。

 

ops对象

根据单板控制的领域模型特点,抽象出统一的接口对象:ctrl_operations,并按应用场景分为3类:内部接口、外部接口、ioctl接口,并分别进行模块化设计。ops对象定义示例如下:

/* board_a单板 PCB_VER_1版本 作为 ctrl_operations 的默认实现 */

struct ctrl_operations {

    /* internal operations */

    SWORD32 (*board_init)(struct board *bd);

    SWORD32 (*board_exit)(struct board *bd);

    /* 以下省略... */

    /* external operations */

    /* k_GetCpuFreq wrapper */

    WORD32 (*get_cpu_freq)(struct board *bd);

    /* 以下省略... */

    /* ioctl operations */

    /*BSP_IOCMD_BRDCTRL_LOCAL_MSMSG_GET*/

    WORD32 (*query_local_msMsg) (struct board *bd,T_BSP_BRDCTRL_MSMSG_GET *ptMsMsg);  

    /* 以下省略... */

    const struct ctrl_operations    *inherits;

};

 

 

Ops对象差异性的设计需求,通过继承和覆盖来满足。通过继承,满足体现单板硬件设计上的渐进演进关系的设计需求;通过覆盖,满足体现单板与单板、单板改版间的硬件改动的设计需求。

下面的一个实例中可以一目了然看出board_a单板与board_b单板的继承关系和硬件实现差异。

static struct ctrl_operations board_b_ops = {

    .inherits               = &board_a_ops,

    .rov_wr                 = bsp_board_b_rov_wr,

    .vol_modify             = bsp_board_b_vol_modify,

    .get_voltage_current    = bsp_board_b_get_voltage_current,

    .funccard_info          = bsp_board_b_funccard_info,

};

 

在上例中可以看出,继承通过inherits字段表达,由finalize接口实现,具体实现细节如下:

SWORD32 finalize(struct ctrl_operations *ops)

{

    const struct ctrl_operations *cur;

    void **begin = (void **)ops;

    void **end = (void **)&ops->inherits;

    void **pp;

 

    if (!ops)

        return -EINVAL;

 

    if (!ops->inherits)

        return 0;  

    

    for (cur = ops->inherits; cur; cur = cur->inherits) {

        void **inherit = (void **)cur;

        for (pp = begin; pp < end; pp++, inherit++)

            if (!*pp)

                *pp = *inherit;

    }

 

    for (pp = begin; pp < end; pp++)

        if (IS_ERR(*pp)) {

            *pp = NULL;

            return -EFAULT;

        }

 

    ops->inherits = NULL;

 

    return 0;

}

 

 

epld对象

EPLD对象通过BOOTEPLD_REGISTERWORKEPLD_REGISTERCPUEPLD_REGISTER 3DSL进行描述,其描述语法形式采用硬件提供的寄存器说明手册格式(如下)。

// boot epld registers

BOOTEPLD_REGISTER(board_id,         0x000)  /* Board ID Reg */

BOOTEPLD_REGISTER(bom_pcb_cpld_ver, 0x002)  /* PCB BOM ID Reg  */

BOOTEPLD_REGISTER(system_ctrl,      0x004)  /* System Control Reg */

BOOTEPLD_REGISTER(flash_rst,        0x010)  /* Flash Reset Reg */

BOOTEPLD_REGISTER(rec_rst,          0x040)  /* Recored Reset Reg */

 

DSLEPLD对象的定义、初始化和测试接口提供统一封装,模型根据不同的应用场景,定义具体EPLD_REGISTER的行为,完成EPLD对象的动态定义、动态初始化和动态测试接口实现。如EPLD对象的动态初始化实现:

 

#define BOOTEPLD_REGISTER(name, offset)  .name = VALID(offset),    

struct bootepld_reg bootepld_base = {

    .inherits = NULL,

    #include "bsp_register_define.h"

};

 

 

 

通过继承和覆盖支持不同单板的epld差异性的设计需求,继承实现与ops对象类似,详见ops对象部分,不再赘述。

static struct bootepld_reg board_a_bootepld = {

    .inherits = (void *)&bootepld_base,

};

 

另外,单元测试中对EPLD硬件进行mock打桩的需求,本模型也提供了很好的支持,实现起来也十分方便。如:

struct bootepld_reg test_bootepld_1 = {

    .inherits = (void *)&bootepld_base,

    .bom_pcb_cpld_ver = VALID(0x1beef),

};

 

struct bootepld_reg test_bootepld_2 = {

    .inherits = (void *)&test_bootepld_1,

    .bom_pcb_cpld_ver = VALID(0x2beef),

    .cpu_type = VALID(0xcbeef),

};

 

 

 

效果与推广

采用文中所述设计方法,对1块已有单板控制(共18540行)进行重构,3块新增板卡的单板控制进行开发,新增一块单板支持的代码量减少98.6%(从18540行降到254行),代码规模减少82.3%(从74160行降到13101行),平均复杂度减少42%(从4.76降到2.8)。

本设计方法先后在多个团队实践,应用在PFUSFUsubcard等单板控制模块上,并已大量商用。

本设计方法适用于有相同硬件框架标准的多设备的驱动开发,其中涉及到的技术方法对所有C语言软件模块都可以借鉴参考。

posted @ 2016-12-15 10:14  wahaha02  阅读(1119)  评论(0编辑  收藏  举报