版本兼容地启动sequence
uvm从1.1d到1.2再到IEEE1800.2,有了很多变化。尤其是从1.1d到1.2,在objection的使用上有了一些关键性变化。
在uvm进入到1.2后,starting_phase不在推荐使用。更为重要的是,不仅仅是不再推荐,而且如果以default sequence的方式启动以后,default sequence被启动以后,starting_phase依然会是null,如果沿用以前的代码,整个平台就起不来了
task body()
if (starting_phase != null)
starting_phase.raise_objection(this)
*//do something*
if (starting_phase != null)
starting_phase.drop_objection(this)
endtask
尽管starting_phase不推荐使用,但是这个类并没有被uvm删除,如果想要继续使用,那么需要在使用之前进行如下赋值。
starting_phase = get_starting_phase()
get_starting_phase的原型如下
如果不想这么麻烦,也可以使用1.2的新方法,
uvm_sequence_base::set_automatic_phase_objection
下面是一个例子
function my_sequence :: new( string name = "unnamed");
super.new(name);
set_automatic_phase_objection(1);
endfunction : new
在时间上,他的整个运行过程如下
/*
start() is executed
--! Objection is raised !--
pre_start() is executed
pre_body() is optionally executed
body() is executed
post_body() is optionally executed
post_start() is executed
--! Objection is dropped !--
start() unblocks
*/
当然,也不一定要在new函数中set,也可以在new以后,再额外set
my_legacy_seq_type seq = new("seq");
seq.set_automatic_phase_objection(1);
seq.start(my_sequencer);
但是个人不推荐使用,因为如果环境在1.1d和1.2之间切换,会产生意想不到的问题,在1.1d中并没有这种方法。
所以按照个人理解,最稳妥的方法就是直接使用start启动sequence,并且在start前后控制objection,像下面这样
task main_phase(uvm_phase phase)
my_seq_type seq = new("seq");
phase.raise_objection(this)
seq.start(my_sequencer);
phase.drop_objection(this)
endtask
这样在uvm1.1d和1.2中都能够安全的启动并且控制objection
[1] 安全地启动sequence https://zhuanlan.zhihu.com/p/446791549 (完全摘抄)
Shell中的$0、$1、$2的含义
$0 就是编写的shell脚本本身的名字
$1 是在运行shell脚本传的第一个参数
$2 是在运行shell脚本传的第二个参数
如:新建了一个shell脚本test.sh
#!/bin/sh
echo "shell脚本名称: $0"
echo "传到shell的第一个参数: $1"
echo "传到shell的第二个参数: $2"
保存,为test.sh文件添加可执行权限:chmod +x test.sh 回车
执行test.sh : sh test.sh 5 6
运行结果:
shell脚本名称: test.sh
传到shell的第一个参数: 5
传到shell的第二个参数: 6
$cast 与类型转换
类型转换分为静态类型转换
和**动态类型转换**
静态类型转换
int a = 2;
real b;
b = real'(a); //将a的类型从int转换成real型
静态类型转换一般是不会检查转换是否合法的,因此具有一定的危险性
动态类型转换
parent_class pc;
child_class cc_1,cc_2;
pc=new();
cc_1=new();
cc_2=new(); //new()返回值为地址,get到子类地址
//$cast(cc_2,pc) //在此处做动态类型转换不成功
pc=cc_1; //a-先把把子类cc_1的指针给父类pc(cc_1类型转换成pc类型)
$cast(cc_2,pc) //b-再进行动态类型转换-成功,此时父类pc的指针给cc_2(pc类型转换为cc_2类型)
成功返回0(一般函数返回值都是0),不成功返回1
向下类型转换
-
简单理解:就是父(类)传子(类)。父类站的高,子类在底下,从父类向子类的转换,称为向下类型转换(即 child_handle = father_handle,直接这样复制是不行的)
-
深入理解:把子类句柄传给父类,
子类的指针
要指向父类
(便于理解) -
父传子属于继承,一定需要check
-
$cast 则是用于向下类型转换。
在向下类型转换时,只有当父类指针指向的对象和待转换的类型一致时,cast才会成功。
father f;
child1 c1,c2;
f = c1; //向上类型转换,f指向的对象是类型是child1的子类对象
$cast(c2,f);//cast成功,c2和c1是相同类型,f又指向c1
向上类型转换
- 简单理解:就是子(类)传父(类)。
- 深入理解:
父类的指针
要指向子类
。 - 向上类型转换是安全的
- 子传父,属于孝顺,需要check。
向下类型转换采用$case的必要性
class parent;
int a=1;
int b=2;
int c=3;
endclass:parent
class child extends parent;
int d=4;
endclass:child
module top;
initial begin
parent pc;
child cc_1,cc_2;
pc=new();
cc_1=new();
cc_2=new();
cc_2 = pc; // 子类句柄指向父类(这是错误的);
end
endmodule
class parent 含有a,b,c 三个变量,class child是parent的子类,多一个变量d。如果直接cc_2 = pc;赋值的话,现在cc_2里面是父类的地址pc,如果后边代码中出现cc_2.d的代码,相当于pc.d,父类里面是找不到d变量,这肯定有问题。
应该考虑加上一个check,如果get到的地址(父类地址or父类的其他子类地址),指向的类中含有d变量的话,转换成功。这个check就是$cast()。
module top;
initial begin
parent pc;
child cc_1,cc_2;
pc=new();
cc_1=new();
cc_2=new();
$cast(cc_2,pc) //使用$cast将父类指针传给子类,仿真出错
end
endmodule
/*result:
# ** Error: (vsim-3971) $cast to type 'class work.test_sv_unit::child'from 'class work.
test _sv_unit::base' failed in file test.sv.
*/
此时虽然用了$cast()做check,但是你若调用cc_2.d
相当于cc_2.d ->pc.d
,但是在父类pc中没有d这个变量。这就需要把一个含有d变量的’cc_1’指针传给pc,然后再把父类指针传给’cc_2’
代码修改
module top;
initial begin
parent pc;
child cc_1,cc_2;
pc=new();
cc_1=new();
cc_2=new();
pc=cc_1;
$cast(cc_2,pc) //使用$cast将父类指针传给子类,仿真成功
end
endmodule
应用场景
-
使用$cast的一种场景是将某些child class数据传递到其他同类child class中,并且接收类的开发人员甚至不知道传入的类是哪种子类,因此通常他会在接收类使用parent class来获取传入的数据,然后使用$cast将数据检查/传输到某些接收类中。
-
检查变量越界情况
枚举类型的缺省类型为双状态int,可以使用简单的赋值表达式把枚举类型变量的值直接赋值给非枚举变量 如int。SV不允许在没有进行显示类型转换的情况下把int变量直接赋值给枚举变量。
SV要求显式的类型转换的目的在于让你意识到可能的数据越界情况。
typedef enum bit[1:0] {RED=0,BLUE,GREEN} COLOR_E;
COLOR_E color,c2;
int c;
initial begin
color = BLUE; // 赋值一个已知的合法的值
c = color; // 将枚举变量赋值给int,此时为 1
c = c+1; // int型变量递增
if(!$cast(color,c)) // 将整型显示转换回枚举类型,如果越界会报错
$display("cast failed for c=%0d",c); // c的值此时为2
$display("Color is %0d/%s",color,color.name());
c++; // c的值为3,对于枚举类型已然越界
c2 = COLOR_E'(c); // 不做类型检查,下句c2.name()由于越界而打印不出
$display("c2 is %0d/$s",c2,c2.name()); // 打印:c2 is 3/
end
补充
- 由于多态的存在,父类的句柄可以指向父类,也可以指向子类。
- 对于virtual method,由于SV支持的动态绑定,即支持子类method重载;SV根据对象的类型,而非句柄的类型来决定调用什么method;
- 对于property和非virtual method,SV是根据handle本身的类型进行判断
[1] $cast()的个人理解 https://www.ngui.cc/zz/2311033.html?action=onClick(完全摘抄)
[2] $cast的用法 https://blog.csdn.net/u011177284/article/details/120238025 (摘抄)
ECC调研
ECC基本原理
ECC (Error Correcting Code) 纠错码:在传输过程中发生错误后能在收端自行发现并纠正的码。
- 为使一种码具有检错或纠错能力,须对原码字增加多余的码元,以扩大码字之间的差别 ,即把原码字按某种规则变成有一定冗余度的码字,并使每个码字的码之间有一定的关系。(编码)
- 码字到达收端后,可以根据编码规则是否满足以判定有无错误。(检错)
- 当不能满足时,按一定规则确定错误所在位置并予以纠正。(译码)
- 检错码与其他手段结合使用,可以纠错。
- 基于 ECC 的实际存储,ECC 方案可能有两种类型:side-band ECC 或 inline ECC。在 side-band ECC 中,ECC 数据存储在单独的 DRAM 上;在inline ECC 中,ECC 数据与实际数据一起存储在同一个 DRAM 上。
ECC 生成和校验顺序如下:
- ECC 数据由控制器根据实际的 WR(写入)数据生成。内存同时存储写命令的数据和 ECC 数据。
- 在 RD(读取)操作期间,控制器从内存读取数据和相应的 ECC 数据。控制器利用接收到的数据重新生成 ECC 数据,并将其与接收到的 ECC 数据进行比较。
- 如果两者匹配,则不会发生错误。如果不匹配,ECC SECDED 机制允许控制器纠正任何单 bit 错误并检测双 bit 错误。
汉明码
汉明码是一种常见的ECC,用于检测DDR,Flash等存储过程中出现的错误(如在MCU唤醒时,对于flash可进行ECC检查),它检测物理反转,可以纠错一位错误码,同时能检测1-2位的错误码。
汉明码构造可以分为两块:
校验码(Parity Bit)
数据位(Data Bit)
汉明码实际由校验码与数据位穿插而成,校验码穿插形式如下图所示(举例32bit长度数据):
可以直接看出(暂时忽略第39bit),P1, P2, P4, …等校验位依次代表0b1, 0b10, 0b100, …等特殊二进制数换算得到的位置。而数据位则根据长度依次填入序号3, 5, 7, …等位置。
数据位D0~D31依次表明32-bit数据的每一位,校验位P1, P2, P4, …等则由已填充的数据位计算获取,直观视图如下:
按照规律以校验码为起点,每n位(n为校验码数字)为一组,将数据分为若干组,每隔一组作为该校验码的异或输入
根据这张表提供的规律,可以直接构造任意数据长度的汉明码。其中:
P1 = D[0] ^ D[1] ^ D[3] ^ … ^ D[30]
P2 = D[0] ^ D[2] ^ D[3] ^ … ^ D[31]
P4 = D[1] ^ D[2] ^ D[3] ^ … ^ D[31]
P8 = D[4] ^ D[5] ^ D[6] ^ … ^ D[25]
P16 = D[11] ^ D[12] ^ D[13] ^ … ^ D[25]
P32 = D[26] ^ D[27] ^ D[28] ^ … ^ D[31]
不考虑PP7位的情况下,这种方式构造得到的汉明码一般称为7,4汉明码(即:7 bit汉明码,数据位占4 bit,校验位占3 bit),只能纠正/检查1 bit错误。如果需要检测2 bit错误,可以增加1 bit校验位PP7,该校验位是前述所有数据位与计算所得校验位异或的结果,这种构造方式得到的汉明码一般称为8,4汉明码(即:8 bit汉明码,数据位占4 bit,校验位占4 bit)。同时可以从原理上看出汉明码构造的规律——某种意义上的二分法。
最小ECC bit位数n要求:
2^n>数据位数+ECC位数n
单bit检测/纠错
原始数据:10101,按上表拆分后可以有
P1 | P2 | 1 | P4 | 0 | 1 | 0 | 1 | P8 |
---|
P1 _ck= D[0] ^ D[1] ^ D[3] ^ D[4]=1 ^ 0 ^ 0 ^ 1 = 0
P2 _ck= D[0] ^ D[2] ^ D[3] =1 ^ 1 ^ 0 = 0
P4 _ck= D[1] ^ D[2] ^ D[3] =0 ^ 1 ^ 0 = 1
P8 _ck= D[4] = 1
所以构造得到的汉明码为 0b001101011
假设接收到的汉明码为0b001100011(D2错误),此时接收方并不知道该数据是否正确,所以进行汉明码的校验操作,数据可分割为
P1_ck | P2_ck | D0 | P4_ck | D1 | D2 | D3 | D4 | P8_ck |
---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
此时,按照汉明码的计算方式计算得到P1-P4,如果Pi和Pi_ck异或为0说明两者一样,为1则说明出现错误,那么就可计算如下
P1 = P1 _ck^D[0] ^ D[1] ^ D[3] ^ D[4]=0 ^ 1 ^ 0 ^ 0 ^ 1 = 0
P2 = P2 _ck^D[0] ^ D[2] ^ D[3] =0 ^ 1 ^ 0 ^ 0 = 1
P4 = P4 _ck^D[1] ^ D[2] ^ D[3] =1 ^ 0 ^ 0 ^ 0 = 1
P8 = P8 _ck^D[4] =1 ^ 1 = 0
P8P4P2P1 = 0b0110 = 6,所以是第6 bit数据位发生错误,如果需要纠正该bit,翻转即可获得正确数据:0b0011010111 。
Flash ECC算法
由于Nand Flash的 生产工艺的局限性,一个Nand Flash存储并不能保证其在整个工作周期中性能的稳定,在其出厂或者使用过程中,都有可能产生坏块,从而造成存储数据的错误。
Nand Flash的数据是以bit的方式保存在memory cell(存储单元)中的。通常情况下,一个cell只能存储一个bit,这些cell以8个或者16个为单位,连成bit line,形成byte或者word,这就是Nand Flash的位宽。这些Line再组成Page(页)。然后一定数量的Page组成一个Block(块)。
以三星Flash为例子,一片Nand flash为一个设备(device),1 (Device) = xxxx (Blocks),1 (Block) = xxxx (Pages),1(Page) =528 (Bytes) = 数据块大小(512Bytes) + OOB 块大小(16Bytes,除OOB第六字节外,通常至少把OOB的前3个字节存放Nand Flash硬件ECC码。
Block是Nand Flash中最大的操作单元,擦除操作就是以Block为单位进行的,而读与编程则是以Page为单位进行的,并且每次编程前都需要进行Flash的擦除。
列校验
P1 = D7 ^ D5 ^ D3 ^ D1 (Dx为每一行对应第x位构成的列的数据)
P1 ‘= D6 ^ D4 ^ D2 ^ D0
按图依次类推
每个列校验都是256×4=1024位异或的结果,若结果为1则有奇数个1;列校验共计6位
P8 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ( 偶数行所有位异或结果)
P8’ = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ( 奇数行所有位异或结果)
P16 = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ( 间隔2行所有位异或结果)
P16’ = bit7 ^ bit6 ^ bit5 ^ bit4 ^ bit3 ^ bit2 ^ bit1 ^ bit0 ( 间隔2行所有位异或结果)
按图依次类推
每个行校验都是8×128=1024位异或的结果,若结果为1则有奇数个1;行校验共计16位
nand中常用3个字节存储此22bit结果
ECC | Bit 7 | Bit 6 | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|---|---|---|
Byte 0 | P64 | P64’ | P32 | P32’ | P16 | P16’ | P8 | P8’ |
Byte 1 | P1024 | P1024’ | P512 | P512’ | P256 | P256’ | P128 | P128’ |
Byte 2 | P4 | P4’ | P2 | P2’ | P1 | P1’ | 1 | 1 |
当往NAND Flash的page中写入数据的时候,每256字节生成一个ECC校验和,称之为原ECC校验和,保存到PAGE的OOB(out-of-band)数据区中。
当从NAND Flash中读取数据的时候,每256字节生成一个ECC校验和,称之为新ECC校验和。
校验的时候,根据上述ECC生成原理不难推断:将从OOB区中读出的原ECC校验和新ECC校验和按位异或,若结果为0,则表示不存在错(或是出现了ECC无法检测的错误);若3个字节异或结果中存在11个比特位为1,表示存在一个比特错误,且可纠正;若3个字节异或结果中只存在1个比特位为1,表示OOB区出错;其他情况均表示出现了无法纠正的错误。
ECC纠错
定位出错bit的方式为:先确定行地址(即哪个字节出错),再确定列地址(即该字节哪一bit出错)
将原ECC校验和与新ECC校验和按位异或之后,得到新的“RP15,RP14...RP1,RP0,CP5,CP4...CP1,CP0”校验和结果。抽取RP15,RP13,RP11,RP9,RP7,RP5,RP3,RP1组成新的8bit数据,表示的值就是出错的字节的行地址(范围0255)。抽取CP5,CP3,CP1组成新的3bit数据,表示的值就是出错的字节的列地址(范围07)。
如上图列地址为例:若CP5发生变化(异或后的CP5=1),则出错处肯定在 Bit 4 ~ Bit 7中;若CP5无变化(异或后的CP5=0),则出错处在 Bit 0 ~ Bit 3 中,这样就筛选掉了一半的Bit位。剩下的4个Bit位中,再看CP3是否发生变化,又选出2个Bit位。剩下的2Bit位中再看CP1是否发生变化,则最终可定位1个出错的Bit位。从异或结果中抽取CP5,CP3,CP1位,便可定位出错bit位的列地址。同理行校验抽取异或结果的RP15,RP13,RP11,RP9,RP7,RP5,RP3,RP1位便可定位出哪个Byte出错,再用CP5,CP3,CP1定位哪个Bit出错。
在做ECC测试时与SRAM的奇偶测试类似,首先关掉中断使能, 之后故意使之发生ECC错误, 观察是否发生ECC错误
————————————————
标签:类型转换,Shell,ECC,cast,cc,pc,bit From: https://www.cnblogs.com/telepath-icer/p/18067305[1]DDR 内存中的 ECC (synopsys.com)
[2]ECC校验——汉明码(Hamming Code)_错误检测(ecc) :1个字节。采用hamming code的方式-CSDN博客
[3]https://www.cnblogs.com/chenfulin5/p/6485497.html
[4]白话 flash ECC原理 - 知乎 (zhihu.com)
[5]ECC校验原理以及在Nand Flash中的应用 (bbsmax.com)
[6]FLASH ECC算法-CSDN博客
[7]Verilog实现Nand flash Ecc校验和纠错 - 知乎 (zhihu.com)