sv标准研读第十一章-操作符和表达式

发布于:2024-10-08 ⋅ 阅读:(5) ⋅ 点赞:(0)

书接上回:

sv标准研读第一章-综述

sv标准研读第二章-标准引用

sv标准研读第三章-设计和验证的building block

sv标准研读第四章-时间调度机制

sv标准研读第五章-词法

sv标准研读第六章-数据类型

sv标准研读第七章-聚合数据类型

sv标准研读第八章-class

sv标准解读第九章-进程

sv标准研读第十章-赋值语句

                                                                11.Operators and expressions

11.1 General

本章主要内容:

11.2 Overview

expression/operands/operators的关系:expression由operands和operators构成,单个的operand也是一个expression。

操作数可以是以下中的任何一个:

11.2.1 Constant expressions

常量表达式中的操作数可以是:常量数字、字符串、常量参数的bit选择和part选择、常量函数调用(13.4.3)、常量系统函数调用。常量表达式中的操作符可以是下表中的任意。

常量系统函数是一些内建的系统函数,在elaboration阶段运行,这些系统函数的参数是常量表达式,例如转换系统函数:

数学系统函数:

位矢量系统函数:

数据查询系统函数:

后面两个系统函数尽管参数不是常量,但是在某些条件下可以成为常量表达式,具体见这些章节的描述。

11.2.2 Aggregate expressions

聚合表达式里可以使用unpacked 结构体和array数据对象、unpacked结构体和arrary构造函数、unpacked arrary的多元素切片。

聚合表达式可以在赋值、端口、subroutine的参数中进行复制,也可以在等于和不等操作符中进行比较。

11.3 Operators

操作符分为以下几类:

  1. 赋值操作符:= | += | -= | *= | /= | %= | &= | |= | ^= | <<= | >>= | <<<= | >>>=
  2. 条件操作符:?:
  3. 一元操作符:+ | - | ! | ~ | & | ~& | | | ~| | ^ | ~^ | ^~
  4. 二元操作符:+ | - | * | / | % | == | != | === | !== | ==? | !=? | && | || | **| < | <= | > | >= | & | | | ^ | ^~ | ~^ | >> | << | >>> | <<<| -> | <->
  5. 自增自减操作符:++|--
  6. 流操作符:>>|<<
  7. inside
  8. dist
  9. {}|{{}}

11.3.1 Operators with real operands

real操作数可用的操作符有:

Operator token

Name

Result

=

Binary assignment operator

取决于操作数

+= -= /= *=

Binary arithmetic assignment operators

取决于操作数

?:

Conditional operator

取决于操作数

+ -

Unary arithmetic operators

取决于操作数

!

Unary logical negation operator

bit

+ - * / **

Binary arithmetic operators

取决于操作数

&& ||
> <>

Binary logical operators

bit

< <= > >=

Binary relational operators

bit

== !=

Binary logical equality operators

bit

++ --

Unary increment, decrement operators

取决于操作数

inside

Binary set membership operator

bit

11.3.2 Operator precedence

11.3.3 Using integer literals in expressions

表达式中的整数可以是以下几种形式:

unsized/unbased12

unsized/based’d12   ‘sd12

sized/based16’d12   16’sd12

对于负数来说,basedunbased整数的解析是不一样的。unbased负数整数会被解析成带符号的补码,based负数整数的无符号整数部分会被解释成无符号整数。例如:

第一个:-12unbased,就会被认为是有符号的补码,运算时符号位不参与运算,即-12/3=-4

第二个:-‘d12based,就会被认为是无符号值,这里的负号会被解析成数值,即:1111_0100=244/3=81

第三个:-‘sd12based,但是加了s被强制解析为有符号,所以结果和第一个一样

第四个:-4’sd12based,是有符号,且是sizedsized先发挥作用,0000_1100变成了1100,然后signed发挥作用,表示这是-4然后前面还有一个负号,即-(-4)=4,结果为1

解析:结合5.7.1来看好理解一些。简单而言就是只要是'd都是无符号的,‘d6/-'d6/-8'd6都是无符号数。sv标准规定了运算表达式中有无符号数就都按无符号处理,这样就有了-'d12/3=81。想要保留符号就用'sd,单看-'d12-'sd12,它们的二进制码是一样的,但-'sd12/3,两个数都是有符号数,就有了-4

11.3.4 Operations on logic (4-state) and bit (2-state) types

操作符可能应用于2态或者4态数据的计算。操作数只要有一个是4态的,那么结果是4态的,如果操作数都是2态的,那么结果肯定是2态的,但是有一种情形例外,即2态数据操作的结果是x(比如除以0操作)

举例如下:

11.3.5 Operator expression short circuiting

操作符表达式短路指的是在某些情况下,表达式的部分操作已经决定了整个表达式的结果,不需要把所有的操作都计算一遍。会发生表达式短路的操作符包括:

其他操作符不会发生表达式短路的行为。举例:

上面的表达式中,即使regA=0,右边的表达式:regB|myFunc(regc)还是会进行计算。

11.3.6 Assignment within an expression

表达式中的赋值可以是blocking赋值,但前提是它没有时序控制,使用时应该用小括号包起来避免产生常见的错误,比如可以使用a=b来代替a==b,用a|=b来代替a!=b。举例:

这种赋值是这样解析的:计算右边的值,然后将右边的值强制转换为左边的数据类型,以此类推,不断更新左边的值,最终计算的结果是最左侧的数据类型。如果左侧是串联?,则最终返回的数据类型是unsigned整型数据,数据长度是所有操作数长度的和。

event表达式、过程连续赋值表达式、不在过程语句的表达式中,不能出现赋值操作符。

11.4 Operator descriptions

11.4.1 Assignment operators

sv包含的赋值操作符有:

= | += | -= | *= | /= | %= | &= | |= | ^= | <<= | >>= | <<<= | >>>=

赋值操作符可以理解为阻塞赋值。

举例:

a+=2    =>    a = a+2

a-=2    =>    a = a-2

a*=2    =>    a = a*2

a/=2    =>    a = a/2

a%=2    =>    a = a%2

a&=2    =>    a = a&2【按位与且赋值】

a|=2    =>    a = a|2【按位或且赋值】

a^=2    =>    a = a^2【按位异或且赋值】

a<<=2    =>    a = a<<2【逻辑左移且赋值】

a>>=2    =>    a = a>>2【逻辑右移且赋值】

a<<<=2    =>    a = a<<<2【算术左移且赋值】

a>>>=2    =>    a = a>>>2【算术右移且赋值】

逻辑移位和算术移位的区别:

逻辑移位:由于移位腾出的空位总是填0

算术移位:左移填0,右移无符号数填0,有符号数填符号位

11.4.2 Increment and decrement operators

++|--

可以理解为阻塞赋值,作用在实数上时,自增自减为1.0。

有两种形式:i++/++i:

举例:

a = 2

b = 2

i = 1

j = 1

a = i++

b = ++j

最后的结果是?

a=i, i=i+1,所以结果是:a=1,i=2

j=j+1,b=j,所以结果是:j=2,b=2

11.4.3 Arithmetic operators

包括:

整数做除法时,应该向0取整。

做/或者%运算时,如果除数是0,结果都是x。

%取余运算时,如果整除,结果是0;该运算结果的符号位跟随被除数的符号。

**幂运算时,两个操作数任意一个是real时,结果是real;第一个操作数是0,第二个操作数是非正数,或者第一个操作数是负数,第二个操作数不是整数值,那么幂运算的结果不确定(vcs给出的结果是0,并没有报warning如果两个操作数都不是real,结果类型参照11.6.111.8.1小节。如果第一个操作数是0,第二个操作数是负数,结果是’x。如果第一个操作数是0,第二个操作数是0,结果是1。

举例:

一元算术运算符优先级高于二元算术运算符,一元算术运算符如下:

一元+相当于正号,一元-相当于负号。

对于所有的算术运算符,任意操作数bit值为x/z,结果就是x。

11.4.3.1 Arithmetic expressions with unsigned and signed types

net可以是unsigned/signed,变量也可以是unsigned/signed。

byte/shotint/int/integer/longint默认是signed

其他数据类型默认是unsigned。

unsigned net/variable=value,此value被解析为unsigned value

signed net/variable=value,此value被解析为signed value【以补码形式存储,除了当signed variable为real时】

unsigned和signed转换时,位数是不变的。

举例:

11.4.4 Relational operators

关系操作符有:

关系操作符的结果是false,返回0;结果是true,返回1。

关系操作符任意一个操作数为x/z,结果都是1bit的x。

关系操作符任意一个操作数是unsigned,比较时就应该按照unsigned值进行比较。关系操作符两个操作数长度不等时,长度短的操作数要进行0扩充。

只有两个操作数都是signed时,比较时才按照signed值进行比较。关系操作符两个操作数长度不等时,长度短的操作数要进行符号位扩充。

两个操作数只要有一个real,另一个操作数要转化成real进行比较。

关系运算符之间优先级相等,但是关系运算符优先级低于算术运算符。

举例:

11.4.5 Equality operators

相等操作符的优先级低于关系运算符。

上面四个的优先级相等,这四个在比较时是逐bit进行比较,成功返回1,失败返回0。

任意一个操作数是unsigned,比较时就应该按照unsigned值进行比较。两个操作数长度不等时,长度短的操作数要进行0扩充。

只有两个操作数都是signed时,比较时才按照signed值进行比较。两个操作数长度不等时,长度短的操作数要进行符号位扩充。

两个操作数只要有一个real,另一个操作数要转化成real进行比较。

对于==/===:如果任意一个操作数为类句柄或者null,且其中一个操作数与另一个操作数是赋值兼容(?)的,也是合法的;如果任意一个操作数为chandle或者null,也是合法的。此时比较的是类句柄、chandles的值。

对于==/!=:如果操作数中有x/z的bit位,那么关系是不确定的,比较的结果是1bit x。

对于===/!==:比较操作的进行就像在过程语句中一样。且会比较每一个x/z。比较的结果只有两种:0/1。

11.4.6 Wildcard equality operators

通配符相等操作符和相等操作符的优先级一样。

==?/!=?:特点是如果右边的操作数里某些bit有x/z,这些bit会当做通配符处理。但是不对左边的操作符做这样的处理。其余位的比较和==/!=保持一致。

会逐bit进行比较,成功返回1bit 1,失败返回1bit 0,关系不确定返回x。两个操作数位宽不一样时,做法和==/!=保持一致。

对于==/!=:如果操作数某些bit含有x/z,结果可能是x

对于===/!==:会认真比较4态值,结果不会出现x,只能是0/1

对于==?/!=?:如果左边的操作数有x/z,结果可能是x,即使右边操作数对应的bit位是x/z,也不会进行比较。

对于==?:如果比较的操作数是class handle/chandle/null,它的做法和==一致。

11.4.7 Logical operators

包括:逻辑与【&&】逻辑或【||】逻辑隐含【->】逻辑相等【<->】,结果有3种:0/1/x。

优先级:逻辑与【&&】高于逻辑或【||】,但两者都低于关系操作符和equality操作符。逻辑隐含【->】逻辑相等【<->】优先级一样,两者的优先级都低于其他的逻辑运算符和条件操作符。

逻辑隐含【->】的意思:

exp1 -> exp2等同于(!exp1||exp2)

exp1<->exp2等同于((exp1->exp2)&&(exp2->exp1))

一元逻辑操作符(!)的作用是将操作数中的true/非0转化成0,将0/false转化成1,如果值不确定,保持为x(Vcs:!4’b110x的结果是0)

逻辑与【&&】逻辑或【||】会出现短路现象:

第一个操作数总是要进行计算;

对于逻辑与【&&】:第一个操作数为flase,第二个操作数就不会进行计算了;

对于逻辑或【||】:第一个操作数为true,第二个操作数就不会进行计算了。

11.4.8 Bitwise operators

二元bitwise操作符就是对操作数进行位操作,结果是1位,计算规则如下:

11.4.9 Reduction operators

reduction操作符可以理解为一元bitwise操作符,只对一个操作数起作用。它首先会计算第1bit和第2bit,然后会计算前面的结果和下一bit,以此类推。

举例:

11.4.10 Shift operators

逻辑移位操作符:<</>>

算术移位操作符:<<</>>>

<<和<<<空位都用0填充;>>空位用0填充,>>>如果是unsigned数,空位用0填充,如果是signed数,用符号位填充。

对于移位操作符,如果右边的操作数有x/z,结果是unknown;右边的操作数始终会当做unsigned数处理,并且不会对结果的符号产生影响,结果的符号位是由左边的操作数决定的。

11.4.11 Conditional operator

exp1?exp2:exp3

本小节介绍的exp1只是一个简单的表达式,但是sv其实还允许exp1进行模式匹配,可参照12.6小节的描述。

exp1为真,执行exp2,不会看exp3的结果;为flase,执行exp3,不会看exp2的结果;

exp1为x/z,exp2和exp3都会执行,然后会通过equality操作符(11.4.5的==)比较这两个的逻辑结果,如果比较的结果是true,则会返回exp2或者exp3,否则会根据这两个表达式的数据类型返回结果??(实际上,如果exp2exp3不等,那么最后的结果就是逐步比较每bit,每bit的结果参照表11-20)

如果exp2和exp3是整型,当exp1是不确定的结果,且exp2和exp3逻辑对比的结果是不等,那么按照下表逐位合并计算出最终结果:

如果exp2和exp3是real,结果的类型就是real;如果一个是real,另一个是shortreal或者整型,结果是real;如果一个是shortreal,另一个是整型,结果是shortreal。

其他情况下,如果一个是整型,另一个可以隐式转换为整型,结果是整型。

如果一个是class 或者interface class,那么:

  1. 如果两个都是null,结果是null;
  2. 如果一个是null,结果是非null;
  3. 如果第一个兼容于第二个,结果的类型是第二个的类型;
  4. 如果第二个兼容于第一个,结果的类型是第一个的类型;
  5. 如果两个派生自同一个class,结果的类型是最近的公共继承class类型。

上面所有的情况,都要求exp2和exp3是equivalent的。

对于非整型的或者聚合表达式,当exp1不确定且exp2和exp3比对结果不等时:

  1. 对于聚合数组类型,除了关联数组,如果两个表达式包含的元素数量相同,则要逐个比较每一个元素,匹配,则返回该元素,不匹配,则返回该类型的默认值。
  2. 其他数据类型,返回该类型的默认值。

11.4.12 Concatenation operators

{}可连接1个或多个表达式,多个表达式之间用逗号隔开。可用在赋值的左边,可用在赋值的右边。

不允许出现unsized的常数。

{}可以被理解为packed vector.

可以选择{}里的某些bit位,例如{a,b}[3:2],但是这种用法不能用在net的左值,变量的左值,或者赋值语句的左侧等等。举例:

需要注意的是,连接操作符虽然和结构体以及数组长的很像,但是连接操作符只需要用{}即可,而结构体和数组需要’{}才行。

11.4.12.1 Replication operator

在连接操作符里,可以在前面使用重复操作符,重复操作符必须是非负、非x、非z的常量表达式,包裹在大括号{}里面。同时,重复操作符也不能出现赋值表达式的左侧,也不能出现在output/inout端口。例如:

重复操作符的重复次数可以是0,这在参数化代码中很有用。重复次数为0意味着这个操作数的size是0,将被ignored。但是重复次数为0只能出现在:连接操作符里至少有一个操作数是正数。举例:

11.4.12.2 String concatenation

当{}里只要有1个操作数是字符串,其他操作数都会隐式转化为字符串进行字符串连接,字符串连接不允许出现在赋值的左侧。举例:

在字符串连接里仍然可以用重复操作符,表示对字符串进行复制,重复操作符用在字符串时,可以是非常量。举例:

需要注意的是,字符串连接或者重复不会被截断,如果被赋值的对象长度不够,会自动调节被赋值对象的长度来适应字符串。

11.4.13 Set membership operator

a inside b

b是一个用逗号分隔的列表或者表达式。如果b里面有一个表达式是unpacked arrary,那么就会通过降序的方式去遍历它的元素。b里面的值可以重复。举例:

当表达式是非整型的,inside会使用equality操作符(==)去比较。有match,返回1’b1,没有match,返回1’b0。

当表达式是整型的,inside会使用wildcard操作符(==?)去比较,因此,右边的集合里如果有x/z,就不会care。但是左边的表达式里如果有x/z就会care。

如果没有match,但是比较的结果里有x,则返回1’bx。

inside右边的集合里可以出现用中括号[]括起来的指定范围,中间用冒号:来分隔高低边界;其中$可以代指最高值或者最低值。如果冒号左边的值大于右边的值,那么range是空的。举例:

11.4.14 Streaming operators (pack/unpack)

Bit-stream casting很有用的地方在于:当我们只需要做类型转换,而不关注bit stream的顺序时就很有用。

流操作表达式既可以作为赋值表达式的右边,也可以作为赋值表达式的左边,甚至可以作为bit流cast的操作数。

当流操作符放在left hand,可用于reverse操作,如下:

也可以用来unpack bits给不同的变量。

如果packed的数据里有4态数据,那么结果就是一个4态的数据流,否则就是2态数据流。

Bit stream串联语法如下:

streaming_concatenation既可以作为赋值的对象,也可以作为赋值的来源,甚至可以作为bit-stream转换的操作数。

当我们把streaming_concatenation用于表达式里的操作数时,如果之前并没有将其转化成bit-stream类型,那么是会报错的;当我们把streaming_concatenation用于赋值的来源时,那么它赋值的对象应该是一个bit-stream或者streaming_concatenation类型的数据对象。

当我们赋值的对象是bit-stream的数据类型,并且使用streaming_concatenation作为来源给其赋值,那么在一些情况下我们有必要将streaming_concatenation进行扩充以便左对齐到目标数据对象,规则如下:

  1. 如果赋值左边是一个固定size的变量,赋值右边是stream,且左边的位宽小于右边,那么会报错;
  2. 如果赋值左边是一个固定size的变量,赋值右边是stream,且左边的位宽大于右边,那么stream会被拓宽,并在右边填充0。
  3. 如果赋值左边是一个动态size变量,例如队列或者动态数组,那么左边的变量会被resized,让它有最少的成员使其wide>=stream。如果resize后是>的关系,那么stream还是会在右边填0。

在widened之后,stream会被隐式转化为左边目标的类型。

11.4.14.1 Concatenation of stream_expressions

写法:通过逗号隔开,从左到右排列多个流表达式。

最后打包成generic流,遵循以下步骤:

  1. 如果表达式是流串联或者任意bit流类型,那么就会使用bit-stream cast变成packed array,并且是从左到右的顺序进行pack;
  2. 如果表达式是unpacked array(例如队列、动态数组、联合数组、固定大小的unpacked array等),那么就会对里面的每一个元素进行处理,对于关联数组,会按照索引的顺序进行处理,其他的,会按照foreach循环的顺序进行处理;
  3. 如果表达式是一个结构体,那么就会按照声明的顺序依次处理每一个元素;
  4. 如果表达式是untagged union类型,那么会对该union里第一个声明的成员进行处理。
  5. 如果表达式是null class handle,那么不会被streamed,并且给出警告信息;
  6. 如果表达式是非null class handle,那么会依次处理handle多指代对象里的每一个数据成员,而不是handle本身。每一个数据成员会按照声明的顺序依次被streamed。Extended成员将安排在class里成员的后面。如果streaming的是一个对象hierarchy且包含了cycles,那么结果是未知的,会产生error。如果treaming的是class handl,且该class handle里的local/protected成员不能被streaming操作符访问到,那么这个streaming操作是非法的。
  7. 其他:stream失败,会当error处理。

当stream失败,那么该表达式是不会append到stream的后面的,streaming的流程会按照顺序跳到下一个item。实现时,当遇到这种error,就没有必要继续该streaming流程了。

11.4.14.2 Re-ordering of the generic stream

本节描述的内容是在11.4.14.1小节描述的步骤打包成generic bit-stream后将其切块做重排序。

Slice_size:指的是每个block的大小,也就是每个block的bits数量,默认为1,必须是常量整数表达式(<=0会报错)或者是simple type。如果是用一个type指定,那么size应该是这个type的bits数量。

<</>>:该符号决定了bit流的顺序;>>表示的是从左到右的顺序;<<表示从右到左的顺序。>>操作并没有重排序,<<实际上就是进行了reverse,即streaming是从最右开始的。在切片的过程中,最左边block的bits数小于block size,那么最左边block的bits数将等于剩余bits,并没有填充或者截断。举例:

11.4.14.3 Streaming concatenation as an assignment target (unpack)

当将streaming_concatation作为赋值的对象时,流操作符起到的作用就是reverse或者unpack。此时,赋值的来源应该是bit-stream类型。如果来源里的bit数多于needed,那么最左边的bits数应该被cut掉,如果来源里的bit数小于needed,那么会报错。

4态bit流unpack成2态目标时,会被cast到2态类型,反之亦然。Null handle在做pack/unpack操作时会被跳过;因此,unpack操作并不会产生class对象。如果要从流重构特定的对象层次结构,则必须在应用流操作符之前创建要将流unpack到其中的对象层次结构。unpack操作只能修改显式声明的属性;它不会修改隐式声明的属性,如随机模式(见第18章)。举例:

11.4.14.4 Streaming dynamically sized data

如果unpack操作包含无界动态大小的类型,则进程是贪婪的(就像在强制转换中一样):第一个动态大小的项被调整大小以接受流中所有可用的数据(不包括随后的固定大小的项);任何剩余的动态大小项都为空。此机制足以unpack仅包含一个动态大小的数据项的数据包大小的流。但是,当流包含多个可变大小的数据包,或者每个数据包包含多个可变大小的数据项,或者要解压缩的数据的大小存储在流的中间时,这种机制可能会变得繁琐且容易出错。为了克服这些问题,unpack操作允许with表达式显式地指定unpack操作中可变大小字段的范围。with表达式的语法如下:

with构造中的数组范围表达式应为整型,其计算结果应在固定大小数组的边界内,对于动态数组或队列,其计算结果应为正值。with之前的表达式可以是任何一维未打包数组(包括队列)。with中的表达式在其对应的数组流化(即打包或拆包)之前立即求值。因此,表达式可以引用由相同操作符unpack但位于数组之前的数据。如果表达式引用的变量是在相应数组之后unpack的(在数组的右边),则使用变量的先前值对表达式进行求值。

当在unpack操作的上下文中使用并且数组是可变大小的数组时,它应该调整大小以适应范围表达式。如果数组是固定大小的数组,并且范围表达式的计算结果为数组范围之外的范围,则仅unpack位于数组内的范围并生成错误。如果范围表达式的计算结果小于数组的范围(固定大小或可变大小),则只有指定的项被unpack到指定的数组位置;数组的其余部分未被修改。

当在包的上下文中使用时(在右侧),它的行为与数组切片相同。如果范围表达式的计算结果是一个范围,则只有指定的数组项被流式传输。如果range表达式的取值范围大于数组大小的范围,则整个数组被流式传输,并且使用给定数组中不存在的数组条目值(如7.4.6中的表7-1所述)生成剩余的项。举例:

上面pack操作还可以写成:

上面4种写法的结果都是一样的。

11.5 Operands

可以在表达式中指定几种类型的操作数。最简单的类型是对net、变量或参数的完整引用;也就是说,只给出了net、变量或参数的名称。在这种情况下,组成net、变量或参数值的所有位都应用作操作数。

如果需要vector net、vector变量、packed数组、packed结构体或参数的单个比特,则应使用选位操作数。部分选择操作数应用于引用vector net、vector变量、packed数组、packed结构体或参数中的一组相邻位。

Unpacked array元素可以作为操作数引用。

其他操作数的连接(包括嵌套连接)可以指定为操作数。

Function也可以是操作数。

11.5.1 Vector bit-select and part-select addressing

位选择从向量、填充数组、填充结构、参数或连接中提取特定的位。该位可以使用表达式来寻址,该表达式应在自确定的上下文中求值。如果bitselect地址无效(越界或有一个或多个x或z位),则引用返回的值对于4状态值应为x,对于2状态值为0。标量、实变量或实参数的位选择或部分选择是非法的。

可以对几个连续的位进行寻址,称为部分选择。有两种类型的部分选择:非索引部分选择和索引部分选择。给出一个非索引部分选择,语法如下:

msb_expr和lsb_expr都是常量整型表达式。这些表达式中的每一个都应在自行确定的上下文中进行评估。第一个表达式应比第二个表达式处理更重要的位。

索引部分选择的语法如下:

msb_base_expr和lsb_base_expr为整型表达式,width_expr为正常量整型表达式。这些表达式中的每一个都应在自行确定的上下文中进行评估。lsb_base_expr和msb_base_expr可以在运行时变化。前两个示例从基数开始选择位,并按位范围升序排列。选择的比特数等于宽度表达式。第二个两个示例选择从基数开始并按位范围递减的位。

常数位选择是指位置为常数的位选择。常量部分选择是位置和宽度都是常量的部分选择。部分选择的宽度总是恒定的。因此,非索引部分选择始终是常量部分选择,如果索引部分选择的基数和宽度都是常量,则它是常量部分选择。

如果部分选择的地址范围完全超出了向量、打包数组、打包结构、参数或串联的地址边界,或者部分选择的地址范围为x或z,则在读取时产生的值为x,并且在写入时对存储的数据没有影响。部分超出范围的部分选择在读取时将返回超出范围的位的x,并且在写入时将只影响在范围内的位。

举例:

下面的示例指定了由操作数索引寻址的向量acc的单个位:

地址访问的实际位部分由acc的声明决定。例如,在下面的例子中,每个acc的声明都会导致index的特定值访问不同的位:

下一个示例及其后面的项目说明了位寻址的原则。代码声明了一个名为vect的8位变量,并将其初始化为值4。该列表描述了如何对该向量的各个位进行寻址。

如果地址是2,返回1;

如果地址越界,返回x;

如果地址是0/1/3-7,返回0

如果地址是x,返回x;

如果地址是z,返回x;

如果地址中任何1bit包含x/z,返回x。

11.5.2 Array and memory addressing

数组和内存的声明(一维的reg、逻辑或位数组)将在7.4中讨论。本小节讨论数组寻址。

下面的例子声明了1024个8位字的内存:

内存地址的语法应该由内存的名称和地址的表达式组成,用以下格式指定:

addr_expr可以是任意整数表达式;因此,可以在单个表达式中指定内存间接。下一个例子说明了内存间接:

在本例中,mem_name[3]指向内存中名为mem_name的单词3。第3个字处的值是内存地址mem_name[mem_name[3]]使用的到mem_name的索引。与位选择一样,内存声明中给出的地址边界决定了地址表达式的效果。如果地址无效(越界或有一个或多个x或z位),则引用的值应如7.4.6中所述。

下一个例子声明了一个包含256 × 256 8位元素的数组和一个包含256 × 256 × 8 1位元素的数组:

访问数组的语法应该包括内存或数组的名称和每个寻址维度的整数表达式:

和前面一样,addr_expr可以是任何整数表达式。数组twod_array访问整个8位向量,而数组threed_array访问三维数组的单个位。

为了表示数组元素的位选择或部分选择,首先要通过为每个维度提供地址来选择所需的单词。一旦被选中,位选择和部分选择应以与净位选择和可变位选择和部分选择相同的方式处理(见11.5.1)。举例:

11.5.3 Longest static prefix

非正式地说,选择的最长静态前缀是分析工具在精化之后已知值的选择的最长部分。这个概念用于描述隐式灵敏度列表(见9.2.2.2)和描述逻辑端口驱动程序的错误条件(见6.5)。此子句的其余部分定义了select的“最长静态前缀”的构成。

字段选择被定义为一个分层名称,其中最后一个“.”层次分隔符标识类型为结构体或联合声明的变量的字段。字段选择前缀被定义为最后一个“.”的左侧。"字段选择中的层次分隔符。

索引选择是单个索引操作。索引选择前缀是一个标识符,如果是多维选择,则是另一个索引选择。数组选择、位选择、部分选择和索引部分选择都是索引选择的例子。

静态前缀的定义是递归的,定义如下:

—标识符是静态前缀。

—对对象的层次引用是静态前缀。

—对net或变量的包引用是静态前缀。

—如果字段选择前缀为静态前缀,则该字段选择前缀为静态前缀。

—如果索引选择前缀为静态前缀,且选择表达式为常量表达式,则索引选择为静态前缀。

最长静态前缀的定义如下:

不是静态前缀表达式的字段选择前缀或索引选择前缀的标识符。

非静态前缀表达式的字段选择前缀或索引选择前缀的字段选择。

非字段选择前缀的索引选择或静态前缀的表达式的索引选择前缀。

举例:

11.6 Expression bit lengths

表达式的位数由操作数和上下文决定。强制类型转换可用于设置中间值的大小上下文(参见6.24)。

如果要获得一致的结果,控制表达式求值中使用的位的数量是很重要的。有些情况有一个简单的解决方案;例如,如果对两个16位变量指定按位与操作,则结果是一个16位值。然而,在某些情况下,使用多少位来求值表达式或结果应该是多大的并不明显。

例如,两个16位值的算术相加应该使用16位执行求值,还是应该使用17位执行求值以允许可能的进位溢出?答案取决于正在建模的设备的类型以及该设备是否处理携带溢出。

SystemVerilog使用操作数的位长度来确定在计算表达式时要使用多少位。位长度规则在11.6.1中给出。在加法运算符的情况下,应使用最大操作数的位长度,包括赋值操作的左侧。

11.6.1 Rules for expression bit lengths

已经制定了控制表达式位长度的规则,以便大多数实际情况都有一个自然的解决方案。

表达式的位数(称为表达式的大小)应由表达式中涉及的操作数和给出表达式的上下文决定。

自确定表达式是这样一种表达式,它的位长度是由表达式本身确定的,例如,表示延迟值的表达式。

上下文决定的表达式是这样一种表达式,其中表达式的位长度由表达式的位长度和它是另一个表达式的一部分这一事实决定。例如,赋值的右侧表达式的位大小取决于其本身和左侧的大小。

表11-21显示了表达式的形式如何决定表达式的字节长度,表达式的字节长度是多少,表达式的字节长度是多少。表11-21中,i、j、k分别表示操作数的表达式,L(i)表示i所代表的操作数的位长度。

通过将结果赋值给足够宽的空间来容纳它,可以在不丢失任何溢出位的情况下执行乘法运算。

11.6.2 Example of expression bit-length problem

在表达式求值期间,中间结果应取最大操作数的大小(在赋值的情况下,也包括左侧)。在表达式求值期间,必须注意防止有效位的丢失。下面的示例描述了操作数的位长度如何导致有效位的丢失。

鉴于下列声明:

目的是求表达式的值:

其中a和b要相加,这可能导致溢出,然后右移1位以保留16位答案中的进位。

然而,出现了一个问题,因为表达式中的所有操作数都是16位宽度。因此,表达式(a + b)产生一个只有16位宽的中间结果,从而在求值执行1位右移操作之前失去进位。

解决方案是使用至少17位强制表达式(a + b)求值。例如,向表达式中添加一个整数值0将导致使用整数的位大小执行计算。

下面的例子将产生预期的结果:

举例:

打印结果:

就其本身而言,表达式a&b的位长度为4,但由于它是在使用最大位长度的条件表达式的上下文中,因此表达式a&b的实际长度为5,即d的长度。

11.6.3 Example of self-determined expressions

11.7 Signed expressions

如果要获得一致的结果,控制表达式的符号是很重要的。11.8.1概述确定表达式是有符号还是无符号的规则。

强制转换操作符可用于更改表达式的符号或类型(参见6.24.1)。除了强制转换操作符之外,$signed和$unsigned系统函数也可用于强制转换表达式的签名。这些函数对输入表达式求值,并返回与输入表达式和函数定义的签名具有相同位数和值的一维打包数组。

举例:

11.8 Expression evaluation rules

11.8.1 Rules for expression types

确定表达式结果类型的规则如下:

—表达式类型仅取决于操作数。它不依赖于左边(如果有的话)。

—十进制数字有符号。

-基于的数字是无符号的,除非在基数说明符中使用了s表示法(如4的sd12)。

—无论操作数是什么,位选择结果都是无符号的。

—部分选择的结果是无符号的,与操作数无关,即使部分选择指定了整个向量。

—无论操作数是什么,连接结果都是无符号的。

-无论操作数是什么,比较和约简操作符的结果都是无符号的。

—通过类型强制转换为整数的实数有符号。

—任何自定义操作数的符号和大小由操作数本身决定,与表达式的剩余部分无关。

—对于非自定操作数,适用以下规则:

•如果任意操作数为实数,则结果为实数。

•如果任何操作数为无符号数,则结果为无符号数,与操作符无关。

•如果所有的操作数都是有符号的,那么结果都是有符号的,不管操作符是什么,除非另有说明。

11.8.2 Steps for evaluating an expression

计算表达式的步骤如下:

—根据表达式大小确定的标准规则,确定表达式大小。

—使用11.8.1中的规则确定表达式的符号。-将表达式(或自定义子表达式)的类型和大小传播回表达式的上下文确定的操作数。一般来说,操作符的任何上下文决定的操作数都应与该操作符的结果具有相同的类型和大小。

•如果操作符的结果类型是实数,并且它有一个上下文决定的非实数操作数,则该操作数应被视为自决定的,然后在应用操作符之前转换为实数。

•关系和相等操作符的操作数既不是完全自决定的,也不是完全上下文决定的。操作数应该相互影响,就像它们是上下文决定的操作数一样,由它们决定结果类型和大小(两个操作数大小中的最大值)。但是,实际的结果类型总是1位无符号。操作数的类型和大小应该独立于表达式的其余部分,反之亦然。

-当传播到达11.5中定义的简单操作数时,该操作数应转换为传播的类型和大小。如果要扩展操作数,那么只有在传播的类型是有符号的情况下,它才应该是符号扩展的。

11.8.3 Steps for evaluating an assignment

计算赋值的步骤如下:—根据标准赋值大小确定规则(见11.6)确定右侧的大小。-如果需要,扩展右手边的大小,当且仅当右手边的类型有符号时执行符号扩展。

11.8.4 Handling X and Z in signed expressions

如果签名操作数的大小更大宽度和签署的价值符号位是x,结果值应当bit-filled x。如果z值的符号位,然后得到的值应当与z bit-filled。如果任何签名值x和z,那么任何逻辑操作涉及价值结果在整个合成值x和类型与表达式的类型一致。

11.9 Tagged union expressions and member access

标记联合表达式(打包或未打包)的表示方式是:使用标记的联合成员标识符,后跟标记的联合成员标识符,后跟表示相应成员值的表达式。对于void成员,省略成员值表达式。

举例:

在下列带标签的联合表达式中,大括号中的表达式是结构赋值模式(见10.9.2)。

标记联合表达式的类型应该从它的上下文中知道(例如,它被用在对已知类型的变量赋值的右侧,或者它有强制类型转换,或者它被用在另一个已知其类型的表达式中)。表达式的计算结果为该类型的标记联合值。带标签的联合表达式可以完全静态地进行类型检查;带标签的关键字后只允许使用表达式类型的成员名,并且成员表达式必须具有相应的成员类型。

标记联合类型的未初始化变量应为未定义。这包括标签位。如果成员值表达式是成员类型的合法初始化式,则标记联合类型的变量可以用标记联合表达式初始化。

可以使用通常的点表示法读取或分配带标记的联合的成员。这种访问是完全类型检查的,即读取或分配的值必须与当前标签一致。通常,这可能需要运行时检查。尝试读取或赋值类型与标记不一致的值将导致运行时错误。

只有当指令变量i1当前带有Add标签时,以下示例才合法:

11.10 String literal expressions 

本小节讨论了对字符串字面值(见5.9)和存储在位向量和其他打包类型中的字符串字面值的操作。SystemVerilog也有字符串变量,它们存储字符串的方式不同于存储向量。字符串数据类型有几个特殊的内置方法来操作字符串。有关string数据类型和相关方法的讨论,请参见6.16。

字符串字面值操作数应被视为由8位ASCII码序列组成的常量,每个字符一个。任何SystemVerilog操作符都可以操作字符串文字操作数。操作符的行为应该像整个字符串是单个数值一样。当vector的值大于所赋值的字符串字面量所需的值时,赋值后的内容在左侧用零填充。这与在赋值非字符串无符号值时发生的填充一致。下面的示例声明了一个足够大的vector变量,可以容纳14个字符,并为其赋值。然后,该示例使用连接操作符操作存储的值。

模拟上述描述的结果如下:

11.10.1 String literal operations

SystemVerilog操作符支持对字符串字面值和存储在vector中的字符串字面值进行常见的字符串操作复制、连接和比较。拷贝由简单赋值提供。连接由连接操作符提供。比较由相等运算符提供。当在vector中操作字符串文字值时,为了保留8位ASCII码,vector应该至少为8*n位(其中n是ASCII字符的数量)。

11.10.2 String literal value padding and potential problems

当将字符串字面值赋值给vector时,存储的值应在左侧用零填充。填充会影响比较和连接操作的结果。比较和连接操作符不能区分由填充产生的零和原始字符串字符(\0,ASCII NUL)。下面的例子说明了潜在的问题:

这个例子中的比较失败,因为在赋值过程中,变量s1和s2被填充,如下面的例子所示:

s1和s2的连接包括零填充,产生以下值:

因为字符串字面量“Hello world!”,则比较失败,如下例所示:

这个比较的结果为0,表示false。

11.10.3 Empty string literal handling

空字符串字面值("")应被视为等同于ASCII NUL("\0"),其值为零(0),与字符串"0"不同。

11.11 Minimum, typical, and maximum delay expressions

SystemVerilog延迟表达式可以指定为三个用冒号分隔、用圆括号括起来的表达式。这是用来表示最小值、典型值和最大值的顺序。语法请参见语法11-7。

SystemVerilog模型通常为延迟表达式指定三个值。这三个值允许用最小、典型或最大延迟值测试设计,称为min: type:max表达式。以min: type:max格式表示的值可以用于表达式中。min: type:max格式可以用在任何表达式出现的地方。

例子1:这个例子展示了一个表达式,它定义了一个延迟值的三元组。最小值是a+d的和;典型值为b+e;最大值为c+f,如下所示:

例子2:下一个示例显示了一个用于指定min: type:max格式值的典型表达式:

11.12 Let construct

let声明定义了一个模板表达式(let主体),由其端口自定义。let结构可以在其他表达式中实例化。Let声明可用于自定义,在许多情况下可替换文本宏。let结构更安全,因为它具有局部作用域,而编译器指令的作用域在编译单元内是全局的。在包中包含let声明(参见第26条)是实现设计代码结构良好的自定义的自然方法。

例子1:

例子2:

就像属性和序列可以作为并发断言的模板一样(参见16.5),let构造也可以用于即时断言。例如:

let的另一个预期用途是为标识符或子表达式提供快捷方式。例如:

形式参数可以可选地键入,也可以具有可选的默认值。如果let的形式参数是类型化的,则类型应为event或16.6中允许的类型之一。以下规则适用于类型化形式实参及其对应的实际实参,包括在let中声明的默认实际实参:1)如果形式实参是event类型,则实际实参应该是event_expression,并且对形式实参的每个引用都应该位于可以写入event_expression的地方。2)否则,实际实参的自定结果类型必须与形式实参的类型兼容(见6.22.4)。在重写算法中替换对形式参数的引用之前,实际参数应被强制转换为形式参数的类型(参见F.4)。在let中使用的变量,如果不是let的正式参数,则根据声明let所在范围的作用域规则进行解析。在声明范围内,let函数体在使用之前必须定义。不允许对let声明进行分层引用。let主体通过用实际参数替换形式参数得到了实际参数的扩展。执行语义检查以验证带有实际参数的扩展let体是否合法。替换的结果被括在圆括号(…)中,以保持let函数体求值的优先级。不允许递归的let实例化。let函数体可以包含抽样值函数调用(参见16.9.3和16.9.4)。如果没有显式指定它们的时钟,则会在实例化上下文中以与在实例化上下文中直接使用函数相同的方式进行推断。如果需要时钟,但不能在实例化上下文中推断,则会出现错误。

Let可以声明在:

举例1:带参数和不带参数的let

展开let表达式后的有效代码:

举例2:let的参数声明上下文绑定

展开let表达式后的有效代码:

举例3:sequence或者property里声明let

展开let表达式后的有效代码:

举例4:在generate里声明let

语句s1变成了两个语句L0[0]. l1。[2]。S1,第一个是

第二个是:

语句s2是非法的,因为它分层引用let表达式,而分层引用let表达式是不允许的。

举例5:let里带有typed参数

在本例中,let表达式ones_match检查两个参数是否在相同位置将位设置为1。由于在let声明中明确规定形式参数为2状态类型位,因此所有具有未知逻辑值或高阻抗值的参数位都变为0,因此比较捕获设置为1的位的匹配。let表达式same测试其操作数的大小写是否相等(见11.4.6)。当在属性toggles中实例化时,其实际参数将为bit类型。展开let表达式后的有效代码:

举例6:let里带有采样值函数

展开let表达式后的有效代码:


网站公告

今日签到

点亮在社区的每一天
去签到