前段时间给内部团队做了这个培训:in nek:架构设计入门知识。后来看了芯片验证团队做的一个设计,感觉就没听懂(当然也许是没有参加,因为我们是个线上演讲,只知道人数有千把人,不知道谁在线上),相关的同事想我单独对验证遇到的情况进行一些具体的指导,所以我准备了一下专门针对芯片验证的提纲,描述在这里。

首先,我们需要理解,架构是为了解决非常具体的问题,是因为你本身有问题,所以你需要做架构设计,而不是因为某些专家给你灌输一些你理解不了的概念,然后你需要按他们的原则来办事。不是要你符合某些“架构”专家的要求,而是你自己有问题需要解决,需要通过架构问题解决这些问题。

那么芯片验证需要解决什么样的问题呢?很简单,你建立一个框架,后面一堆的人在你这个框架上写测试用例,你的代码可以一转眼就会从数千行变成数十万行,那个时候,如果你随便加点代码都要调整很多地方,或者要加一个功能根本加不动,你就会很被动。

如果你很自信,你没有这种问题,那我们就不用说什么了。你就随手写你的代码就行了,你不用听任何人唧唧歪歪。但你也别拿个模棱两可的框框来说你的“架构”,那玩意也没什么用。

一个软件的架构主要控制的是软件什么地方是不变的,什么地方是变化的。所以,其实验证用的软件和普通的软件是很不一样的。验证软件更关心的是如何可以重置芯片的状态,然后构造一个或者多个行为,判断芯片是否可以正常反应。而芯片的使用软件更关心的是如何选定一个使用这个芯片的方法,让其他状态根本进都进不去。

比如说,一个网卡可以被多个通讯队列分给不同的VF,如果进行验证,我们需要制造多种初始化手段,让这些VF分别分配不同的队列的数量。而如果要用于实现功能,我们可能一开始就设置一种VF队列分配算法,固定地把队列分配给每个VF,而且任何地方发现这些队列不是这样分配的,都进行错误处理。

这样,两个软件的代码行为和模块分解就是不一样的。验证代码可能会根据不同的队列分配关系,测试不同VF的带宽是否得到扩充。而功能软件可能会更关注比如按一定的策略分配了VF后,把两个VF组合起来形成Bonding的功能是否可以正常工作,或者其中一条链路断了,另一条链路能否独立保持两个通讯节点直接的数据是可靠的。

验证是针对芯片或者IP的功能的,而功能软件是基于芯片或者IP的功能实现高层的逻辑,大部分设计是针对它自己的功能的。

所以,你不能指望直接那一个实际的功能去完整“验证”你的芯片,你的芯片能让Linux正常工作,不表示它能让QNX正常工作。

一个验证软件说起来很简单,只要初始化芯片,然后根据芯片的功能输入数据,看看输出是否符合预期就可以了。但如果考虑我们将需要在几十种芯片(或者FPGA,或者EMS模型)上使用,要运行数万个用例的时候,你就没有那么简单了。

所以,你的软件到底聚焦什么范围呢?适配某种特定的芯片代码到底算不算是你的输出的一部分呢?这个说起来好像不重要的问题,其实很重要,否则你根本就落不了地。

所以,系统的上下文就显得很重要了,比如你的验证软件范围可能是这样的:

芯片验证软件的4+1方法(未完成) - 知乎-LMLPHP

这就是Use Case图。很多人会重视中间那些圈圈,其实这个图更关心的是那个四方的框框,那里定义了这个软件的边界,决定了你的软件包括什么东西,和谁打交道。上面这幅图中,验证软件需要提供两个接口给用户,一个是针对一种芯片实现一些功能,保证这个验证软件能在芯片上跑起来。另一个是让测试的人能运行这个软件,然后把验证的结果输出来。

这不一定是你的预期,因为可能你还希望这个软件可以任何加测试用例,那么这个图应该是这样的:

芯片验证软件的4+1方法(未完成) - 知乎-LMLPHP

所谓的Use Case图,主要是为了让你弄清楚到底你打算做的是个什么东西。这个东西你说不定觉得显而易见,但跟你讨论的人不见得就认为它那么显而易见。而其实对我来说,我已经看过很多很多次了,认为这个事情显而易见的,到最后写出来的软件,常常确实也不那么显而易见地符合预期。不是丢这里,就是丢那里,在作者自己那里就理不顺逻辑。所以,你对这个建模是必要的。

你决定了某个人是特定的使用者,你就需要定义给这个人的接口,你的设计在这个地方就会标准化。比如你让人可以标准化地增加测试用例,你就需要给出写用例的人应该包含什么头文件,可以使用什么API,不可以使用什么API等等。如果用例都是你自己写,不是个标准的接口,那就另当别论,你的测试用例可以是一团麻,但你也不要指望,后面放十几个人来给你写代码,因为这些人会天天来问:“这里到底是怎么回事?为什么我包含<stdio.h>编译不过?……”。

当你有了这方面的考量,你就会需要概念空间的建模。这就是概念视图的建模。概念空间说明这些使用者每个怎么用你的系统。为什么说明白这些功能,你需要一些基本的定义,说明这些定义之间的关系。比如这里提到的用例增加的功能,你可能需要说明白:

  1. 用例要用什么语言写?可以调用什么基础设施?
  2. 写好的用例怎么插入到目标系统中?
  3. 这些用例会被按什么顺序调用,怎么报错?

为了说明这些里面,你需要说明什么是“用例”,什么是“插入”,什么是目标系统,什么是“测试报告”等等。这些概念不一定需要深入到具体的接口或者语言,但你需要说明它们之间的关系,使用的逻辑。这个建模的作用是保证无论你的功能最终怎么实现,你至少是“说得通”的。很多人不建这个模型,都会觉得“这个很通啊”,但其实你真说一下看看,你就知道在概念上说通一个功能,其实很不简单。

概念可以用UML的类图表述(这个我在前面的链接中有描述了,这里不再另外画图了),这样可以说明不同概念之间的关系。但概念空间的建模的重点就不是那张UML图。

(未完成,待补充)

09-12 07:54