通知设置 新通知
6天前 关注
建立时间,保持时间,以及时钟skew对建立时间余量和保持时间余量的影响
寄存器能够稳定的把数据锁存起来,需要满足2个时间参数,一个是setup time,另外一个是hold time。setup time数据提前时钟沿到达的最小时间,hold time是时钟沿后数据保持的时间。如图4.1.1是集成电路中最常见的组合关系,Tco为从时钟上升沿处开始到寄存器输出端Q处有数据输出之间的时间间隔,Tdelay为组合电路,以及走线延时。
图4.1.1
探讨的问题:
数据data从A点开始到达C点,满足什么条件才能保证ff2稳稳的锁存住数据?
clk_a和clk_b没有时钟skew:
建立时间:
如图4.1.2所示,data从第一个时钟上升沿也即t0时刻,传到C,需要经过ff1以及delay模块,耗时分别是Tco和Tdelay,在时刻t1到达C。第2个时钟上升沿在t2时刻才能到达。t2-t1为data先于第2个时钟上升沿到达的时间,建立时间要求data要至少先于第2个时钟上升沿Tsu时间到达,t2-t1-Tsu为建立时间余量用Tsu_r表示,根据图4.1.2黄色区域可得如下公式:
上式中建立时间余量Tsu_r一定不能小于0,因此,
保持时间:
如图4.1.1所示,data要传到C,需要经历ff1以及delay模块,耗时分别是Tco和Tdelay.
现在关注图4.1.3所示的第2个时钟上升沿p2,p2时刻ff2锁存住C上面的数据1,根据保持时间知识,ff2寄存的数据C要保持至少Thd。p2时刻ff1锁存住的新数据0到达C所用的时间为Tco+Tdelay要比Thd大才能保证ff2输出稳定。
图4.1.3黄色区域可得如下公式:
其中Thd_r为保持时间余量,一定不能小于0,因此,
clk_a和clk_b之间有skew:
建立时间:
图4.1.4
时钟无skew:
因此,对比可知当有时钟skew时,建立时间空余量更大,因此更能满足建立时间的要求。
保持时间:
图4.1.5
对于ff1,p3时刻锁存的新的数据0,传到C端的所用时间依然是Tco+Tdelay,对于ff2,相对于ff1的p3其要过skew时间,p4才能锁存住数据1,依然要求C端数据1稳定的时间至少是Thd。根据黄色区域有如下公式:
无skew:
因此,对比可知当有时钟skew的时,保持时间空余量变小,因此不利于保持时间的满足。
6天前 关注
基于D触发器谈一下对数字电路中时序的一些理解(针对单个触发器的时序分析)
图1. D触发器symbol
为了便于理解给出了D触发器的symbol,时序图和比较简单的静态CMOS D触发器原理图
时序逻辑电路中重要的几个参数:
t-su : 建立时间,D的数据需要在建立时间前到达
t-hold: 保持时间,D在该段时间应保持稳定
Tc-q : 时钟到输出的延时,时钟到CLK→Q路径延迟
结合CMOS结构可以对上述三个时间有更好的理解:
图2. D触发器简单时序图
图3. D触发器简单的CMOS结构-时钟上升沿敏感
根据原理图对三个时间进一步理解:该电路工作原理,CLK = 0,T1开,数据经D传输至两个反相器进行缓存,等到CLK=1;T2开,数据经过反相器 I3,I4后输出至 Q。所以上升沿敏感其实传输的数据是CLK= 0 时的数据。因此需要被传输的数据应该在上升沿来到前稳定,即建立时间。保持时间需要从传输门进行考虑,CMOS结构并不是突变,MOS在输入变化时有一段时间不稳定,为了I1,I2处数据发生突变,所以数据需要稳定一段时间。Tc-q很好理解,路劲延迟。
结合上述分析,进行一个简单的实验。
实验内容,4位二进制计数器,计数器计数到10将valid拉高一个时钟,实验中有两个valid,一个valid_w,一个valid_r,具体不同见源代码:
module timing_test(clk,rst,valid_w,valid_r);
input clk;
input rst;
output valid_w;
output reg valid_r;
reg [3:0] cnt;
assign valid_w = cnt == 4'd10 ? 1'b1: 1'b0;
always @(posedge clk or posedge rst)
if(rst)
valid_r <= 1'b0;
else if(cnt == 4'd10)
valid_r <= 1'b1;
else if(cnt == 4'd11)
valid_r <= 1'b0;
else
valid_r <= 1'b0;
always @(posedge clk or posedge rst)
if(rst)
cnt <= 4'd0;
else
cnt <= cnt + 1'b1;
endmodule仿真结果如图4:
图4 实验仿真结果
可以看到valid_w,是在cnt = 10时拉高,而valid_r是在cnt = 11时拉高,如何理解呢?结合图5的RTL图可以做一个很好的解释。
图5 实验RTL图当时钟上升沿到的时候
计数加一需要在建立时间时才能被触发器采样,同时需要一段延迟才能数稳定。考虑当前时钟cnt=9;当下个时钟到来时,cnt触发器和valid_r触发器同时对clk上升沿敏感,D也同时采样,经过前面对时序的分析也可以知道,D采的是上升沿到来前的值所以valid_r采到的是cnt=9,在RTL_ROM中的比对结果,所以需要下个时钟才能获得cnt=10,在RTL_ROM中的比对结果。当然上升沿这个时刻valid_w也是0,但经过一小段传输延迟后valid_w变为0了,这里仿真结果在逻辑上是正确的,在大自然下应该会有一定的延迟。
1月前 关注
存储器的设计/建模
不知道从哪本书/哪篇文章摘来的了,先mark一下:
一、随机访问存储器(RAM)
(1)随机访问存储器(RAM)概述
①随机存储器可以随时从任何一个指定地址中读出数据,也可以随时将数据写入任何一个指定的存储单元中,RAM的结构如下:
②RAM 单元根据地址总线、数据总线以及读写控制线的数目可以分为单口RAM、双口RAM 两大类。
·单口RAM 只有一套数据总线、地址总线和读写控制线,因此当多个外设需要访问同一块单口RAM 时,需要通过仲裁电路来判断。
·双口RAM 具有两套完全独立的数据线、地址线和读写控制线,从而实现了大量数据的高速访问以及不同时钟域的数据交换。
(2)随机存储器设计基础
①在Verilog HDL 中,若干个相同宽度的向量构成数组,其中reg 型数组变量就代表着存储器。例如:
reg [7:0] memory[1023:0];
该语句定义了1024 个字的存储器变量memory,每个字的字长为8 位,(说白了,就是有1024个8bit宽的寄存器)经过定义后的memory 型变量可以用下面的语句对存储器单元赋值:
memory [7] = 90; //存储器memory 的第7 个字被赋值为90
②存储器单元中的数据也可以读出,因此存储器型变量相当于一个RAM。由于存储器由逻辑资源产生,因此存储容量越大,所需要的逻辑资源就越多。
(3)单口RAM 单元的实现
①单口RAM,只有一套地址总线,读和写是分开(至少不能在同一个周期内完成)。8× 8 位RAM 的设计举例实现如下:
②部分信号说明:
·addm 为3 比特地址线,可以实现8 个存储单元的寻址;
·cs_n 为片选信号,低有效,当cs_n 为低时,存储器处于工作状态(可以读或写);当cs_n 为高时,存储器处于禁止状态(强制输出0)。
·we_n 为写使能信号,低有效,当we_n为高时,存储器处于读状态,否则处于写状态。
·dout 为存储器的输出端口,din 为存储器的输入端口。
module ram_single( input clk,
input [2:0] addm,
input cs_n,
input we_n,
input [7:0] din,
output reg [7:0] dout
);
reg [7:0] ram1[7:0];
always @(posedge clk) begin
if(cs_n)
dout <= 8'b0000_0000;
else
if(we_n)
dout <= ram1[addm];
else
ram1[addm] <= din;
end
endmodule
综合得到的电路如下所示:
(4)双口同步RAM 单元
①双口同步RAM 具有两套地址总线,一套用于读数据,一套用于写数据,二者可分别独立操作。128× 8 位双口RAM 的实现举例:
②代码如下所示:
module rom_test(
output reg [7:0] q,
input [7:0] d,
input [6:0] addr_in,
input [6:0] addr_out,
input we, clk1, clk2
);
reg [6:0] addr_out_reg;
reg [7:0] mem[127:0];
always @(posedge clk1) begin
if (we)
mem[addr_in] <= d;
end
always @(posedge clk2) begin
q <= mem[addr_out_reg];
addr_out_reg <= addr_out;
end
endmodule
综合得到的电路如下所示:
二、只读存储器
①在数字系统中,由于ROM 掉电后数据不会丢失,对于容量不大的ROM,在Verilog HDL 中可以通过case 语句来实现。
②在应用中,case 语句中的数值可以根据实际需要修改,其中addm 为地址输入信号,cs_n为片选信号,8× 8 位的ROM 模块的实现:
module rom_test(
input clk,
input [2:0] addm,
input cs_n,
output reg [7:0] dout
);
always @(posedge clk) begin
if(cs_n)
dout <= 8'b0000_0000;
else
case(addm)
3'b000: dout <= 1;
3'b001: dout <= 2;
3'b010: dout <= 4;
3'b011: dout <= 8;
3'b100: dout <= 16;
3'b101: dout <= 32;
3'b110: dout <= 64;
3'b111: dout <= 128;21
endcase
end
综合得到的电路如下所示:
1月前 关注
静态时序分析的三种分析模式(简述)
经过跟行业前辈的探讨和参考一些书籍,本文中的“个人理解”部分有误,即:
(个人理解:)在一个库中,尽管电路器件单元已经被综合映射,但是工具可以通过改变周围的环境来得到不同的单元延时,所以即使是同一个库,调用工艺参数不一样的情况下,其单元延时是不同的,因此就有了最快路径和最慢路径。 (这里有误)。
对于一个综合好的电路网表,在一个确定的pvt环境下(即只读入一个库的情况下)、约束好了端口的transition和load,那么电路网表中的某个器件的延时是唯一确定的(从库查表得到)。ovc模式下一个器件才有两个延时值。
因此下面的库分析(延时分析)过程中,存在认识错误,请读者们注意!!!以后我会专门写一篇文章来更正的。
学习数字设计(数字IC设计、FPGA设计)都必须学习静态时序分析(Static Timing Analysis ,STA)。然而静态时序时序分析是一个比较大的方向,涉及到的内容也比较多,如果要系统得学习,那得花费不少的心思。这里来记录一下关于静态时序分析的三种分析模式,这里的记录只是记录一下学习笔记,或者说是随笔,而不是系统地学习STA。本文是来自于前天遇到了一道静态时序分析的题目,感觉有点疑惑,于是发到群里请求解答。经过一番讨论、查找资料之后,真相渐渐露出水面。
先看一下题目:
一、时序路径分析模式及相关概念
1.最快路径和最慢路径
在求解这道题目之前,先来介绍一下时序路径分析模式及相关概念。
①最快路径(early- path):指在信号传播延时计算中调用最快工艺参数的路径;根据信号的分类可以分为最快时钟路径和最快数据路径。
②最慢路径(late path):指在信号传播延附计算中调用最慢工艺参数的路径;分为最慢时钟路径和最慢数据路径。
(个人理解:)在一个库中,尽管电路器件单元已经被综合映射,但是工具可以通过改变周围的环境来得到不同的单元延时,所以即使是同一个库,调用工艺参数不一样的情况下,其单元延时是不同的,因此就有了最快路径和最慢路径。(这里理解有误)
注意:
与数据路径不同,最快时钟路径、最慢时钟路径的选择在建立时间分析和保持时间分析中是不同的。
1)建立时间分析最快时钟路径和最慢时钟路径如下图所示:
在建立时间分析中,最快时钟路径是指时序路径中时钟信号从时钟源点到达终止点时序单元时钟端口的延时最短捕获时钟路径,而最慢时钟路径是指时序路径中时钟信号从时钟源点到达始发点时序单元时钟端口的延时最长发射时钟路径。
2)保持时间分析最快时钟路径和最慢时钟路径如下图所示:
在保持时间分析中,最快时钟路径是指时序路径中时钟信号从时钟源点到达达始发点时序单元时钟端口的延时最短发射时钟路径,而最慢时钟路径是指时序路径中时钟信号从时钟源点到终止点时序单元时钟端口的延时最长捕获时钟路径。
2.分析模式
静态时序分析工具提供3种分析模式进行静态时序分析,不同的设计需求通过选择对应的时序分析模式从而可以在合理的时序计算负荷范围内得到接近于实际工作的时序分析结果。这三种模式是:单一分析模式(single mode)、最好-最坏分析模式(BC-WC mode)、全芯片变化分析模式(OCV模式)。
我查阅了一些资料,在Synopsys公司的静态时序分析工具PrimeTime在早期的userguide中是有说明的,例如2010.06版本中:
但是在最近两三年的版本中,却忽略了BC_WC模式,变成了其他三种模式,比如2015.12版本中:
虽然在user guide中没有明确写出BC_WC模式,但是这种模式还是存在的,也就是还是可以使用这种模式的。
在Cadence的时序分析分析工具Encounter Timing System的2013.01的版本中,也是支持这三种分析模式的:
上面的工具默认的都是单一模式。回到题目中,查了一下,这道题目是5、6年前(甚至更早)的了,算是很经典的一道题目。很显然,题目要求的是在BC_WC模式先分析建立时间和保持时间。在求解题目之前,先来看一下这三种模式是如何分析建立时间和保持时间的。
对于PT的2010.06版本:
对于PT的2015.12版本:
可以看到,2015.12并没有给出WC-BC模式的描述,但是是支持的。
对于ETS:没有给出表格,但是和PT的差不多。
下面就来介绍这三种模式下是如何分析路径延时的,这里只进行介绍建立时间的分析,看情况介绍保持时间。
二、单一分析模式(工具默认的模式)
1.模式介绍
在该模式下,工具只会在指定的一种工作条件下检查建立时间和保持时间,该工作条件可能是最好的、典型的、最坏的中的一种,但只能是单一的一种,
而这里不进行配置:
(1)建立时间分析
对于触发器到触发器时序路径的建立时间的要求,转换成单一分析模式下建立时间的基本计算公式如下:
发送时钟最慢路径延时+最慢数据路径延时≤捕获时钟最快路径延时+时钟周期-终止点时序单元建立时间
进行建立时间检查时,始发点触发器的发射时钟路径延时、终止点触发器捕获时钟路径沿和从始发点到终止点的数据路径延时都是基于单一工作条件下所计算的路径延时。这是工作单一的一个库中,也就是工具在同一工艺进程、温度、电源下,调用其他不同的工艺参数,得到最快、最慢的时钟路径和数据路径。这是路径值是确定的。例如下面例子中(时间单位为ns):
假设上述电路是在典型库中进行综合的,那么在分析建立时间的时候,工具通过调用不同的工艺参数,得到最慢的发射时钟路径、最慢的数据路径和最快的捕获时钟路径:
时钟周期=4
发射时钟最慢延时 = U1+U2 = 0.8+0.6 = 1.4
最慢数据路径延时 =3.6
最快捕获时钟延时 = U1+U3 = 1.3
时序单元FF2的建立时间要求查库得到0.2
因此 :建立时间的slack为:
1.3 + 4-0.2 - 1.4 - 3.6 = 0.1
(2)保持时间分析
保持时间的计算思路是一样的,这里只给出保持时间需要满足的公式,不再举具体例子。单一模式下要满足的保持时间要求如下所示:
发射时钟最快路径延时 + 最快数据路径延时≥捕获时钟最慢路径延时 + 终止点时序单元保持时间
2.题目计算
对于前面的题目,由于题目的要求是在WC-BC模式下,但是假如是在单一模式,我们来看看该如何分析:
单一库下工具提取到延时信息的理解如下:
下面分析题目中的路径:
对于F1和F2之间的建立时间分析如下所示:
时钟周期 = 2*4 = 8
最慢发射时钟路径(延时) = C1max + C2max = 1
最慢数据路径 = F1cqmax+L1max = 0.7+7 = 7.7
最快捕获时钟路径 = C1min + C2min + C3min = 0.6
F2的D端口建立时间 = 0.3
因此建立时间slack 为 :
8 +0.6 - 0.3 - 1 - 7.7 = -0.4 (建立时间违规)
对于F1和F2之间的保持时间分析如下所示(题目没有要分析这条路径的保持时间):
最快发射时钟路径:C1min + C2min = 0.4
最快数据路径:F1cqmin+L1min = 3.2
最慢捕获时钟:C1max + C2max + C3max = 1.5
F2保持时间 = 0.1
因此保持时间slack为:
0.4+3.2 - 1.5 - 0.1 = 2
同理可以分析单一模式下F3-F4路径的保持时间:
最快发射时钟路径:C1min + C2min = 0.4
最快数据路径:F3cqmin + L2min = 0.4
最慢捕获时钟:C1max + C2max + C4max+C5max = 2
F2保持时间 = 0.1
因此保持时间slack为:
0.4 + 0.4 - 2 - 0.1 = -1.3(保持时间违规)
三、最好-最坏分析模式(BC-WC)
1.模式介绍
对于最好-最坏分析模式,静态时序分析工具会同时在PVT环境中的最好的和最坏的工作环境下检查建立时间和保持时间。也就是说,使用这个方式的时候,至少需要读入两个库(环境),一个用来设置最好的工作环境(或者说延时最小),一个用来设置最坏的工作环境(或者说延时最大)。
(1)建立时间分析
最好-最坏分析模式中建立时间的基本计算公式与单一分析模式下建立时间的基本计算公式一致,不同点在于计算建立时间所使用的工作环境不同,在计算建立时间过程中静态时序分析工具调用逻辑单元的最大(max)延时时序库,并用来检查时序路径最大延时是否满足触发器建立时间。
例如对下面电路进行建立时间分析:
时钟周期 = 4
发射时钟最慢路径延时(max库)=U1单元延时(max库)+U2单元延时(max库)=0.7+0.6=1.3
最慢数据路径延时(max库)=3.5
最快数据路径延时(max库) =1.9
捕获时钟最快路径延时值(max库) = U1单元延时(max库)+ U3单元延时(max库)=0.7+0.5=1.2
建立时间要求(max库) = 0.2
因此触发器之间路径的建立时间slack为:
1.2 + 4 - 0.2 -1.3 - 3.5 = 0.2
(2)保持时间分析
同样,最好-最坏路径分析模式中保持时间的基本计算公式与单一分析模式下保持时间的基本计算公式一致。不同点在于计算保持时间所使用的工作环境不同。在计算保持时间过程中,静态时序分析工具调用逻辑单元的最小(min)延时时序库,并用来检查时序路径最小延时是否满足触发器保持时间的约束。 即进行保持时间检查时,始发点触发器的发射时钟延时、终止点触发器捕获时钟延时和从始发点到终止点的数据路径延时都是基于最好工作条件下所计算的路径延延时
例如对下面电路进行保持时间分析:
时钟周期 = 4
发射时钟最快路径延时(min库)=U1单元延时(min库)+U2单元延时(min库)=0.5+0.4=0.9
最快数据路径延时(min库)=1
最慢数据路径延时(min库) =2.3
捕获时钟最慢路径延时值(min库) = U1单元延时(min库)+ U3单元延时(min库)=0.5+0.3=0.8
保持时间要求(min库) = 0.1
因此触发器之间路径的保持时间slack为:
0.9+1-0.8-0.1 = 1 (保持时间不违规)
2.题目计算
对于我们的题目,就是要求我们在BC-WC模式下进行分析建立时间和保持时间。现在就来分析一下。
首先我们分析工具提取到延时信息:
这里我们需要注意,本来我在读入max库的时候,应该有会得到max库下的单元延时的最大最小值;在读入min库的时候后,会得到min库下单元的最大最小值。题目中相当于只有一个库下单元延时的最大最小值,这个库取了max库的最大延时,同时取min库的最小延时。因此在进行WC分析的时候,我们就将max库中的单元延时最大和最小值看做相等进行处理,即max库中单元只有一个固定延时值(即上面的max列表里面)。同理BC分析的时候,min库中的单元也只有一个延时值(即上面的min列表里面)。
下面就对时序路径进行分析:
对于F1-F2路径的建立时间分析如下所示:
时钟周期 = 2*4 = 8
最慢发射时钟路径(max库) = C1max + C2max = 1
最慢数据路径(max库) = L1max = 7
最快捕获时钟路径(max库) = C1maxmin + C2maxmin + C3maxmin=C1max + C2max + C3max = 1.5 (maxmin表示max库下的最快路径)
F2的D端口建立时间 (max库)= 0.3
因此建立时间slack 为 :
8 +1.5 - 0.3 - 1 -0.7- 7 = 0.5
对F3-F4路径的保持时间分析如下所示:
最快发射时钟路径(min库):C1min + C2min = 0.4
最快数据路径(min库):F3cqmin + L2min = 0.4
最慢捕获时钟(min库):C1minmax + C2minmax + C4minmax+C5minmax =C1min + C2min + C4min+C5min=0.8 (minmax表示min库下的最慢路径)
F2保持时间 = 0.1
因此保持时间slack为:
0.4 + 0.4 - 0.8 - 0.1 = -0.1(保持时间违规)
四、OCV分析模式
在芯片变化相关工作模式下,与最好-最坏分析模式一样,静态时序分析工具也会同时在PVT境中的最好的和最坏的工作环境下检查建立时间和保持时间,也就是要读入两个库。
1.基本的OCV模式:
(1)建立时间的分析
OCV分析模式中建立时间的基本计算公式与其他分析模式下建立时间的基本计算公式一致,不同点在于计算最快路径和最慢路径所使用的工作环境不同,在计算建立时闻过程中静态时序分析工具调用时序单元的最大延时时序库来计算最慢路径的延时,同时调用逻辑单元的最小延时时序库来计算最快路径的延时,只检查时序路径的延时是否满足触发器建立时间的约束。
进行建立时间检查时。始发点触发器的发射时钟采用的是最坏条件下最慢时钟路径,终止点触发器的捕获时钟采用的是最好条件下最快时钟路径,而从始发点到终止点的数据路径的延时则是在最坏条件下最慢数据路径延时。
例如:
时钟周期 = 4
发射时钟最慢路径延时(max库)=U1单元延时(max库)+U2单元延时(max库)=0.7+0.6=1.3
最慢数据路径延时(max库)=3.5
捕获时钟最快路径延时值(min库) = U1单元延时(min库)+ U3单元延时(min库)=0.5+0.3=0.8
建立时间要求(max库) = 0.2
因此触发器之间路径的建立时间slack为:
0.8 + 4 - 0.2 -1.3 - 3.5 =- 0.2(时序违规)
·保持时间:类似,不进行详细描述
(2)题目计算
对于我们的题目,假设要在基本的OCV模式下进行计算,我们来看一下:
首先库的分析WC-BC模式一样,不重复说明:
然后对时序路径进行分析:
对于F1-F2路径的建立时间分析如下所示:
时钟周期 = 2*4 = 8
最慢发射时钟路径(max库) = C1max + C2max = 1
最慢数据路径(max库) = F1cqmax+L1max = 0.7+7 = 7.7
最快捕获时钟路径(min库) = C1min + C2min + C3min= 0.6
F2的D端口建立时间 = 0.3
因此建立时间slack 为 :
8 +0.6 - 0.3 - 1 -7.7= -0.4 (建立时间违例)
对F3-F4路径的保持时间分析如下所示:
最快发射时钟路径(min库):C1min + C2min = 0.4
最快数据路径(min库):F3cqmin + L2min = 0.4
最慢捕获时钟(max库):C1max + C2max + C4max+C5max =2
F2保持时间 = 0.1
因此保持时间slack为:
0.4 + 0.4 - 2 - 0.1 = -1.3(保持时间违规)
对于上面的题目,在基本的OCV模式中计算分析建立时间时,公共路径C1、C2在计算最慢发射时钟路径时,使用的是max库的最慢延时;而在计算最快捕获时钟路径的时候使用的是min库的最快延时。也就是说,该分析把公共路径的输出,当做两个不同传播延时的信号进行延时计算。然而在芯片实际工作时,公共路径的输出是一个信号驱动后续的发射时钟和捕获时钟,上面的检查分析太过于悲观,不太符合实际,因此延伸到下面两种模式。
2.考虑时序减免的OCV模式:
时序减免〔timing derate)的作用是很据减免(derating)系数,静态时序分析工具会在时序路径的每级逻辑门、连线和端口上都加上或减去一个原来延时值乘以减免系数值的延时作为最终的延时结果。设置时序减免值的目的是使时序分析结果更加符合实际情况。
使用这种方式需要设置derating系数,系数值需要通过实际工程经验总结出来,这里不进行深入探讨,而且题目中没有给出该系数,因此不进行深入介绍。
3.考虑时钟路径悲观移除(CPPR)的OCV模式:
可以分为 不考虑时序减免和考虑时序减免情况,这里不进行介绍,感兴趣可以参考有关资料。
其他先进的分析模式如AOVC、POCV等,涉及内容较多,就不写了,感兴趣可以参考有关资料。
参考资料:
《集成电路静态时序分析与建模》,刘峰,2016
《PrimeTime User Guide》,synopsys,2015
《PrimeTime Fundamentals User Guide》,synopsys,2010
《Encounter Timing System User Guide》,cadence,2013
1月前 关注
Linux系统的基本使用
曾经在网上看到一个一篇文章,说到了Linux学习的入门与学习技巧,也就是:初学者可以自己安装虚拟机,然后把linux常用命令例如cd、ls、chmod、useradd、vi等等多练习几十遍,把自己敲打命令的熟练程度提升上来。然后根据文档搭建Linux下常见的各种服务(DHCP、SAMBA、DNS、Apache、Mysql等),遇到问题后可以在google搜索,搜索的时候多看几篇文章,综合最好的文章来解决问题。能够熟练的搭建服务后,理解每个服务的完整配置和优化,可以拓展思维。例如LAMP,我们一般是把所有服务放在一台机器上,如果分开多台该如何部署呢?等等。平时多积累shell编程,可以在网上查找前辈们写的非常好的shell,自己下载下来多练习几遍,从中吸取,不断提高。
然而对于一个数字IC设计人员,学习Linux的主要目的是熟悉Linux的操作环境与相关EDA工具的使用,因此搭什么服务器什么的,用到的时候再充电吧。这篇博文记录一下linux的基本使用,内容如下所示:
·系统的目录介绍
·常用的linux系统操作命令介绍
-用户切换
-显示文件列表
-清屏跟打印当前目录
-后台运行
-文件(夹)的基本操作:创建、删除、移动、复制
-文件内容的查看
-文件字数统计
-文件属性及更改
-文件的查找
-文件的压缩与解压缩
-程序管理
-rpm包安装
-网络配置信息
1、系统的目录介绍
接下来主要熟悉一下系统的目录。我装的是RHEL6 32位的虚拟机,进入虚拟机后的界面是这样的:
打开终端:
显示的是桌面的位置。
Linux的目录结构如下所示:
在我的Linux中查看的结果如下所示:
说明如下:
/ ------根目录
/bin ------存放必要的命令
/boot ------存放内核以及启动所需的文件
/dev ------存放设备文件
/etc ------存放系统配置文件
/home ------普通用户的宿主目录,用户数据存放在其主目录中
/lib ------存放必要的运行库
/mnt ------存放临时的映射文件系统,通常用来挂载使用。
/proc ------存放存储进程和系统信息
/root ------超级用户的主目录
/sbin ------存放系统管理程序
/tmp ------存放临时文件
/usr ------存放应用程序,命令程序文件、程序库、手册和其它文档。
/var -------系统默认日志存放目录
2、常用的命令
我先学习目录,主要是先对整体的结构有个了解,因为那个基本不需要动手,看看书就可以了解记忆了的。需要动手记忆的,就是这些常用的命令了。
进入系统前,需要输入密码:
cfx就是普通的用户名,而后面的就是是hostname。
普通用户与超级用户(root用户)
输入密码打开终端后,我们会看到这样的字符:[cfx@localhost Desktop]$ ,表示当前是叫cfx的普通用户登录,$表示当前为普通用户。
切换到超级用户为:
输入密码,就可以进入了:
[root@localhost cfx]#,其中#代表当前是root用户登录。运行 su cfx,就可以回到普通用户。
那我来学习第一个(貌似不是第一个)Linux命令:
cd命令
在命令行中运行 cd /home :
意思是进入/home目录。
cd /boot --------------进入/boot目录 ;
cd .. --------------返回上一级目录;
cd ./x1/y2/z3------进入当前目录x1文件夹下的y2文件下的z3文件夹中;(.和..可以理解为相对路径;例如cd /hom/test ,cd加完整的路径,可以理解为绝对路径)
更多的常用命令:
①列出文件
ls ./或者ls ------ 查看当前目录所有的文件和目录:
ls -a 查看所有的文件,包括隐藏文件,以.开头的文件:
ll 查看文件的信息(信息的内容在之后解释):
ll -all ,ll -a ,ls -all,ls -a 显示所有文件及其信息:
②清屏跟打印当前目录
clear 清屏:
pwd 显示当前所在的目录。
③创建移动删除文件(夹)---------以下无演示
mkdir 创建目录,命令后接目录的名称。
mkdir test -----创建名字叫test的目录(文件夹)。
rmdir 删除空目录,用法同mkdir。
rm 删除文件或者目录
rm –rf test.txt 强制删除test.txt文件(-r表示递归,-f表示强制)。
cp 拷贝文件
cp old.txt /tmp/new.txt 复制文件old.txt到tmp目录中,并且代替new.txt文件的内容。
cp /xxx/xxx/a /xxx/z/ 复制a文件到z目录下。
如果拷贝目录(文件夹)需要加 –r参数。(cp -r /xxx/xxx/a /xxx/z/)
mv 重命名或者移动文件或者目录
mv /xxx/xxx/a /xxx/z/ 移动a文件到z目录下。
mv old.txt new.txt 重命名old为new
其他创建
touch 创建文件,
touch test.txt,如果文件存在,则表示修改当前文件时间。
Useradd 创建用户
useradd wugk ,userdel删除用户。
Groupadd 创建组
groupadd wugk1 ,groupdel删除组。
④后台运行
& 后台运行
dve & 后台运行dve
⑤对文件的其他操作
查看文件:
cat file : 在终端查看file文件的内容
more file : 直接查看file的内容
less file : 像gvim和vi那样查看file内容
查看文件内容情况:
wc file : 计算文件的行数和字节数
wc -w file : 计算文件的字数
wc -l file : 计算文件的行数
改变文件属性:
每个Linux文件具有四种访问权限:可读(r)、可写(w)、可执行(x)和无权限(-)。利用ls -l命令可以看到某个文件或目录的权限,它以显示数据的第一个字段为准 ,第一个字段由10个字符组成。
如monitor_log.sh文件的属性:
-rw-r--r-- 1 root root 91 May 7 20:21 monitor_log.sh
对于档案的类型属性(10位字符):
第一位:表示文件类型,-表示文件,d表示目录;后面每三位为一组。
第一组:2-4位表示文件所有者的权限,即用户user权限,简称u
第二组:5-7位表示文件所有者所属组成员的权限,group权限,简称g
第三组:8-10位表示所有者所属组之外的用户的权限,other权限,简称o
从上面这个文件,我们可以看出,monito_log.sh文件对应的权限为:
root用户具有读和写的权限,root组具有读的权限,其他人具有读的权限。
为了能更简单快捷的使用和熟悉权限,rwx权限可以用数字来表示,分别表示为r(4)、w(2)、x(1)。
Monitor_log.sh权限可以表示为:644
如果给某个文件授权(最好在根目录下),命令为chmod:chmod 777 monitor_log.sh
⑦查找文件
find 查找文件或目录
命令格式为:find 后接查找的目录,-name指定需要查找的文件名称,名称可以使用*表示所有。
find /home -name “test.txt”,
find /home -name “*.txt” :查找/home目录下,所有以.txt结尾的文件或者目录。
find /etc -name '*con*' :查找/ect目录下,名字有con的文件
find / -size +100M :查找文件大于100M的文件
⑧对文件进行解压与压缩
在Linux 的环境中,压缩文件案的扩展名大多是:『*.tar, *.tar.gz, *.tgz, *.gz, *.Z, *.bz2』tar 的选项与参数非常的多!我们只讲几个常用的选项,更多选项您可以自行 man tar 查询啰!
压缩成.tar文件: tar -cvf file.tar files
解压.tar文件: tar -xvf file.tar
压缩成.tar.gz文件: tar -czvf file.tar.gz files
解压.tar.gz文件: tar -xzvf file.tar.gz
压缩.tar.bz2文件: tar -cjvf file.tar.bz2 files
解压.tar.bz2文件: tar -xjvf file.tar.bz2
压缩成.gz 文件 gzip file
解压 gzip -d file.gz :
⑨程序管理
ps : 显示当前活跃的进程(程序)(列出运行中程序的pid号)
top : 显示系统的所有进程
kill pid : 结束号为pid的进程
killall proc : 结束所有的proc进程
bg : 列出在后台悬挂的进程
fg : 重启最近一个悬挂在后台的进程
⑩安装rpm包
rpm -ivh RPM.rpm : 安装rpm包
cat /proc/cpuinfo : 显示cpu信息
cat /proc/meminfo : 显示存储器信息
------ network info --------------------------
ping host|IP : ping一下IP地址,然后显示结果
hostname : 显示系统的hostname
ifconfig -a : 显示网络的配置信息
1月前 关注
使用Modelsim进行简单仿真
这里记载一下使用modelsim进行简单的仿真,方便以后使用的时候进行查看。所谓的简单的仿真,就是没有IP核、只用图形界面不用tcl脚本进行的仿真。简单的仿真步骤为:
1、改变路径到工作环境下的路径下面,创建工程。
2、添加仿真的源文件(.v文件等)。
3、编译源文件。
4、启动仿真,添加仿真信号。
5、调试、查看仿真波形。
这里就使用一个简单的例子——8分频电路,进行演示和讲解:
1、改变路径到工作环境下的路径下面,创建工程:
①打开modelsim后如果有工程,则先关掉工程:
关掉工程:
②改变工作路径:
③创建工程
OK之后,在当前的工作目录下面就创建了一个“work”的文件夹,这也就是物理库:
2、添加仿真源文件
或者右击空白处:
然后进行选择添加:
3、编译源文件
或者:
编译成功后:
如果编译不成功的,根据提示的信息,进行修改代码,然后对不成功的模块进行部分编译:
(如果modelsim看到的中文是乱码,那么可以在编辑器中进行模式转换,如从ASC转换成xxx编码模式),然后修改再编译(注意,所有的目录都应该放在英文路径中)。
4、启动仿真,添加仿真信号。
①切换到库选项卡,点开work,启动仿真
在信息栏上面有两个选项卡:library和project两个选项卡,编译完之后,文件被编译到work目录下(library上面有work的选项,点开可以看到设计和仿真的.v文件),work文件夹里面包含此次工程编译库的信息;用modelsim打开的是.mpf
文件,也就是.mpf是modelsim的工程文件。
右击tb文件(如下图所示),选中测试模块的文件,右击,选择第二个simulation without optimistic(不进行优化代码再仿真,因为优化代码可能会把一些信号给优化掉),然后就启动了仿真:
②进行添加仿真信号
启动仿真后,信息栏上面的选项卡会增加一个选项:sim;也就是总共有三个选项卡在信息栏上面(Library,project,sim)在sim选项中,左边是模块的整体结构;右击例化的设计文件,选择 ADD wire,就可以添加信号到波形窗口
然后就弹出了波形窗口:
5、调试、查看仿真波形。
①点击运行
就可以看到波形了:
Run是每点击一次运行固定的时间(如100ns);
Continuerun是一直运行,直到点击break,点击break后显示波形;
runall是运行所有,直到点击break,点击break后显示波形。
②看波形,发现不对;修改代码后,需要在project区进行重新编译,然后回到sim区,点击重新开始,然后根据①的步骤就可以继续完成简单的仿真工作了。
注:添加仿真信号可以直接把信号拖到波形窗口区:
然后可以调试查看波形。关于其他调试、查看波形的技巧,以后学习了、用到了再进行记录。
1月前 关注
UVM寄存器模型regmodel
本文来自于猴哥验证公众号。
从这一期开始我们聊一聊regmodel这个话题,regmodel涉及的方方面面很多,我们分几期展开描述。
对于初入验证这个坑的小伙伴来说,regmodel还比较陌生,存在和设计里面的寄存器怎么个关系理解不太清晰的情况。
字面上看regmodel就是寄存器的模型,这个寄存器是设计里面的控制寄存器组,并不是Verilog的reg信号类型。
一个寄存器设计中是实现一个或者多个功能,比如:
reg[15:0] src_addr;
这是一个16位源地址的控制寄存器。
regmodel是一个模型,它是验证环境的一部分,可以看做是DUT设计里面寄存器组在环境中的抽象。
regmodel的内容来源于DUT,是模块设计在环境中的一个映射,模块设计在实现的映射就是DUT代码咯,这个概念之前有不少小伙伴没太搞懂,大家注意一下。
regmodel和DUT之间的连接一个是前门方式也就是通过总线的访问,另外一个是后门方式,这个问题我们后面再展开讲。
所以,regmodel和DUT设计其实是寄存器组的两个表现,它们是一致的,但是一个是环境中的抽象组件,一个是实际的rtl代码,通过行为方式将它们联系起来,保持一致性。
regmodel是随验证方法学发展起来的,OVM里面是叫RGM,VMM里面是regmodel,后来UVM采用了VMM的方式。
SOC系统中寄存器是非常关键的一个部分,系统中的各项功能都是根据寄存器的配置来实现的。因此寄存器验证涉及到两个方面,一个是对寄存器本身的验证,一个是寄存器的使用。
寄存器本身的验证包括reset 默认值的检查,寄存器的读写属性检查,不使用regmodel的时候一般怎么来做呢?
`define SRC_ADDR 16'h10 // 宏定义src_addr寄存器的地址
//dut reset
reg_read(`SRC_ADDR, data ); //读取默认值
reg_write(`SRC_ADDR,0 ); //写0
reg_read(`SRC_ADDR, data ); //读取检查是不是0
reg_write(`SRC_ADDR,16'h55AA ); //写55AA
reg_read(`SRC_ADDR, data ); //读取检查是不是55AA
reg_write(`SRC_ADDR,16'hAA55 ); //写AA55
reg_read(`SRC_ADDR, data ); //读取检查是不是AA55
reg_write(`SRC_ADDR,16'hFFFF ); //写AA55
reg_read(`SRC_ADDR, data ); //读取检查是不是AA55
以上是一个简单的寄存器测试流程,地址使用宏定义传递给task,对寄存器做默认值检查,写0写全1,写全翻转值检查。
这是一个比较纯粹的RW寄存器的测试,那么还有其它属性的寄存器,比如有效位小于寄存器宽度的,寄存器属性只读,只写,读清0,写清0。
对于这些属性,就需要做个class或者struct表示这些属性,然后在寄存器处理的时候根据各个属性做对于的处理操作。
这些处理方式对于寄存器来说都是固定的,而各个公司的处理存在实现的差异,那么使用统一的代码来完成这个任务,就会大大提高效率,这只是regmodel的一个功能而已。
使用寄存器做功能验证,不使用regmodel的情况下,大多会采用宏定义地址方式,这个地址值可以由脚本生成,由于宏定义是一个全局的数值,复用性大打折扣。
对于模块级验证SRC_ADDR是'h10地址,到子系统的时候需要加上偏移,这样宏定义变成如下情况:
`define SRC_ADDR X0_BASE+'h10
如果子系统上例化了2个这样的模块,那么宏定义变成如下情况:
`define X0_SRC_ADDR X0_BASE+'h10
`define X1_SRC_ADDR X1_BASE+'h10
到系统级宏定义又变成如下情况:
`define X0_SRC_ADDR SYS_BASE+X0_BASE+'h10
`define X1_SRC_ADDR SYS_BASE+X1_BASE+'h10
如果两个系统在一个环境里呢?又要加区分了
`define S0_X0_SRC_ADDR SYS0_BASE+X0_BASE+'h10
`define S0_X1_SRC_ADDR SYS0_BASE+X1_BASE+'h10
`define S1_X0_SRC_ADDR SYS1_BASE+X0_BASE+'h10
`define S1_X1_SRC_ADDR SYS1_BASE+X1_BASE+'h10
那么对于寄存器操作的sequence复用起来比较困难,再增加一层宏定义处理方式,如果要实现模块到系统的复用,那么必须在一开始做好规划,而且不能有大的改动。
还有一个情况,现在SOC系统一般都是多核异构的系统,存在多个master都会对同一模块操作。不同的master寄存器映射关系又存在不同,那么宏定义又变成如下:
`define X0_CPU_SRC_ADDR X0_CPU_BASE+'h10
`define X1_CPU_SRC_ADDR X1_CPU_BASE+'h10
`define X0_DSP_SRC_ADDR X0_DSP_BASE+'h10
`define X1_DSP_SRC_ADDR X1_DSP_BASE+'h10
再加上系统级,双系统......
所以regmodel要解决的另外一个问题就是地址问题,通过分block,分subsystem管理寄存器地址,由regmodel自己去解析对于的address信息,提高抽象行为。
regmodel带来了很多好处,我们都怎么来使用它呢?同样的它也有自己的局限性,那都是些什么问题呢?这些都会在后续文章中一一展开描述。
2月前 关注
Soc debug经验<5>
在验证芯片的case中,很多时候需要先在自己的case中配置模块中的一些寄存器(这些寄存器的某些域可能是数据能够成功穿过该模块的条件,因此必须优先配置这些寄存器)。在数据在到达该模块之后,只有先配置这些寄存器,该模块才能成功输出数据到下一个模块。在配置好这些寄存器之后,可能需要检测这些配置是否达到要求,会有如下代码。
Unsigned int timeout;
unsigned int signal;
timeout=100000;
signal=0;
While( (signal!=1) &&timeout!=0)
{
signal= get_value(tb.a.siganl_A);
@ posedge clk;
Timeout- -;
}
这段代码的意思是case在执行到该代码时,在10000个clk之内,判断模块a中的信号signal_A是否是1。如果一直不是1, 说明初始化某些寄存器没有达到要求。在仿真的log中,我们可以看到仿真不会继续走下去,如果在case中关键位置加了打印信息,打印信息到这里就不会继续打印了。
我们如果知道配置哪一个寄存器可以影响到signal_A,我们首先要在log 中去匹配这个寄存器地址,先检查一下我们配置了几次该寄存器,都配置了哪些值。如果不知道具体是哪一个寄存器的话,只能在波形中trace该信号signal_A。
在波形中,可以拉出来该信号signal_A,会发现其值是0。一直trace,可能会发现如下代码。
Port_enable=EN?enable_A:enable.
EN值是1, enable_A的值是1,但是Port_enable的值是0。Port_enable的值和enable_A的值不一样。现在只需要解决为什么Port_enable的值和enable_A的值不一样即可。
这里可能会发现,该信号Port_enable 被force成0了。我们在波形中的message中,可以看到Port_enable是多驱动,另外一个赋值语句是force语句。
force tb.a.b.Port_enable=1’b0;
一般情况下,所有的force语句都被按模块分在同一个目录下面,我们在该目录下,去搜索信号Port_enable,就能看到是否被force了。
4月前 关注
验证环境中经常使用的参数$test$plusargs和$value$plusargs
$test$plusargs和$value$plusargs是Verilog和SystemVerilog仿真运行时调用的系统函数,可以在仿真命令行直接进行赋值,并且不局限于不同仿真器对于参数在仿真命令中定义格式不同的限制,也避免了调换参数带来的频繁编译等问题。使用这两条函数对于搭建测试平台有一定的便利,同时对于理解Factory中用例是如何传递进Proxy Class有一定的帮助。
本文将对$test$plusargs和$value$plusargs使用过程中遇到的问题进行小结。
首先,在进行宏定义时,我们经常使用`ifdef等命令在代码中,参看下例:
test.v
initial begin
`ifdef dumpon
$dumpfile("results.vcd");
$dumpvars;
`endif
end
如果要能够成功调用$dump等函数,需要在编译(compile)时指定`define的宏定义,其使用方法如下:
但是,在仿真过程中不需要该部分定义时该如何处理呢?
当需要改变编译条件时,经常需要重新编译。并且一旦编译通过,在编译阶段指定的宏定义在整个仿真运行过程中一直有效,因此,如果需要修改宏定义,则需要重新进行编译,从而降低了仿真的效率。
为此,可以使用$test$plusargs和$value$plusargs进行解决,该函数的调用发生在仿真运行(run)阶段。这样仅需要对设计进行一次编译即可,如果需要改变相应的条件,可以在run的时候动态指定,这样有利于脚本处理进行回归的验证,同时也有利于object的动态construct。
1.$test$plusargs
在运行(run)仿真时指定要选择的条件,即只需要在仿真运行命令(run-options)中指定参数需要选择的条件即可,例如下例中,如果要将test01.dat、test02.dat、test03.dat分别load到各自的men中,仅需要如下命令在运行命令中加入“<+test01+test02+test03>”即可,当仿真运行时,$test$plusargs会在命令行中搜索指定的字符,若找到相应字符,在函数返回“1”,否则返回“0”。如果下次仿真时不需要test01时,仅需要将test01从运行命令中删除即可。
test.v
initial begin
if($test$plusargs("test01"))
$readmemh("test01.dat",mem01);
if($test$plusargs("test02"))
$readmemh("test02.dat",mem02);
if($test$plusargs("test03"))
$readmemh("test03.dat",mem03);
end
2.$value$plusargs
$value$plusargs可以讲运行命令(run-options)中的参数值传递给指定的信号或者字符,其语法格式如下:
Integer=$value$plusargs(“string”,signalname);
其中string=”plusarg_format”+”format_string”,”plusarg_format”指定了用户定义的要进行传递的值,”format_string”指定了要传递的值的格式(类似$display中定义的%s、%h、etc.),并且string中”plusarg_format”和”format_string”格式应该为”plusarg_format”(=/+)”format_string”。如果转换后的位宽和传递的值不一致,则按照如下规则转换:
plusarg位宽与sigalname的关系 | Signalname值 |
< | plusarg左补零 |
> | plusarg截位 |
plusarg为负数 | 按照正数处理 |
不匹配 | 若为指定默认值,则reg类型为x |
$value$plusargs使用示例如下:
test.v
if($value$plusargs("FINISH=%d",stop_clk)) begin
repeat(stop_clk) @(posedge);
$finish;
end
if($value$plusargs("TESTNAME=%s",testname))
begin
$display("Running test %0s",testname);
...
end
if($vaue$plusargs("FREQ=",frequency)) begin
frequency=8.333333;
...
end
若使用的运行命令如下:
则上例的运行结果为:
stop_clk : 10000
testname:this_test
frequency:5.6666(如果run-options中没有增加“FREQ=5.6666”,那么frequency为8.333333)。
4月前 关注
Soc debug经验<4>
在soc芯片验证的时候,有时候需要我们验证一段数据通路,我们需要发送一个数据包,让这笔数据包从起始端,到达我们想要的目的端。这里有两个比较重要的点是需要注意的,第一个点是如何保证这笔数据包按照我们想要的数据通路走;第二个点是如果已经按照我们想要的数据通路走下去了,如何保证这笔数据能成功穿过每一个模块而不丢掉。
1. 如何保证这笔数据按照我们想要的数据通路走。
例如数据通路A<->B<->C<->D<->E。我们想让数据通过A->B->C->D->C->B->F。在Soc芯片中,如果我们想从A模块发送数据到F中,在编译的时候需要A模块是一个A模块的UVC,也就不是A模块的rtl代码,这样我们就可以利用A模块的UVC发送数据包 。从A发出来的数据包到达了D之后,可以有两路通路,一个是到E模块,另一个可以是从D返回去,也就是D->C->B->F,到达F模块。那这里如何保证数据包到达D模块之后,让数据包沿着D->C->B->F,到达F模块。
在模块D中,会有各种各样的Decoder小模块,这些Decoder决定了到达模块D之后,数据包接下来的走向。这些Decoder是根据来源不同划分的,比如从模块A发出来的数据包可以叫A_Decoder,从其他模块A1发出来的,可以叫A1_Decoder,A和A1大概率属于不同类型的模块。这两个Decoder的逻辑很相似,里面都会有一个信号destination,该信号的值决定了要到达哪一个模块。
举例:
assign Destination= ~enable? 0:F_mem? `to_F : E_mem? `to_E: 0;
可以看到,我们要想让数据包成功到达F,必须让F_mem拉高。我们继续追踪F_mem。
可能会发现这个F_mem的逻辑是对地址的范围判断。
举例:
assign F_mem=(addr >=F_mem_low)&&(addr<=F_mem_high);
这里就是说,我们从A模块发送到F模块的地址是有限制的,如果这个地址在F模块的memory区间里,接下来D模块就会发送数据到F,沿着D->C->B->F模块走。这里可能牵扯到在我们的case 里面,要先配置F_mem_low 和F_mem_high之后,才去从模块A发送数据包。如果你知道配置哪一个寄存器可以配置F_mem_low 和F_mem_high,那更好了,如果你不知道配置哪一个寄存器,这也没有关系,你可以读完接下来我要说的第二点去找到该寄存器。
2. 如果已经按照我们想要的数据通路走下去了,如何保证这笔数据能成功穿过每一个模块而不丢掉。
假设我们想验证A->B->C->D->C->B->F这段数据通路。从模块A发送出来的地址到达模块C 的输入端口之后,这个时候发现数据包在C到D的输出端口上没有了。我们去追踪该输出地址接口,可能会发现如下逻辑。
Case (state)
`idle:
If(enable)
begin
C_D_addr<=C_D_addr_;
state<=`send;
end
`send:
…..
我们可能发现C_D_addr_是有数据的,但是这个enable是等于0的,这个状态机只停留在idle状态,C_D_addr没有数据。
这里enable没有起来,所以我们会继续追踪enable为什么没有被拉起来。(这个就像第一个点里面F_mem_low和F_mem_high没有数据是一样的)我们一直追踪enable,会发现如下decoder代码。
case(addr[7:0])
20: enable_C_D_reg_control <=1;
30: …….
…….
我们如果追踪addr,能看到完整的地址,在波形中的这些地址都是我们已经配置的寄存器,这里显然没有我们想要的那个寄存器。实际也没有必要去追踪addr了。首先我们要明白,enable_C_D_reg_control是我们要配置寄存器的一个域,然后要配置寄存器地址的后面8位是20。所有寄存器地址define一般都会放在一个文件里。我们可以通过匹配一些关键词去找到该寄存器。
格式如下:
` enable_C_D_reg 10122120
…….
比如我们找到了该寄存器叫enable_C_D_reg,我们还需要确定域enable_C_D_reg_control在enable_C_D_reg是第几位。一般情况下,我们可以去查询寄存器的spec,去了解一下enable_C_D_reg寄存器,也可以通过查询UVM的寄存器模型去了解该寄存器。假设我们查到了该域enable_C_D_reg_control是32位寄存器enable_C_D_reg的第二位。那在我们从A端发送数据包之前,需要在自己的case cpp里面配置该寄存器。每个验证环境配置具体函数不一样,但是大致思路都是一样的。配置思路代码如下:
data= api.read(enable_C_D_reg);
data=data|0x00000002;
Api.write(enable_C_D_reg,data);