一、Verilog 语法—设计使用
a) reg:寄存器型信号;//在always语句中变量必须定义为reg,在initial语句中用reg。
b) wire: 线网型信号;//信号若无声明,默认为wire型,assign语句中,例化的输出信号。
c) parameter:参数声明;
d) assign:连续赋值;
e) {}:拼接/复制运算符;//led = 0001 {led[0],led[3:1]} led = 1000;
f) if - else、if - else if – else、case :条件选择;
a) 赋值运算符:非阻塞赋值(<=)、阻塞赋值(=);
阻塞赋值“=”(组合逻辑电路),非阻塞赋值“<=”(时序逻辑电路);
阻塞赋值具有先后顺序
非阻塞不具有先后顺序
如:a = 1; b = 2; c = 3;
a = b + c;
c = a;
结果是a = 5 , c = 5
a <= b + c;
c <= a;
结果是a = 5 , c = 1
b) 算术运算符:+,-,*,/;
c) 关系运算符:<、<=、>、>=、 ==、 !=;
d) 逻辑运算符:&&、||、!;
e) 位运算符: ~、|、^、&;
f) 逻辑移位运算符:<<、>>;
g) 算术移位运算符:<<<、>>>;//有符号数的移位操作,只能使用算数移位。逻辑右移要补符号位
Verilog 语法—仿真使用
a) initial:激励信号初始化;
b) for/while/repeat/forever:循环;//forever只能在initial中用
c) integer:整型变量声明;
d) #:延时;
e) $random:产生随机数;// 加上{}后表示在正数范围随机
{$random}%10000//表示10000内的正数随机数
f) task/founction:任务、函数;
module task_function(
in1,
in2,
in3,
in4,
out1,
out2
);
input [7:0] in1,in2,in3,in4;
output reg [8:0] out1,out2;
//任务
task add1; //注意无端口列表
input [7:0] a,b; //输入端口和数据类型声明
output reg [8:0] out1; //输出端口和数据类型声明
out1 = a + b; //a,b,out1名称的作用范围仅为task任务内部
endtask
//调用任务
always@(in1 or in2) begin
add1(in1,in2,out1); //调用任务,注意端口列表的顺序要和任务中定义的一致
end
//函数
function [8:0] add2; //函数定义 函数会自动生成一个和函数名同名的内部变量,作为函数返回值所用的寄存器。
input [7:0] c,d; //输入端口声明 函数只有输入端口,输出端口为函数名本身。
add2 = c + d; //返回输出值 输出寄存器为和函数同名的内部寄存器
endfunction
//调用函数
always @(in3 or in4) begin
out2 = add2(in3,in4); //调用函数,注意端口列表的顺序要和函数中定义的一致
end
endmodule
分别在任务和函数中实现两个数相加的操作,在调用任务时,需要向任务中传递输入输出端口。在调用函数的时候,只需要传递输入信号,函数的返回值作为输出信号。
g) force/wait/fork:仿真很少用;
Verilog 代码设计—时序逻辑、组合逻辑
二、Verilog 代码设计规范
- 一个 always 只产生一个信号,一个信号只能在一个 always 赋值;
- always 是描述一个信号的方法,在某种情况下,这个信号的值为多少;在其他情况下,值又为多少,要全部考虑清楚;
- 条件判断只使用 if-else / if-else if – else 和 case;
- 敏感列表含有 posedge 或 negedge 的一定是时序逻辑;
- 设计时,如果想立即有结果,就用组合逻辑;如果想要延时一拍再输出,就用时序
逻辑; - always 中的信号一定用 reg 定义,非 always 中的信号一定用 wire 定义;
- 时序逻辑使用非阻塞(<=)赋值,组合逻辑使用阻塞(=)赋值。
补充:
1、算术左移-逻辑左移
算术左移和逻辑左移一样都是右边补 0: 比如 00101011
算术左移一位:01010110
逻辑左移一位:01010110
对于二进制的数值来说左移 n 位等于原来的数值乘以 2 的 n 次方
比如 00011010 十进制是 26,左移两位后是 01101000 转成十进制是 104 恰好是 26 的 4
倍。
ps:这种倍数关系只适用于左移后被舍弃的高位不含 1 的情况,否则会溢出。
2、算术右移,逻辑右移
逻辑右移很简单,只要将二进制数整体右移,左边补 0 即可
如 10101101 逻辑右移一位为 01010110
算术右移符号位要一起移动,并且在左边补上符号位,也就是如果符号位是 1 就补 1 符号
位是 0 就补 0
比如:11100 算术右移一位为 11110(符号位 1 跟着一起移动并且左边补了 1)
对于二进制的数值来说右移 n 位等于原来的数值除以 2 的 n 次方
比如 10110100 十进制是 76(需要先将这个补码转换成原码之后再转换成十进制),右移两
位后是 11101101 转成十进制是 19 恰好是 76 的 4 倍。
算术左移和算术右移主要用来进行有符号数的倍增、减半;
逻辑左移和逻辑右移主要用来进行无符号数的倍增、减半。
**原码:**原码的表示与机器数真值表示的一样,即用第一位表示符号,其余位表示数值,例如的十进制的的正负1,用8位二进 制的原码表示如下: 【+1】= 原:[ 0000 0001 ]
【-1】= 原:[ 1000 0001 ]
**反码:**反码的表示方法为:
正数的反码是其原码本身。
负数的反码是在其原码的基础上,符号位不变,其余各位取反。
【+1】= 原: [ 0000 0001 ] = 反:[ 0000 0001 ]
【-1】 = 原:[ 1000 0001 ] = 反:[ 1111 1110 ]
**补码:**补码的表示方法为: 正数的补码是其原码本身。 负数的补码是在其原码的基础上,符号位不变,其余各位取反后加1(即在反码的基础上加1)。
【+1】= 原: [ 0000 0001 ] = 反:[ 0000 0001 ] = 补:[ 0000 0001 ]
【-1】 = 原:[ 1000 0001 ] = 反:[ 1111 1110 ] = 补:[ 1111 1111 ]