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


    现状与问题

    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语言软件模块都可以借鉴参考。

  • 相关阅读:
    MPlayer 开始支持RTSP/RTP流媒体文件
    Linux(CentOS 6.4)系统中安装mplayer
    IP实时传输协议RTP/RTCP详解
    --without-v4l ,make clean, 重新make即可。
    关于IP数据包首部校验字段的理解
    转[总结]FFMPEG视音频编解码零基础学习方法 .
    指针为什么分类型
    IOS-ARC和垃圾回收机制
    IOS-frame和bounds有什么不同
    iOS-消息推送机制的实现
  • 原文地址:https://www.cnblogs.com/wahaha02/p/6182158.html
Copyright © 2020-2023  润新知