语法回顾-《Verilog编程艺术》之表达式

目录

表达式

操作符

操作符优先级

整数

算数操作符

比较操作符

逻辑操作符

位运算操作符

归约操作符

移位操作符

条件操作符

连接操作符

操作数

向量的抽取

数组的访问

字符串

表达式位长

符号表达式

赋值和截断

与x/z比较


参考《Verilog 编程艺术》魏家明著

表达式

表达式是用操作符(operator)把操作数(operand)组合起来,并按照操作符的语义计算出结果。

操作符

操作符优先级

单目操作符优先级最高;

表格中行间的操作符优先级按降序排列

表格中行内的操作符具有相同的优先级

可以用括号调整操作符的顺序。

整数

整数可以在表达式中使用:

An unsized, unbased integer (12)

An unsized, based integer ('d12, 'sd12)

A sized, based integer  (16'd12, 16'sd12)

在表达式中,对于用不同方式表达的整数有不同的解释

1。An unsized, unbased integer 被当作符号数

2。An unsized, based integer 被当作符号数

3。A sized,signed,based integer 被当作符号数

4。An unsized,unsigned,based integer 被当作无符号数

5。A sized,unsigned,based,integer 被当作无符号数

例如:有两种方式执行“minus 12 divided by 3”注意-12和-'d12具有同样的2的补码位。但是-'d12在表达式中失去了作为符号负数的特性。

integer intA;

intA = -12 / 3;          // result is -4

intA = -'d12 / 3;        // result is 1431655761

intA = -'sd12 / 3;       // result is -4

intA = -4'sd12 / 3;      // result is 1;-4'sd12 is the negative of the 4-bit quantity 1100,

 // which is -4, -(-4) = 4。

 // 属于上边第3种情况,属于符号数,1100第一位被当作负号解释,故其值表示(-4)

算数操作符

+,-,*,/,%,**

1.“/”是除法运算,在做整数除时向零方向舍去小数部分

2.“**”是指数运算

3.“%”取模运算,只可用于整数运算,而其他操作符既可用于整数也可用于实数

例如:在生成时钟的时候,必须选择合适的timescale和precision。当时用 PERIOD/2 计算延时时,必须保证小数不会被舍弃,实际上应该使用 PERIOD/2.0

prameter PERIOD = 15;

initial begin clk <= 0; forever #(PERIOD/2) clk = ~clk; end     // Not correct

initial begin clk <= 0; forever #(PERIOD/2.0) clk = ~clk; end   // Correct

算数表达式中的regs和integers

1.赋给reg变量或net线网的值被当作unsigned,除非reg变量或net线网被清晰声明成signed。

2.赋给integer,real或realtime变量的值被当作signed。

3.赋给time变量的值被当作unsigned

4.除了赋给real和realtime变量的值,符号数signed都用2的补码形式表示

5.赋给real和realtime变量的值用浮点数表示

6.signed和unsigned之间的转换保持同样的位bit表示。

比较操作符

<,<=,>,>=,===,!==,==,!=

比较操作符的规则如下:

1.它们的比较结果是0(true)或1(false),但是如果操作数中有x或z,而且比较操作不是===和!==,那么结果就是x。

2.==和!=被称为logical equality and logical inequality  operators,操作数中的x或z会导致结果为x。

3.===和!==被称为case equality and case inequality  operators,操作数中的x或z也会参与比较,所以结果只能为0或1。

4.如果两个操作数中有一个或两个无符号数,那么比较就按照无符号数比较;如果操作数的位长不一样,那么位长小的操作数就需要做零扩展。

5.只有两个操作数都是符号数时,比较才按照符号数比较;如果操作数的位长不一样,那么位长小的操作数就需要先做符号扩展。

6.如果两个操作数中只有一个操作数是实数,那么另一个操作数要先转换成实数,然后再比较。

7.比较操作符比算数操作符的优先级低。

逻辑操作符

&& || !

1.它们的运算结果为0或1,但是当操作数中有x或z时,结果为x

2.优先级次为:!最高,&&次之,||最低

例如:a<size-1 && b!=c && index!=lastone

等价于:(a<size-1) && (b!=c) && (index!=lastone)

位运算操作符

&,|,^,~^,~

归约操作符

归约操作对操作数进行操作然后产生1-bit的结果,包括:&,|,^,~&,~|,~^

 

移位操作符

逻辑移位:<< 和 >>

算数移位:<<< 和 >>>

移位操作规则:

1/左移操作<< 和 <<<左移相应的位数,空位填0

2/右移操作>> 和 >>>右移相应的位数,但是对于空出的位处理不一样:对于逻辑右移,空出的位填0;对于算数右移,且操作数为unsigned,空出的位填0;对于signed,空出的位填符号位。

3/如果右操作数含有x或z,那么结果为x

4/如果使用 >>> 那么结果的正负是由左操作数决定的。

5/右操作数始终被当作无符号数,而且对结果的正负没有影响

条件操作符

(?:)

需要三个操作数

conditional_expression = expr1 ? expr2 : expr3;

规则:

1/如果expr1为真,那么结果时expr2;

2/如果expr1为假,那么结果时expr3;

3/如果expr1为x或z,那么结果时x;

连接操作符

把一个或多个操作数的位连接起来。没有size的常数不能再连接操作中使用,因为连接操作需要每个操作数的size

例子:{a, b[3:0], w, 3'b101};

等价于:{a, b[3], b[2], b[1], b[0], w, 1'b1, 1'b0, 1'b1};

连接操作还支持复制,复制数应该大于等于0;

{4{w}};               // {w, w, w, w};

{b, 3{a, b}};         // {b, a, b, a, b, a, b};

操作数

表达式中的操作数有不同的形式

1.如果直接以瓦纳征的方式使用net,variable或parameter,就是直接使用它们的名字,那么它们中的所有位都将被当作操作数。

2.如果使用vector net,vector reg,integer,time variable或parameter的1-bit数据,那么就用bit-select操作数。

3.如果使用vector net,vector reg,integer,time variable或parameter的multi-bit数据,那么就用part-select操作数。

4.数组元素的bit-select或part-select可以当作操作数使用。

5.连接操作生成的数据(包括嵌套的连接操作)可以当作操作数使用。

6.函数调用可以当作操作数使用

向量的抽取

向量的抽取(bit-select and part-select)

bit-select规则:

1/bit-select从vector net,vector reg,integer,time variable后parameter中抽取1bit指定的数据。

2/位索引可以是一个表达式。

3/如果位索引超出范围,或者位索引是x或z,那么结果是x

4/对scalar,real variable和realtime variable使用bit-select是非法的。

part-select规则:

1/part-select从vector net,vector reg,integer,time variable后parameter中抽取multi-bit指定的数据。

2/对scalar,real variable和realtime variable使用multi-select是非法的。

3/如果part-select完全超出范围,对于read来说,结果为x;对于write来说,没有意义

4/如果part-select部分超出范围,对于read来说,没有超出的位正常返回,超出的位结果为x;对于write来说,没有超出的位受到影响。

5/part-select有两种类型:constant part-select和indexed part-select

constant part-select使用如下的语法:

vect[msb_expr : lsb_expr];    msb_expr 和 lsb_expr必须是常数

indexed part-select使用如下的语法:

reg [15:0] big_vect;
reg [0:15] little_vect;


big_vect[lsb_base_expr +: width_expr];
little_vect[msb_base_expr +: width_expr];
big_vect[msb_base_expr -: width_expr];
little_vect[lsb_base_expr -: width_expr];

使用part-select:

reg [31 : 0] big_vect;
reg [0 : 31] little_vect;
reg [63 : 0] dword;
integer sel;

big_vect[0 +: 8];    // == big_vect[7 : 0]
big_vect[15 -: 8];   // == bit_vect[15 : 8]
little_vect[0 +: 8]; // == little_vect[0 : 7]
little_vect[15 -: 8];// == little_vect[8 : 15]
dword[8*sel +: 8];

数组的访问

reg [7 : 0] twod_array[0 : 255][0 : 255];      // 一个二维数组,每个元素是8位宽的
wire threed_array[0:255][0:255][0:7];          // 三维数组,每个元素是1位宽的

towd_array[15][2];       // access one word
towd_array[15][3][3:0];  // accsee lower 4bits of word
towd_array[15][4][0+:4]; // accsee lower 4bits of word
towd_array[2][4][6];     // access bit 6 of word
threed_array[15][2][0];  // legal
threed_array[15][2][3:0];// illegal

字符串

字符串就是一个由8-bit ASCII 构成的序列,它看起来就像一个单一的数值(single numericvalue)。当字符串变量的长度大于它所容纳实际字符串长的时,在赋值时这个变量的左侧就用0填充,这个赋值操作与其他非字符串的赋值类似。

例子:

module string_test;
    reg [8*14 : 1] stringvar;        // 可以容纳14个字符
    initial begin
    stringvar = "Hello world";
    $dispaly("%s is stored as %h", stringvar, stringvar);
    stringvar = {stringvar, "!!!"};
    $dispaly("%s is stored as %h", stringvar, stringvar);
    end
endmodule

字符串操作:

支持复制,连接和比较。复制通过赋值语句实现,连接通过连接操作符实现,比较通过比较操作符实现。

当在reg vector中操作字符串时,为了保存8-bitASCII序列,reg至少应该有8*n bits,这里n是字符的个数

表达式位长

如果想要在计算表达式时获得一致和谐的结果,那么控制表达式中的位长就很重要。例如,如果两个16-bit的reg变量上做位与操作,那么计算结果就是16-bit。加法操作中,选择16-bit还是17-bit与模型的操作类型有关,也与操作是否要处理进位溢出有关。

reg [15:0] a, b;
reg [15:0] sumA;
reg [16:0] sumB;
sumA = a + b;
sumB = a + b;

表达式位长规则:

1.表达式的位长由表达式中的操作数和表达式所处的上下文决定。

2.自决定表达式就是表达式的位长完全由表达式自己决定,例如用于表示延迟的表达式

3.上下文决定表达式就是表达式的位长既由表达式本身的位长决定,也由这样的事实决定(表达式本身是另一个表达式的一部分)。

4.如果不想让乘法丢失溢出的位,那么就要把结果赋值给一个位长足够大的变量,这样才能够保存运算的最大结果。

符号表达式

系统函数$signed()  $unsigned()用于处理表达式的类型转换

例子:

reg [7:0] regA,regB;
reg signed [7:0] regS;
regA = $unsigned(-4);          // 8'b11111100
regB = $unsigned(-4'sd4);      // 8'b00001100
regS = $signed(4'd1100);       // -4

赋值和截断

赋值时,如果RHS的位长大于LHS的位长,那么直接把多出的位丢弃,以匹配LHS的位长。

对于signed 表达式截断可能会改变结果的符号。

例子:

reg [5:0] a;
reg signed [4:0] b;
initial begin
a = 8'hff;       // a = 6'h3f
b = 8'hff;       // b = 5'h1f
end
reg [0:5] a;
reg signed [0:4] b, c;
initial begin
a = 8'sh8f;      // a = 6'h0f;
b = 8'sh8f;      // b = 5'h0f;
c = -113;        // c = 15;
                 // 1000_1111 = -113,截断 = 'h0f = 15
end

与x/z比较

综合工具总是把对x或z的比较当作false,这种行为不同于仿真器行为,可能会导致仿真和综合的不一致。所以为了防止这样的不一致,在比较时不要使用这些不关心的值(x/z)

例子:case语句就会导致仿真和综合的不一致,因为仿真器会让 2'b1x 匹配 A=2'b11 或 A = 2'b10.但是综合工具把 2'b1x 当作false,对于2'b0x也同样处理。

case (A)
    2'b1x:…
    2'b0x:…
    default:…
endcase

例子:综合工具总是把B赋值给1,同时发出警告,因为 if(A == 1'bx)总是被当作false

module tset(
    input A,
    output reg B
);
    always @(*) begin
        if (A == 1'bx) B = 0;
        eslse          B = 1;
    end
endmodule

  • 31
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值