Verilog快速入门
本篇文章综合北航计算机组成原理课题组&大黑书&网上一些资料&自己杂七杂八的一些想法整理而成,笔者主要写在这儿做复习用。。。。若有侵权请联系删除。
数据类型
-
wire型:属于nets型数据,通常用于组合逻辑。
- 有输入才有输出
- 输出随输入即时改变
- 一般用assign赋值
- 在声明过程中指明位数。(例如32位wire型变量的声明为:wire [31:0] in;)
assign a=a+1不合法!!!
-
reg型:通常用于时序逻辑,尤其是always块里。
- reg的值改变需要有触发信号
不能被assign赋值- 可以用于建立数组,(例如reg [31:0] mem [0:1023];前面的中括号内是位宽,后面的是存储器数量。
- 字符串保存在reg型变量中,每个字符占用一个字节(8bit),如果寄存器变量的宽度小于字符串大小,会截去字符串左边多余的数据。
-
integer型:通常用于for循环
- 不用声明位宽,默认为32位
- 默认为有符号数
-
parameter型:定义在编译时为确定值的常量
- parameter width =8;
-
real型:实数,可用十进制或者科学计数法表示
- 声明不能带有范围,默认为0
- 如果将实数赋值给一个整数,只有整数部分会赋值过去。
-
time型:对仿真时间进行保存。
- 其宽度一般为 64 bit
- 通过调用系统函数 $time 获取当前仿真时间。
-
有一些特殊字符在显示字符串中有特殊意义,例如换行符,制表符等。如果需要在字符串中显示这些特殊的字符,则需要在前面加前缀转义字符 \ 。例如下表所示:
转义字符 显示字符 \n 换行 \t 制表符 %% % \ \ " " \ooo 1到3个8进制数字字符 -
数字字面量:
- 数字的完整表达为<位宽>’<进制><值>,例如2’b00
- 省略【位宽】时使用默认位宽,一般为32位
- 省略【进制】时一般为十进制
- 【值】部分可以用下划线隔开,但下划线只能用于数字之间,例如8’b_0100_1000是非法的,而8’b0100_1000则是合法的。
-
有符号数
-
verilog语言默认wire、reg型变量为无符号数。
-
要进行有符号数的比较,需要使用$signed( );
reg [3:0] a; reg [3:0] b; wire result; assign result = $signed(a)>$signed(b);
-
常用语句
-
assign语句
- 连续赋值:assign out=~in;
- 条件运算:
- assign y = s ? di:d0;(单个条件)
- assign y= s ? (s[0] ? 1:0) : (s[1] ? 1:0); (多个条件复用)
- asssign语句的含义表示a的值时刻等于b
- assign语句不能在always和initial块中使用
-
优先级:
- ~
- *,/,%
- +,-
- <<,>> 逻辑左移/逻辑右移
- <<<,>>> 算数左移/算数右移 <,<=,>,>=(相对比较) ==,!=(相等比较)
逻辑左移=算数左移,右边统一添0 ;逻辑右移,左边统一添0 ,算数右移,左边添加的数和符号有关
-
&,~&(NAND)
-
,~(XNOR)
-
|,~| (NOR)
-
?= (条件运算)
-
verilog的其他运算符:
- 相等比较运算符“= =”、“===”、“!=”、“!= = =” (没有空格,但是markdown里面两个等号好像是高亮格式,有点难受)
- ==和!=可能由于不定值x和高阻值z的出现导致结果为不定值x
- 而= = =和!==的结果一定是确定的0或1(x与z也参与比较)
- 阻塞赋值=和非阻塞赋值<=
- 阻塞赋值表示每条语句依次执行,有明确的顺序关系,当一条赋值完成后再执行下一条语句
- 非阻塞赋值表示多条语句并行执行,在描述时序逻辑时要使用非阻塞式赋值<=
- 位拼接运算符{}
- {a,3{a,b} } == {a,a,b,a,b,a,b} 将信号拼接
- {a,b[3:0],w,3’b101} 拼接
- 缩减运算符
- 如reg [31:0] B,&B表示将B的每一位与得到结果
- 相等比较运算符“= =”、“===”、“!=”、“!= = =” (没有空格,但是markdown里面两个等号好像是高亮格式,有点难受)
-
always语句:
-
always@或者always@(*)==always_comb
- 可以避免因为漏写变量而出现错误(尤其是当always语句块中出现中间变量的情况)
-
用always语句实现同步复位/异步复位:
-
同步:
always@(posedge clk) begin if (reset) ··· end
-
异步:
always@(posedge clk,posedge reset) begin ··· end
-
-
always紧跟多个条件时,用‘,’或者‘ or ’连接,只要有其中一个条件被触发,always之后的语句都会被执行
-
不要在多个always块中对同一个变量进行赋值!
-
-
initial语句:
- 在硬件仿真开始时执行,且仅执行一次
- 一般用于对reg型变量进行初始化
-
if语句
- 记得写else
-
case语句
-
begin+endcase标准模板别忘了!
-
case语句在分支执行结束后不会落入下一个分支,而会自动退出。
-
写出default语句,消除异常输入或者无关项。
- 例如,0-11为有效信号,当输入12-15时,case会记录之前的输出。
- 定义所有情况下的输出,保证产生组合逻辑
-
case语句只能在always块中使用
-
casez语句:可以识别作为无关系的“?”
casez(a) 4'b00??: y<=2'b00; 4'b01??: y<=2'b01; 4'b10??: y<=2'b10; 4'b11??: y<=2'b11; default: ; endcase
-
-
for&while语句:
module vote7( input [6:0] vote, output reg pass ); reg [2:0] sum; integer i; always @(vote) begin sum=3'b000; for (i=0;i<7;i=i+1) begin if (vote[i]) sum=sum+1; end if (sum>3'd4) pass=1'b1; else pass=1'b0; end endmodule
module counts1_while( input clk, input [7:0] rega; output reg [3:0] count ); always @(posedge clk) begin:count1//命名顺序快,建模时序逻辑 reg [7:0] tempreg;//中间变量 count=0; tempreg=rega; while (tempreg) begin if (tempreg[0]) count=count+1; tempreg = tempreg >> 1;//逻辑右移一位 end end endmodule
-
时间控制语句:
#3; #5 b=a; always #5 clk=~clk; assign #5 b=a;//表达式右边的值延时5个时间单位后赋值给b
模块引用
调用方式1:被调用模块的名称+实例名(自定义)+按模块端口顺序写下实例的端口名。
调用方式2:被调用模块的名称+实例名(自定义)+( ‘ . ’ +模块接口+(被调用接口) )。此方式不需要使用全部接口,且接口顺序任意。
module Sample(
input a,
input b,
input c,
output d
);
Sample sample_1(e,f,g,h);
Sample sample_2(.a(i), .b(j), c(k), .d(l));
宏定义
目前只涉及用宏定义定义常量(类似于parameter)。
**引用宏名时也必须在宏名前加上符号"`"**表明该名字是经过宏定义的名字。
`define WORDLENGTH 8
reg [`WORDLENGTH:1] data;