Verilog HDL入门(1)

Verilog 基础语言1

1.第一章内容

1
2
3
4
5
6
7
8
9
10
11
12
13
编译器指令:
`define ,`undef
指令与C类似,可以在多个文件中使用,但是
`ifdef ,`ifndef,`else,`elsif,`endif

`default_nettype
该指令用于为隐式线网指定线网类型,就是为那些没有被声明类型的线网定义类型
`default_nettype wand

`include
类似于C语言,在代码红肿包含任何其他文件的内容。包含的文件,可以使用相对路径和绝对路径
`include "../../primitives.v"
编译时,这一行由文件 "../../primitives.v"的内容替换

``define`使用

与C语言使用方式有所不同

1
2
3
4
5
6
`define DATA_0	8'D0

//使用时候,
if(`DATA_0 == 8'D0) begin

end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
`resetall
将所有的编译指令重新设置为缺省值

`timescale
这个是用在测试文件中的,该值是设置时间单位和时间精度
`timescale 1ns/100ps 这里时间延时是1ns 精度是100ps
测试文件使用方法 #100 即延时100纳秒

`unconnected_drive , `nounconnected_drive

`unconnected_drive pull1
/* 位于这两个编译器指令之间的所有未连接的输入端口 设置上拉(既连接到1)关键字(pull1) */
`nounconnected_drive

`unconnected_drive pull0
/* 位于这两个编译器指令之间的所有未连接的输入端口 设置下拉(既连接到0)关键字(pull0) */
`nounconnected_drive

`celldefine,`endcelldefine (未使用过)
编译器指令用于把模块标记为单元(Cell)模块。它们一般包含模块的定义

`line

整型数
两种格式:简单十进制 基数格式
基数格式 指定位数 指定进制 数值
例: 5'D25

实数(浮点数) 会采用四舍五入的方法隐式的转换成整数

字符串 字符串不能分多行书写,其他和C语言相同
"INTER REACHED->HERE"
存储字符串
reg [1:8*4] message = "abcd";


数据类型 : 线网类型 变量类型
线网类型 实际的物理连线 线网类型没有被驱动,那么就处于高阻态
变量类型 与C语言类似

线网数据类型
wire trior trireg
tri wand tri1
wor triand tri0
supply0 supply1

注释:有tri的都是三态,既 0 1 x
声明线网的简单语法
net_kind [signed] [[msb:lsb]] net1, net2... netN;

net_kind: 是一种线网数据类型
signed:是一个关键字,声明具有符号值的线网 ;在声明线网类型时,缺省该值,线网时没有符号值的
msb、lsb:是用于指定线网位宽,当没有指定时,位宽为1

wire signed [7:0] pwdata; 线网类型 8根有符号 其数的形式为2的补码
wire [7:0] prdata; 线网类型 无符号 其数的形式为原码

wiretri (语法语义完全相同)
用于连接电路原件;tri类型线网可用于描述多个信号源驱动的线网;
wire类型(或者tri类型)的线网由多个源驱动,则线网的有效值如下表
wire (tri) 0 1 x z
0 0 x x 0
1 x 1 x 1
x x x x x
z 0 z x z
1
2
3
4
5
6
7
8
9
10
例如:
wire [2:0] mode_endable,clk_enable,clk_mode; 假设 clk_enable 的值 01x; clk_mode的值 11z
assign mode_endable = clk_enable & clk_mode; => 01x
assign mode_endable = clk_enable | clk_mode; => 11x
则 mode_endable 的有效值时 x1x


wor和trior线网 与或逻辑相似
线或,线或(wor)和三态线或(trior)在语法和功能上一致
若多个驱动源驱动该线网,则线网的有效值由下表决定
wor (trior) 0 1 x z
0 0 1 x 0
1 1 1 1 1
x x 1 x x
z 0 1 x z
1
2
3
wand和triand线网 与与逻辑相似
线与(wand)和三态线与(triand) 在语法和功能上一致
若多个驱动源驱动该线网,则线网的有效值由下表决定
wand (triand) 0 1 x z
0 0 0 0 0
1 0 1 x 1
x 0 x x x
z 0 1 x z
1
2
3
4
5
6
7
trireg 线网		{建模只能用于测试平台}
该类线网类型能储存数值(类似于变量)(可用于电容节点的建模(没理解))
当三态变量的驱动源都属于z时,三态变量类型的线网保存该线网上的最后一个值;三态变量线网的缺省初始化为x(未知)

tri0和tri1线网
可以用于线逻辑线网的建模,可以多个源驱动线网,tri0(tri1)特征无驱动则其值为0(1)
若多个驱动源驱动该线网,tri0或tri1类型线网的有效值
tri0 (tri1) 0 1 x z
0 0 x x 0
1 x 1 x 1
x x x x x
z 0 1 x 0(1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
supply0和supply1线网
supply0线网用于对地建模,低电平;supply1线网用于对电源线网建模,高电平

未声明的线网

向量线网和标量线网
定义向量线网时可选用关键词 scalared(标量) 或 vectored(向量)
vectored定义线网,不允许选择该向量的某位或部分位赋值,必须整体赋值;若没有指定关键字,则缺省值是 scalared


变量类型
reg integer time
real realtime

reg变量
reg [signed][msb:lsb] reg1;
msb:lsb 指定了范围,且均是常数表达值,范围是可选的;若没有定义范围,则缺省值是1位的reg变量
reg变量的值通常被解释无符号数;除非用关键字signed,则是有符号和C语言类似
未初始化的reg变量的缺省值为x

存储器(memories)
由reg变量组成的数组,如下形式声明
reg [msb:lsb] memory1[upper1:lower1];
举例
reg[0:3]ebi_mem[0:63]; /* ebi_mem 由64个4位reg变量组成的数组 */

将一个存储器内容复制到另一个存储器,可用循环语句每次复制一条
举例:
parameter WORD_LENTH = 8, NUM_WORDS = 64;
reg [WORD_LENTH-1:0]
mem_a[NUM_WORDS-1:0],
mem_b[NUM_WORDS-1:0];
integer i;
for(i = 0 ;i < NUM_WORDS; i = i + 1) begin
mem_a[i] = mem_b[i];
end

整数型(integer)变量 {建模使用,只能用在测试文件中}
可以作普通变量使用 一个整形数至少32位,可以存储有符号


时间变量(time)
用于存储时间和处理时间值;
time time_idn[msb:lsb];
若定义位宽,为指定位宽,未定义位宽,则每个标识存储至少64位。
时间变量只能存储无符号值。
[msb:lsb]是指数组

实型(real)和实型时间(realtime)变量
这两个定义变量完全相同
real swing_margin;
realtime curr_time_in_real;
实型(real)变量缺省值0,声明中不允许对位宽或字界做任何指定。
若将x、z赋予实型变量,则这些值将被当作0处理



数组

reg与tri的不同点


参数(parameter)
是常量,通常用于指定延时和变量的位宽,用参数声明语句,只能对参数赋一次值,
参数值可以在编译时被改变,参数值的改变可以使用 defparam(既重新定义参数)语句实现;
参数声明可以选择指定一种类型,有(integer real realtime time),不允许参数指定范围和使用 signed关键字

parameter 与 define 不同之处 (作用范围不同)
参数是局部的,只在其定义的模块内部起作用;
而宏定义对同时编译的多个文件起作用,既使在某一模块内指定的宏定义,在编译过程中仍旧对多个文件起作用,直到遇到重新定义为止。
建议:`define严格用于全局变量和全局文本替换;而将参数用于某个模块内部所需要的常数


局部参数(localparam) (模块内部参数)
在模块例化(引用)时不能通过 参数传递或重新定义参数值(defparam)语句对局部参数进行修改;不同于parameter;
局部参数与普通参数声明语句完全相同。
若局部参数是用其他非局部参数定义的,则外部赋值使得参数值发生变化时,局部参数值间接地发生改变,例:

parameter BYTE = 8; //(非局部参数)
localparam NIBBLE = 2 * BYTE;//局部参数

在编译期间,参数BYTE发生改变,则局部参数NIBBLE也将随之发生改变




表达式

操作数可以是以下类型
常数 参数
线网 变量
位选(Bit-select) 部分位选(Part-select)
存储器和数组元素 函数调用


常数
12 有符号数
5'b01100 无符号数

-44和-6'o54 存储形式相同,但是处理方式不同,例如:
reg1 = -44/4 => -11 //作为有符号数处理
reg2 = -6'o54/4 => 1073741813 //作为无符号数处理 即当作32位数据处理

线网
标量现网(1位) 向量线网(多位)
assign gpio_prot = -3; //gpio_prot被赋予向量 1011 ,即十进制数13
assign gpio_prot = 4'hA;//gpio_prot被赋予向量 1010 ,即十进制数A

变量 (标量变量 向量变量)
整型变量的值被解释为有符号二进制数;
若reg声明语句中有signed,则变量中的值被解释为有符号数;否则无符号数

位选(Bit-select)(单个位就是位选,多个位就是部分位选)
从一个向量中抽取特定的位,语法
net_or_reg_vextor[bit_select_expr]
表达式中如何进行位选
mis_state[1] && mis_state[4] //变量的位选
gpio_prot[0] | intr //线网位选
LIMIT[5] //参数位选

部分位选(Part-select)
在向量中选取相邻的若干位,部分位选语法格式:
net_or_reg_vextor[msb_const_expr:lsb_const_expr]
msb_const_expr:lsb_const_expr:范围必须是常数表达式

用索引的部分位选的格式
net_or_reg_vextor[base_expr+:const_width_expr]
net_or_reg_vextor[base_expr+:const_width_expr]

base_expr不必是常数, 但宽度必须是常熟
位选的范围是由基表达式加上或减去宽度(const_width_expr)所表示的位的个数。
+:表示部分位选以基表达式作为起点增加若干位。
-:表示部分位选以基表达式作为起点减小若干位

例:
reg [0:15]mark;
reg [0:15]inst_code;
wire [0:31]gpio_data;

inst_code[mark+,2] //选择mark、mark+1位
gpio_data[mark-:4] //选择mark、mark-1、mark-2、mark-3位
若索引越界或计算时遇到x或z,则值为x

存储器和数组元素
格式: memory[word_address]
存储器和数组元素 部分位选 或 位选是允许的。
hdlc_ram[20][2] //第20个元素第2位的值 第2位就是位选
hdlc_ram[20][2:4] //第20个元素部分位选在[2:4]位范围内
hdlc_ram[0:2] //非法
按照格式,在数组中,下标在前,位选 在后

符号
表达式中所有操作数都是有符号,则结果也是有符号,否则无符号数
8'd2+8sb0101 //结果无符号,因为8'd2是一个无符号
4'sb0110-4'sd1 //结果有符号,所有操作数都是有符号

操作符 (Verilog HDL中操作符有9类)
算术操作符 关系操作符
相等操作符 逻辑操作符
按位操作符 缩减操作符
移位操作符 条件操作符
拼接和复制操作
操作符 名称
+ 一元加(正
- 一元减(负
! 一元逻辑非
~ 一元按位取反
& 缩减与
~& 缩减与非
^ 缩减异或
^~或~^ 缩减同或
| 缩减或
~| 缩减或非
** 指数幂
*
/
% 求模
+ 二元加
- 二元减
<<< 算术左移
>>> 算术右移
<< 逻辑左移
>> 逻辑右移
< 小于
<= 小于等于
> 大于
>= 大于等于
== 逻辑相等
!= 逻辑不等
=== 全等
!== 非全等
& 按位与
^ 按位异或
^^ 按位同或
| 按位或
&& 逻辑与
|| 逻辑或
?: 条件判断
{} 拼接
{{}} 重复
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
条件操作符是从右向左处理,其他都是从左至有处理
a?b:c?d:f == > a?b:(c?d:f)

算术操作符(其余操作与C语言类似,除去幂运算)
+(一元加和二元加) -(一元减和二元减)
*(乘) /(除)
%(取模) **(幂运算)

整数除法截断任何小数部分,例如:7/4的运算结果为1


**是求幂运算符:a**b表示a的b次方,即a表示底数,b表示指数;
%是取模运算符:a % b 按照a 和 b中的长度长的补齐。
除法只能取整数;

算数操作符中任意操作数中只要有一位为x 或 z ,则整个结果为x 。例如:4’b10x1+5'b01111 结果为不确定数 5'bxxxxx

算术运算结果的位宽
算术运算结果的位宽由最大操作数的位宽决定。赋值语句下,算术运算结果的位宽也由赋值等号左边目标变量的位宽决定
reg [0B:3] mask_intr,test_ctrl,raw_intr; //位宽为四,0B的代表,二进制标识,0位
reg [0:5] int_req;

mask_intr = test_ctrl+raw_intr; //结果位宽由 mask_intr test_ctrl raw_intr 位宽决定 ,加法操作的溢出部分被丢弃
int_req = test_ctrl+raw_intr; //结果位宽由 int_req test_ctrl raw_intr 位宽决定,位宽为6,任何一处的位将被存储在结果位int_req[5]中

较大表达式中,运算中间结果的位宽确定的规则:表达中的所有中间结果应取最大操作数的位宽(在赋值时,此规则也包括赋值等号左端的目标变量)
wire [4:1]intra_ena,test_reg;
wire [1:5]next_iev;
wire [1:6]peg_intr;
wire [1:8]adt_sense;

assign adt_sense = (intra_ena+next_iev)+(test_reg + peg_intr);
//赋值等号右边表达式最大位宽6 ,包含左边时。最大变量位宽8.所以加法运算使用8位进行

有符号和无符号数
执行算术和赋值时,哪些操作当无符号操作,哪些操作当有符号操作非常重要

无符号数值存储:线网中、reg(寄存器)变量中、用普通(没有有符号标记s)的基数格式表示的整型中

有符号数值存储:用s(有符号)标记的基数格式表示的整型中、十进制形式的整数中、有符号的reg变量中、有符号线网中

reg[0:5]burst_data;
integer mtx_addr;

burst_data = -4'd12; //reg变量burst_data的十进制数为52 既向量 110100 (有符号赋值到无符号)
mtx_addr = -4'd12; //整型数mtx_addr的十进制数为-12,二进制表示110100 (有符号赋值到有符号)
-4'd12/4 //结果是1073741821 (-4'd12转换成32位数,以无符号显示 取最大的位宽进行运算)
-12/4 //结果为 -3 都是有符号数据

burst_data 是无符号寄存器,右边表达式值为 6’d110100(-12二进制补码),因此赋值后,burst_data存储的是十进制52。






关系运算符 (> < >= <=)
若操作数中有一位为x或z,则结果为x
52<16'hxFF //结果为x,

操作数位宽不同,所有操作数都是无符号 ,则位宽较小的操作数高位补0对齐
'b1000 >= 'b01100 ==》 'b01000 >= 'b01100
若两个操作数都是有符号数,则用符号位将位数较小的操作数的位数补齐
4'sb1011 <=8'sh1A ==> 8'sb11111011 <= 8'sb00011010 结果为真
若表达式中一个操作数是无符号,则该表达式的其余操作数均被当作无符号数处理
(8'sb9*4'd2) < 4 ==> 假(18 < 4)
(4'sd9*2)<4 ==> 真(-14<4)

相等操作符号(结果假为0 结果真为1)
==(逻辑相等) !=(逻辑不等)
===(全等,case equality) !===(非全等,)
全等比较中,把x和z当作数值(而不考虑其物理含义)严格地按字符值进行比较,结果不是1就是0 ,不会出现其他地值。
逻辑比较中,值x和z具有物理含义(物理含义:有大小区别),结果可能出现不确定值(==)参与比较的两个操作数必须逐位相对,其结果才为1,如果某些值是不定态X或高阻态Z,那么得到的结果是不定值X;
例:
sw_data = 'b11x0; //没有指定位数,默认32位数据比较
sw_addr = 'b11x0;
sw_data == sw_addr; //结果为x,按照规定,
sw_data === sw_addr; //结果为1,按照规定,

'b010x != 'b11x0
上式中的两个操作数中都有x,但比较结果为真,是因为第一位不同
若操作数的位宽不相等,位宽较小的操作数在左侧以0补齐,例:
2’b10 == 4'b0010 ==> 4'b0010 == 4'b0010 ==>结果为真

若操作数位宽不同,两个操作数都是有符号数,较小的操作数用符号位扩位补齐,例
`define WIDTH 8
wire [WIDTH:0] mux_bus,byte_a,byte_b,byte_c,byte_d;
wire [1:0] select;






reg [4:0] a = 5'b11x01;
reg [4:0] b = 5'b11x01;
a == b 返回的结果是X; a===b 的返回结果为 1
(!=)和(!==)的用法类似;

归约运算符:归约与(&) 归约与非(~&) 归约或(|) 归约或非(~|) 归约异或(~^)
注意:归约运算符的操作数只有一个,并只产生一位结果:举例a=0101,则&a=0(a中的所有位进行与操作); |a=1(a中的所有位进行或操作);

逻辑操作符 (逻辑与 或 非)
&&
||

对逻辑值0或1进行操作,逻辑操作产生的结果为0或1
mlock = 'b0
mport = 'b1
mlock && mport ==>结果为0(假)
mlock || mport ==>结果为1(真)
!mport ==>结果为0(假)

对向量操作数而言,非0向量被当作1处理
rdy_bus = 'b0110;
intr_bus = 'b0100;

rdy_bus || intr_bus 的结果为1
rdy_bus && intr_bus 的结果为1
并且!rdy_bus !intr_bus的结果相同,均为0

操作数内某一位为x 或 z,若逻辑操作的结果是未定的,则运算的结果为x
1'b1& 1'bx 结果 x
'b1 || 'bx 结果1
'b0 && 'bZ 结果0
!x 的结果x

按位操作符 (5种)
~ (一元非)
& (二元与)
| (二元或)
^ (二元异或)
~^,^~ (二元同或)
这些操作符对输入的操作数进行逐位操作,逐位操作就对应位进行操作,产生一个向量的结果
与 逐位操作结果 或 逐位操作结果
------------------- ---------------------
& | 0 | 1 | x | z | | | | 0 | 1 | x | z |
0 | 0 | 0 | 0 | 0 | | 0 | 0 | 1 | x | x |
1 | 0 | 1 | x | x | | 1 | 1 | 1 | 1 | 1 |
x | 0 | x | x | x | | x | x | 1 | x | x |
z | 0 | x | x | x | | z | x | 1 | x | x |
------------------- ---------------------

异或 逐位操作结果 同或 逐位操作结果
------------------- ---------------------
^ | 0 | 1 | x | z | | ^~| 0 | 1 | x | z |
0 | 0 | 1 | x | x | | 0 | 1 | 0 | x | x |
1 | 1 | 0 | x | x | | 1 | 0 | 1 | x | x |
x | x | x | x | x | | x | x | x | x | x |
z | x | x | x | x | | z | x | x | x | x |
------------------- ---------------------

取反逐位操作结果
------------------
~| 0 | 1 | x | z |
| 1 | 0 | x | x |
------------------

按位运算中,两个操作数的位宽不等,且其中一个操作数是无符号操作数,则位宽较小的操作数高位补0对齐;(最终奥义就是不改变值的大小)
若两个操作数为有符号数,则位宽小的操作数用符号位在高位进行补齐,然后再操作
'b0110^'b10000 ==> 'b00110^'b10000 =>'b10110

4'sb1010 & 8'sb 01100010 ==> 8'b11111010 & 8'b01100010 => 8'b01100010

缩减操作符(对单个操作数进行操作)
对单一操作数上的所有位进行操作,产生1位的操作结果,缩减操作符有6种。
1)&(缩减与)操作数只要有任意一位的值为0,则结果为0,操作数中只要有任意一位为x或z,则结果为x;否则操作结果为0;

2)~&(缩减与非) 缩减与操作结果取反

3)|(缩减或)操作数中只要有任意一位的值为1,则该操作的结果便为1,操作数中只要有任意一位为x或z,则结果为x;否则操作结果为1;

4)~|(缩减或非)缩减或操作结果取反

5)^(缩减异或) 操作数中只要有任意一位为x或z,则结果为x; 若操作数中有偶数个1,则结果为0 ,否则结果为1

6)~^(缩减同或)缩减异或操作结果取反

a= 'b0110;
b ='b0100;

|b //结果1
&b //结果0
~^a //结果1

缩减异或操作符可以用来确定向量中是否存在值x得位
addr_port = 4'b01x0; //对addr_port异或结果为x
对上述功能,可以使用如下的if语句进行检查;
if(addr_port === 1'bx) begin

end

移位操作符
<< (逻辑左移)
>> (逻辑右移)
<<< (算术左移)
>>> (算术右移)
移位操作符右侧操作数是一个无符号数,若右侧操作数值为x或z,则移位操作结果为x。
逻辑移位,移动的位置总是填0;算术移位,左移空出位置填0,右移位,若操作数为无符号数,空出来位填0,若操作数为有符号数,则空出来的位总是填写符号位。

reg[7:0] qreg = 8'h17; //0001 0111
reg signed [3:0] pmaster = 4'b1011;

qreg >> 2 结果为 0000 0101
qreg << 2 结果01011100
qreg <<<4 结果 0111 0000
qreg <<-2 //因为右操作数总是一个无符号数,因此向左移位了2**31-2 2的31次方数-2

qreg >> 4 结果 0000 0001
qreg >>>2 结果 0000 0101
pmaster >>>2 结果 1110

移位操作符可以用来完成指数(幂运算);
例如,若计算2的 num_bits 幂,
32'b1<< num_bits //num_bits必须小于32
//指数(幂)操作符也可以用 2** num_bits

也可以用移位操作符为2-4译码器建立verilog模型
wire [3:0]decode_out = 4'b1 << address[1:0];
address分别可取值 0 1 2 3
decode_out对应值 4'd0001 4'd0010 4'd0100 4'd1000

算术右移是有符号的除2运算,分数部分被移位到下一个最低位的整型中。
qreg>>>2 //运算结果5(23/4)
pmaster>>>2 //结果是-2(-5/2)

算术移位操作符场合,存在一种特殊情况,操作数是有符号,移位却是0,
这是一个操作符有符号,一个无符号,按无符号数处理
reg signed [3:0]xfer_port;
3'd4+xfer_port>>>1 //因为xfer_port当作无符号处理,所以移位空出位补0

条件操作符
cond_expr?expr1:expr2;
和C语言类似,不同处若cond_expr为x或z,则操作结果按以下逻辑执行 expr1与expr2按位操作;0与0得到结果0,1与1得到结果1,其余情况下的结果为x;
例:
wire [2:0] student = (marks>18)?grade_a:grade_c;
计算(marks>18) 为真则赋值grade_a ,若 marks <= 18 则赋值grade_c


拼接和复制操作符
拼接(concatenation)是将小表达式中的位拼接起来形成一个由多个位组成的大表达式的操作,语法:
{expr1,expr2,...,exprn}
例:
wire [7:0]dbus;
wire [11:0]abus;

assign dbus[7:4] = {dbus[0],dbus[1],dbus[2],dbus[3]}; //dbus的第四位值以颠倒的顺序,赋值给其高四位
assign abus[7:0] = {dbus[3:0],dbus[7:4]};//dbus的高4位与低4位交换后赋值给abus[7:0]

未指定位宽的常数其位数是未知的,拼接不允许出现未指定位宽的常数。
{dbus,5} //不允许在拼接操作中出现5,这样未指定位宽的常数
拼接是非法的。

abus = {3{4'b1011}}; //位向量12‘b101110111011 就是3个4位数拼接
abus = {{4{dbus[7]}},dbus} /*符号扩展*/

{3{1'b1}} //结果为111
{3{ack}} //结果与{ack,ack,ack}等效

重复操作也可以被参数化,
parameter LENGTH = 8;
{LENGTH{1'b0}} //一个由8个0组成的字符串

拼接操作计算2的幂指数、符号的扩展以及0的扩展
parameter POWER_OF = 4, PAD_BY = 5;
wire[7:0]cgr_reg;

wire [31:0] power_of_two = {1'b1,{POWER_OF{1'b0}}}; //二次幂
wire [12:0] sign_extension = {{PAD_BY{cgr_reg[7]}},cgr_reg}; //标志扩展
wire [11:0] zero_extension = {{4{1'b0}},cgr_reg}; //符号扩展

表达式的类型



操作符对操作数进行逐位操作,
()[https://www.runoob.com/w3cnote/verilog-expression.html]




缩减操作符
{% endcodeblock %}