| ||
最近两周是做了不少的工作,一方面在审定明年DVCon的论文,今年文章的质量又比去年上升了一个台阶。路桑也顺带提交了一篇关于系统监测应用于SoC的方案,受抬爱明年可以继续去做一次论文主题演讲,有兴趣的路粉明年4月份我们上海见;另外一方面,还在有条不紊地支持验证平台自动化的工具支持和生成验证结构的个性化定制问题。
诚如我们之前介绍的Pangu自动化平台可以解决占据1/3验证精力耗费的环境搭建调试问题,但用户通过结构化文件输入继而生成完整验证结构之后,对于非标准化、非公共的验证组件还需要自己实现。不过对于这些非公共的验证组件实现难度是不大的,只要上百人的验证团队都广泛使用统一的验证结构和测试指令集,那么环境复用和测试用例复用就是自然而然的事情了。
上周我们的用户鹏哥三番五次地跟我要预定一个IO控制组件,心思缜密思密达的路桑一开始时拒绝的。因为这个IO控制组件,早已经跟时钟复位组件都作为标准环境组件集成到了Pangu自动化流程中去了,我反问道,以后的IOC还满足不了你吗?鹏哥一脸憨厚地望着我,欲吐无言,最后给我提了几点要求,听完我发现,TND,还真是满足不了他的要求。
有些小白粉可能还不知道IO控制组件是做什么的?简单来讲,一般的IO控制组件可以实现这些空能:
作为输出端,可以控制多个信号置为0或者1。
作为输入端,可以等待信号的某些状态、获取或者检查信号的值。
复杂的操作,可以桥接IO接口上的某些信号,例如将pin1与pin0“桥接”,即实现pin1驱动pin0。
等待动作。
对于一般的DUT而言,上面给出的IO控制组件已经足够健壮,活儿好还不复杂,性价比高。但鹏哥要的不止这么多,他的DUT是一个SoC的中心化控制子系统,时钟、复位、电源、存储等控制信号都从它那里过,各种缠绵悱恻。按照测试类型划分的话,它既不属于处理器大类,也不属于数据流处理大类,也不属于纯综合逻辑大类。它就是控制大类,结合时序逻辑和控制组合逻辑,不夸张的说,各种飘散在信号列表上的信号大致需要几十页才能够看得完,轻松点地说大致就是六、七百根控制信号吧。
这些控制信号在系统级会与其它子系统交互。这是常见的SoC布局方式,尽管信号散乱,但由于是低频的控制时序,在后端走线压力也不那么大,只是苦了验证这个子系统的鹏哥。看着他真诚的眼睛,我不能说我是一个骗子,尽管我答应他可以完美使用的IO控制组件并不是像我一开始说的那样,他说出了两个无法进一步使用的缺陷:
每次IO控制组件都需要等待该次的控制事务执行完才能执行下一次控制事务。但他的几百根信号可以分为几十个控制组,各个控制组之间是独立的,同一个控制组中的若干信号之间也是独立的。这就要求为了实现更复杂的、更边界的测试情况,这些控制行为需要独立发生,互相不依赖不影响不干涉。但从目前的IO控制组件而言,它还无法实现这一点。如果没有新的解决办法,他只能例化十多个IO组件来实现独立控制了。听到这里,路桑先扶他坐下,告诉他,千万别这么做,这是个挖坑害人害己的妥协行为,鹏哥且讲下一个不满意的地方。
鹏哥紧接着说,第二个缺陷是,通常的IO控制组件侧重于发起信号,即作为发起端,主动置目标信号为1或者0。但是对于它的控制子系统,IO控制组件还需要扮演响应端,例如作为握手信号的从端,等待DUT的请求信号,继而做出立即响应,或者做出延迟响应,或者不做出响应,所以IO从使用层面来看,需要使其即扮演master又扮演slave的角色。听到这里,路桑扶他躺下,告诉他,别咽气儿,要对组织有信心,我们暂且没有必要再做一套slave出来,但可以在已有的master driver侧实现类似响应端的信号响应功能。
“Really?”鹏哥从地上爬了起来,没有拥抱我,工程师不相信眼泪,“那我就等你的实现方案了”。
那天回家的路上尽管没有烟来帮助我思考,我还是走了五千步有了方案。
接下来的这周一就开始编码实现了,这期间也从鹏哥那里收集了对于控制系统的个性化控制要求特点:
几百根的信号大致可以分为发起端类和相应端类。
进一步看,各个信号从电平方式来看,可以分为电平方式和脉冲信号。
从信号之间的联系来看,信号之间可以分为非握手信号和握手信号。
各个信号都有复位值。
typedef struct packed {
logic init_val = 0;
logic req_val = 1;
logic wait_val;
logic rsp_vals[$];
int rsp_delay = 1;
int rsp_width = 1;
rk_ioc_pin_t pin_t;
int src_idx;
int tgt_idx;
int clk_idx = -1;
} rk_pin_props_t;
那么单根信号的属性大致就可以定义了,利用这些具体定义,鹏哥就可以从已有的信号属性表单(Excel)中提取信息,利用脚本,将每一根信号的属性按照上述的属性类别自动填写出来,继而将几百个信号的属性存储在IO组件的控制对象中。
接下来,定义新的IO序列事务(sequence item)。
class rk_ioc_seq_item extends uvm_sequence_item;
rand rk_ioc_cmd_t m_command;
rand int m_msb;
rand int m_width;
rand logic [63:0] m_value;
rand int m_field[];
rand int m_idle_cycles;
rand bit m_is_blocking;
event e_trans_finished;
事务传送到IO driver后,就进入驱动阶段了。在继承了Pangu原有IO driver的常见驱动性能之后,我们对于rk_ioc_cmd_t做了扩充,使其可以接受新的命令RK_IOC_CMD_CONFIG_SET。该命令即是让driver接收之后,“自作主张”,可以做出一些时序性的驱动行为,例如将pin0先拉高发起请求,等待pin1置为1,继而pin0再拉低,然后pin1再拉低。很熟悉对不对?这就是一次简单的握手行为,如果按照往常的话,普通IO控制组件需要在sequence这一层添加几次item传送、控制逻辑和时序之后才能让两个sequence分别驱动pin0和pin1。但是从改善之后的IO driver来看,driver可以从IO config对象中获取所有信号的“静态属性”。没错,对于控制行为而言,每一个信号的操作都不是随便的,因此他们的1或者0,何时拉高何时拉低大多数都是固定的行为,所以这些“静态属性”可以由鹏哥的DUT控制信号表单中自动提取生成到IO config对象中去。
那么可能会有一点问题,如果一些信号较为“随便”,没有太多静态属性怎么办呢?对于这种情况就可以由IO driver原有的驱动功能结构IO sequence item的动态属性来做灵活驱动啊。这样的话,一静一动,主于静态,次于动态,就能针对性地解决这个IO控制子系统的控制行为了。还是给出代码来解释这个应用:
class rk_ioc_config extends uvm_object;
rk_pin_props_t m_pins[int];
...
`uvm_object_utils_begin(rk_ioc_config)
`uvm_field_int(ioc_enable, UVM_ALL_ON)
`uvm_object_utils_end
function new(string name ="rk_ioc_config");
super.new(name);
init_pin_props();
endfunction : new
virtual function void init_pin_props();
rk_pin_props_t ps_init, ps;
//------------------------------
// pin0 initiator -> pin1
//------------------------------
ps = ps_init;
ps.init_val = 0;
ps.req_val = 1;
ps.wait_val = 1;
ps.rsp_vals.push_back(0);
ps.rsp_delay = 1;
ps.pin_t = RK_IOC_PIN_HANDSHAKE;
ps.src_idx = 0;
ps.tgt_idx = 1;