您的位置:首页 > 其它

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

2016-12-15 10:14 232 查看

现状与问题

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

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

复制一份老的单板控制模块,解决编译问题,保证编译通过;

对照硬件功能调试清单,对复制的代码进行修修补补,进行调试。

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

各单板单板控制模块各个独立,代码规模跟随单板成线性增长(O(n));

调试清单式验收把关松散,缺少软件架构上的保护,存在遗漏功能点、打印错误、单板间实现差异等问题,导致在联调时大量问题才暴露,影响单板联调进度;

单板控制代码重复度为80%以上,因为同类板卡往往只是更换了某个芯片;

板卡硬件设计的升级、改进、演进是渐进的,但是在软件设计架构上,体现不出、也不支持这种渐进式的演进;

大量的重复代码给开发和维护带来一定的挑战,也带来了巨大的人力浪费和软件管理难度。

设计的目标

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

提供统一的模型框架,降低新增单板的单控控制模块开发复杂度;

提供统一的EPLD寄存器视图,方便与寄存器手册进行直接映射;

提供统一的接口视图;

提供统一的单板控制功能测试;

友好支持单元测试中硬件仿制等需求;

能清晰的体现单板硬件设计上的渐进式演进;

能清晰的体现单板改版的差异;

消除新增单板控制模块的重复代码。原来每新增一块同类新单板,要新增约15000~20000行代码,新模型的设计目标是控制在1000行以内。

设计与实现

领域建模

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

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



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

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_REGISTER、WORKEPLD_REGISTER、CPUEPLD_REGISTER 3个DSL进行描述,其描述语法形式采用硬件提供的寄存器说明手册格式(如下)。

// 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 */


DSL对EPLD对象的定义、初始化和测试接口提供统一封装,模型根据不同的应用场景,定义具体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)。

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

本设计方法适用于有相同硬件框架标准的多设备的驱动开发,其中涉及到的技术方法对所有C语言软件模块都可以借鉴参考。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: