EDA软件研发的DevOps平台

1:什么是DevOps

DevOps是十几年前,在互联网比较火的词,实际上就是ci/cd平台的另外一种说法,核心是说打破研发,测试,运维的边界,能够将整个产品开发的流程快速循环起来,随时可发版,随时可测试,达到敏捷开发的目的。当然,这里还牵扯到大量的线上发布,自动化测试的手段。

记得在更早前,研发桌面软件,或者大型的ERP软件时,并没有持续集成的概念,那时,测试不可能天天拿到最新的版本,拿到可测试版本,至少要一个月的周期。

转眼间,互联网甚至移动互联网的风头已过,芯片热兴起,国内不少企业开始进入EDA软件的领域。那EDA软件的开发,是否需要有DevOps,或者说CI/CD平台呢?实际上,仍然是非常需要的,甚至在某些场景,它起到的作用,非常之大。下面以我见到的案例,讲解一下要研发一个大型的EDA软件(桌面软件),如何搭建一个CI/CD平台。

1.1:EDA软件的开发流程

我们先来看一张图:

上面这张图,是对一个FPGA EDA软件开发流程的理解,它本身就是一个需要快速循环的过程。谁的周转率快,谁就能在软件中占得先机。

行内人会知道,要研发一款全链的EDA工具,不光是在某些环节有很高的难度,它的规模实际上也是不小的。中等规模可能是 100人的研发团队,在Xilinx,曾经有超过1000人的软件团队针对一款工具进行研发。所以,要协同好大家的工作,保证软件快速的持续更新,必须要有Ci/Cd平台。

在早期的CI/CD平台,大家的理解就是 GitLab + Jekins ,Build Tool + Pipe Line

当然,这仍然是核心,但要真正让这个平台有用,还有非常多的细节。

1.2:EDA软件的功能

首先,我们还是来看一下一款EDA工具,它本身是做什么的。

我们仍然以FPGA的EDA工具为例,上图可以非常清楚看到软件提供了编写逻辑电路,综合,映射,布局,布线,生成位流,烧录到芯片的全过程。

1.3:EDA软件的技术架构图

要完成这个软件,我们来看看它的技术架构,见下图:

我们今天不是讲FPGA的EDA软件是如何实现的,只需要看到它的复杂度即可。从上面的图可以看到,一款EDA软件需要多个模块组成,会严格分为 架构,器件,GUI,综合,技术映射,布局,布线,时序,功耗,位流生成,软件集成,TCL支持,测试等......非常多的模块,而这些模块的技术实际上有很大的差别,需要一个比较庞大的团队来支撑。而要将这些模块串到一起,必须要有一个CI/CD平台。好的,我们终于讲到正题了。

1.4:EDA CI/CD 平台的架构

所以,我画一下CI/CD平台的流程,如下:(后面我们会按照这张图,来介绍CI/CD平台需要完成的事情,并不会讲实现的细节哈)

2:开发环境搭建

EDA 软件,一般是采用C++来开发,因为需要极致性能,这方面 C++ 是王者。当然,对于GUI,有可能会使用其它语言。今天我们以全C++的开发语言为例。

因此,我们可以认为这个EDA工个由多个子模块组成,每个开发人员都身处在每个模块中。

由于EDA软件会有非常强的安全要求,所以,一般公司会将代码放到物理隔离的红区,而且也不允许大家跨组查看其它组的代码。但是,又需要随时获取最新代码来完成集成和测试。这差不多是开发环境的基本需求。

于是,代码仓一定是多个,首先,需要搭建多仓的开发环境。

2.1:多仓开发环境

EDA软件本身是一个非常庞大的工程,需要有多个领域的开发人员参与,模块众多,为了开发效率和代码安全,分成多个代码工程,使用 Git 管理代码,Gitlab作为管理平台,完成代码储存、版本控制、代码权限控制等。

注意:git仓禁止 force push(切记,切记)

考虑不同模块和角色的人拥有的权限不同,使用google 提供的 Repo 进行多仓管理,为不同角色的开发者生成不同的manifest,获得不同的代码工程的组合。对于repo的使用(一般不会用到),

git仓的权限,需要通过公司的流程申请获得。原则上按最小权限授予。

如何获取最新代码呢?

一般会给开发提供一个快速的命令:比如 repo_xxx_clone:基于个人权限拉取工程到开发者本地目录(依次将有权限的git工程clone到本地)。对于每个仓的权限管理,当然是使用gitlab的权限管理工具。

如何更新代码环境?

一般会封装一个 xxx_update命令: 获取最新工程代码,如果权限有变更,将追加新授权的工程,或删除收回权限的工程。这里的好处就是,可以将权限集成到这里。不需要用户去关注自已到底有哪些工程的权限。

2.2:开发环境设置

获取代码后,需要对开发环境进行设置。

【环境设置】

  • 设置软件的主目录------XXX_HOME设置为当前目录,并设置一些与主目录相关的环境变量:注意:这个 XXX_HOME是必须的,因为后续有大量的路径都是基于此。

  • 设置git提交时的用户配置------username , useremail 会取当前用户

  • 装载git commit的拦截程序 这是一种本地检查代码的方法。

好了,这里讲到最关键的点,如果我不能获得全部源代码,我如何进行编译连接?对了,我们会将你没有权限的模块的编译产品(so,dll,以及相应的.h)放入到一个特殊的预置仓,你可以随时拉取最新的预置库,通过链接SO的方式来完成程序的构建。

当然,初期也可以存在全源码的构建方式,但一般随着软件的成型,一般不再允许有人拥有全部代码的权限,这样很不安全。

2.3:三方依赖库管理

这里注意一下,我们一定存在直接引用的三方仓库,但是在引用前,一定要做安全准入审查,保证它是合法的,不会有license上的问题(哪果公司有法务,可交给法务判断)。一般对于MIT,Aparch, LGPL等协议可以安全使用(商务无需开源发布)。如果对于三方库来源不信任,可能还需要做一些安全扫描。

依赖的三方仓一般会有几种用法:

  • 动态库引用,这是最常见的用法。

  • 基于三方开源代码(允许修改),需要做少量的修改和维护。

  • 商业购买的三方代码,这各可以按自研源码处理。

  • 头文件使用,三方库只有头文件,直接在源码仓中引入使用。

  • 静态库方式使用。

这里要注意一点,为了保证三方仓的代码可以溯源,一般最好是建立三方仓的源码仓库,自行按需编译生成相应的动态库,这样,可以保证我们产品出问题,可以找到相应的源码,进行调试。

2.4:多分支协同开发规范

一般是创建main分支作为主干,用于存储完整的最新代码。

所有正式的对外发布版本,会创建一个对应的发布分支。

实际开发时,各开发小组自行创建开发分支,当然,也可以统一建议特性开发分支,这个一般不做强行要求,看项目的复杂度了。

对分支需要有相应的管理,一般来说,main分支,原则上只能通过MergeRequest合入,并且只有Leader可以有最终的Merge权限。

发布分支,一般有更严格的合入要求(因为是要正式发版的),在正式发布前的某个时间点,会锁定分支,集体评审后,专人合并(保证代码质量)。

开发分支,一般可以不做管控,创建,权限管理,销毁由各开发小组自行管理。原则上是在开发分支完成基本模块的开发和测试,然后合并到main分支或者发布分支。所有合并到发布分支的内容,原则上都应该在main分支中存在。

注意:代码入主干和发布仓会触发门禁和冒烟,如果不成功,无法入仓,这样用来保证代码质量。这是最关键的,如何做到,后面会讲。

3:代码质量

代码质量如何保证?最容易想到是单元测试,然后就是各种静态,动态检查工具。

3..1:单元测试

要选定单元测试框架,推荐使用 google test 框架。

这里的单元测试代码会作为入仓前的冒烟用例,用来守护代码的质量。

3.2:代码检查

对于提交的代码,一般会进行一些检查,某些情况会拦截,某些情况给出提示。

建议的检查项如下(只是建议)

|-----------|----------------------------|----|------------------------|
| 检测项 | 检查规则 | 动作 | 备注 |
| FileType | 检测提交文件类型,检查是否非代码文件 | 拦截 | 不能直接提交编译后的产物,无法保证找到原码。 |
| FileName | 检测提交文件名字,检查是否重名文件 | 拦截 | 会有编译错误 |
| CopyRight | 检测提交文件内容,是否有CopyRight声明 | 提示 | 不合规 |
| FuncLen | 检测提交文件内容,函数的行数超100 | 提示 | 过于复杂的函数 |
| Cpplint | cpplint检测提交文件内容,存在error的文件 | 拦截 | 是否出现error,每个工程有不同的配置 |
| MVG | 检测提交文件圈复杂度,默认拦截超过最大圈复杂度的提交 | 拦截 | 逻辑过于复杂,分支过多 |

3.3:代码静态检查

CppLint

cpplint是最常用的,检查项目非常多,可以自行网上搜索。

Cloc

主要用于做代码的统计。

Cppncss

主要用于代码(非注释)的统计。

Flawfinder

寻找代码潜在的安全漏洞(主要用于C,C++代码)。

pmd_cpd

检测代码中可能重复(复制,粘贴)的代码,去除冗余,提升代码质量。

Simian

查找分析重复或者相似的代码。

Cccc

专注代码复杂度,代码统计。

代码统计:代码行数(包括注释行,空白行,代码行),用于估计代码规模

代码复杂度:圈复杂度,衡量代码逻辑的复杂性。

代码质量评估:类的耦合性,类之间的继承分析

对代码进行增量检测或定期扫描(注意:需要在流水线中设置,定期检查)。

在开发初期,大家代码质量不太行,可以适度放宽检查,比如:建立一个配置表,让各小组自行选择此阶段要检查的项目。

3.4 代码动态分析

通过测试用例,在运行时统计代码覆盖率,用于覆盖率分析。

gcov

代码覆盖率分析:查找未被测试覆盖的代码。

执行频率分析:代码行执行次数,发现热点代码和冗余代码。

分支覆盖率:测试条件判断分支是否补充分测试。

3.5 代码内存分析

常用的两种方式,ASAN方式,Valgrind 方式

ASAN方式

使用 构建的 ASAN版本(添加可调试的符号),运行用例 ,查看输出的报告。

Valgrind方式

使用普通的调试版本即可。但需要依赖 valgrind工具。无需要源码。

valgrind --leak-check=full --track-origins=yes ./program

3.6 检查策略

为了保证代码质量,采用多种策略进行检查。

本地commit前检查(拦截或提示)

在本地代码执行 git commit 时,可以挂钩自已的程序,然后定制代码检查。

也可以定期生成报告,输出质量报告查看质量。

4:代码构建

好了,开始讲最重要的代码构建,下面以 linux的make方式构建为例:

4.1:make构建(linux)

经典构建框架:

Makfile.top

Makefile.temp

Makefile.com

MakeFile:凡是要参与构建的模块,需要定义Makefile

解析顶层参数:All,Release,Debug,ASAN,Clean,HeadList,checkHeads

MODULES:包含嵌套的需要处理的子目录(模块)

...... 可输出静态库,动态库,可执行文件的参数 ......

Include Makefile.top

解析Modules,完成向子模块传递构建。

Include Makefile.temp Include makefile.com

定义通用的三方库的引用

解析编译,输出参数,完成核心构建

构建有如下的输出:

  • 子模块构建

在comp/子模块构建,输出 .so 动态库,或者.a 静态库

  • 合并子模块构建

将多个模块合并成一个库,这是常见的,否则容易暴露产品的实现细节,一般最后做一次合并。

  • 构建可执行程序

在prog/子模块构建,输出 .sh 可执行程序。

  • 预置库的构建

    本地缺失的源码,使用 Prebuild_lib的动态库。这是开发端最常见的构建方式。

    请注意,请及时更新最新的Prebuild_lib中的动态库。可使用 update_api

  • 全源码构建

    通常在系统平台的流水线中的使用,完成正式的构建。

  • 清理构建输出物

    清理构建的全部输出。

  • 头文件依赖检测

    检测头文件的依赖关系。

重要命令

  • xxx_build

配置:debug / release / asan

模块:all / gui / clean

预制库的更新

定时重新生成所有的动态库,更新到 Prebuild_lib 仓库,包括API头文件。

保证开发人员可以及时获取最新的Prebuild_Lib。

构建的定期检查

循环构建,完成预制库的输出的同时,保证代码可正常编译。如有问题,及时报警。

构建加速

构建速度非常重要,除了需要配置更好的物理资源,也可做如下的优化。

  • 增量编译

  • 缓存处理

  • 分布式编译

对于 cmkae 和 windows的构建,这里就不讲了。

5:代码入仓

5.1:MR入仓

代码进入Main和 Release_XXX仓,原则上不提倡直接Push(少数人也此权限),而是建议采用MergeRequest入仓,因为在MR前会有若干的处理,可以对入仓的代码进行检查,不符合要求将会被拦截。

  • 触发流水线

通过配置 Webhooks,MR时调用 Jekins提供的流水线,

  • 获取代码

将需要MR的代码和正式仓代码进行合并,生成完整代码

  • 门禁检查

    尝试对源代码进行多种build,检查是否可正常。

  • 冒烟检查

运行预置的单元测试,检查关键用例是否可正常执行。

  • Rebase处理

建议对代码进行Rebase,这样保证可保留所有提交历史。

5.2:门禁检查

调用全源码的构建:(这里并不输出结果,只是借用构建查看代码是否正常)

  • Liunx debug build

  • Linux Release lbuild

  • Windows build

任何一个构建失败,整体门禁检查失败。

这是非常重要和关键的一个步骤,可保证入仓代码的质量。

5.3:冒烟测试

冒烟测试可提供配置文件:让各模块的人员配置对应的单元测试。

[Basic]:每次必须要执行的用例 这个理解为主流程

[模块名]:匹配当前MR的工程模块,匹配上的话就执行用例的shell脚本 返回 0 成功

5.4:多仓联合提交

某些情况,存在多仓提交,需要采用多仓联合构建的方式。具体略,需要将仓暂时进行锁定,保证一致性。

5.5:PrebuildLib 入仓

持续构建生成so,定时提交仓库,备用。

同时,达成检测代码可用性的目标。这个很关键,否则,开发人员的工作无法进行。

6: 打包输出

输出多种版本,用于后续的测试或支撑继续开发。这是持续交付的关键,可以让测试的流程快速闭环起来。

6.1 打包输出流程

建议流程如下:打包输出流程主要定义为下面几个步骤,可定制。

  • pre_build

在build前的一些参数预置,根据实际输出的需要,进行设置

  • build

实际的build过程,一般是先清理,然后 make all

  • pre_pack

pack前的预处理,可以交给各模块自行编写脚本插入。比如:生成Primitive 的 HDL templates.

  • Pack

可定制打包最后需要追加和忽略的文件。

  • Post_pack

pack的后处理,比如:将tcl script 进行加密处理。

6.2 重要输出

  • 输出的控制点

选用的代码不同,可能是Main,可能是某个Release分支。

支持哪些器件

是否进行License管控

是否提供内部feature

parm的默认参数值

tcl命令开放列表

  • 输出的所有版本

对控制点不同的配置,形成多种输出

1:内部测试版本------用于内部的开发测试

2:软件部的外部测试版本------给到软件部以外的部门,如:市场,validation,回片 进行测试

3:面向客户的版本------给到正式客户的版本。

4:合作商的版本------提供给我们的集成商的特殊版本,比如:给到 mentor的合作版本

5:DFT测试版本------提供给DFT测试的版本

  • 输出的周期

这个按需提供。可能是每日定点,每周......

  • 重要的输出流程

这个看公司的需求,成熟度不同的阶段,会有不同的测试粒度。

7:安装包发布

7.1 制作安装包

分为Linux版本和Windows版本的打包。

完成安装包文件的SHA验证码

安全检查

提供发版文件,提供安装手册。

上架发布(可能是网站或者其它方式)

7.2:License处理

提供浮动版和单机版License。

确定license申请和绑定的流程,提供相应的审批流,并记录背后的申领数据。

8:工具平台支撑

8.1:调度流水线

提供Jekins Pipeline 流水线,按时间,完成定时调度,或主动触发,可以定制完整的flow。

除了为各种输出提供流水线,也为日常的测试提供调度配置。

比如:DailyTest,QoRTest,RuntimeTest,CustomerTest,WeeklyTest,GUITest。

8.2 监控预警

提供预警API,用公司的协同工具完成消息推送。

预警点:环境输出失败,门禁失败,打包失败......

监控这里更多指的是必要服务的可用性监控,或者说是质量的看护监控,这个都需要自定义脚本来完成。

8.3 数据分析

代码质量看板

提供质量看板,为测试组提供代码覆盖率、缺陷率、代码量的持续监控看板,提高问题洞察效率。

Git仓授权检视
代码提交量统计

更多...... 注意,这里需要选用一款报表系统,进行可视化的展示。取数和加工根据实际情况进行开发。

8.4 安全加密

提供非对称密钥对,公钥在代码内置,私钥在打包时构建入代码。

加密工具可以将用户的代码进行加密,然后只能在EDA软件中安全使用。这样,可以保护用户和我司的代码。

用户也可以使用提供的加密工具,对代码进行加密,然后在EDA中使用。

这个很重要,因为客户的代码是绝密的,必须要想办法保护,在交付三方时,可以使用,但不可见源码。

8.5 三方集成

CI/CD平台需要与 协同工具,Gitlab,Jenkins等平台进行集成,比如:Git自动授权,代码权限审视,代码仓锁库,消息通知等。

8.6 现场调试

需要提供现场调试的能力,因为某些情况,只能在客户现场重现问题。

我们只能将某部分代码重编后,在客户现场通过GDB来调试。

现关的实现。略。

以我的经验,大概就讲这么多了,也摘自之前的总结。

相关推荐
wish3667 小时前
Clean Docker Images and Container by Cron Job
运维·经验分享·docker·容器·devops
tlog18 小时前
【verilog教程】verilog任务
linux·fpga开发·ic·fpga·asic
白码王子小张18 小时前
Matlab Simulink HDL Coder 时钟束信号生成
matlab·fpga开发·fpga·vivado·xilinx·simulink
哎呦没1 天前
欢迪迈手机商城:SpringBoot框架的持续集成
spring boot·ci/cd·智能手机
wish3661 天前
How to use gitlab-runner to execute CI?
ci/cd·gitlab
it噩梦1 天前
使用argo workflow 实现springboot 项目的CI、CD
spring boot·后端·ci/cd
Python私教1 天前
基于 Docker 的持续集成/持续交付(CI/CD)流水线构建实战
ci/cd·docker·eureka
陈哥聊测试1 天前
DevOps引领数字化转型新趋势
项目管理·devops·企业转型