概述
sky视频笔记:数字逻辑回顾&Hello World_哔哩哔哩_bilibili
一、数电基础
1.组合逻辑
- 电路逻辑输出值只和当前的输入有关
- 比如:AND/OR/XOR/NAND/NOR/MUX/Adder/Multiplier
2.时序逻辑
- 电路逻辑输出值跟当前的输入和电路的当前状态有关
- 保存当前状态的器件,如:触发器Flip-Flop,锁存器Latch 二者区别: Flip-Flop和Latch - 知乎
3.逻辑值
- 逻辑状态
-
逻辑 0:表示低电平,也就对应我们电路 GND;
逻辑 1:表示高电平,也就是对应我们电路的 VCC;
逻辑 X:表示未知,有可能是高电平,也有可能是低电平,仿真发生了不能解决的逻辑冲突;
逻辑 Z:表示高阻态,外部没有激励信号是一个悬空状态。相当于啥也没接,可以上拉或者下拉。 -
对于没有进行初始化的信号,一般处于不确定态(x),高阻态表示该信号没有被其他信号驱动,经常用于有多个驱动源的总线型数据上。
-
对于高阻态的理解:Verilog中三态门(高阻态)的理解与例子_CLL_caicai的博客-CSDN博客_verilog 高阻态
-
反相器(非门)
-
与门/或门/异或门
二、Verilog介绍
1.概述
- 是一种硬件描述语言HDL,不是设计语言,先有电路,后有程序
- 类似的有VHDL、SystemVerilog、Chisel(未来的方向,目前不好说)
- 推荐书:国外的Verilog HDL 高级数字设计(第二版)—Miachael,D.Cilette和国内Verilog数字系统设计教程(第4版)夏宇闻。
2.可综合&不可综合
-
可综合描述:综合tool能够把Verilog描述转化(Comlile)为基本的数字电路底层cell(与或非门,寄存器等)
例如:assign y = a & b;—>
-
不可综合描述:不能把Verilog描述转化为基本的数字电路底层cell的描述
例如:$display("Hello world.n");这种是需要串口。
3.Verilog设计仿真与实现
-
何为仿真?
从仿真器的角度理解Verilog语言:从仿真器的角度理解Verilog语言 - 知乎
-
常用的仿真工具(所有verilog描述)
- modelsim/Questasim (Mentor)
- VCS (Synopsys)
- NC-verilog (Candence)
-
Verilog设计的物理实现(可综合verilog描述)
- 综合后在IC芯片上实现
- 综合后在FPGA上实现
4.数字电路设计方法学
-
Top-Down:从顶层结构,算法/协议上开始。享下逐步划分功能模块;再细分各功能模块的功能和IO
三.Verilog语法介绍
1.标识符
- 首字母必须是字母/下划线
- 区分大小写
- 信号名字和信号功能对应
2.关键字
完整关键字定义,见verilog标准:IEEE Std 1364-2001
Verilog标识符与关键字_FPGA&IC设计导师的博客-CSDN博客_verilog标识符
initial一般是不可综合的,always是可综合的
3.数据物理类型
- “线”性数据:用于连续赋值语句(assign)描述组合逻辑或module间的信号连接线
- wire:线性,可综合
- wire a; //1bit位宽
- assign a=1'b0; //连续赋值语句
- tri0/tri1:带下拉/上拉电阻的线性,没有驱动时,会有默认值0/1,一半可综合代码不用。
- wire:线性,可综合
- 寄存器类型:用于过程赋值语句(always,initial)描述组合逻辑/时序逻辑
- reg d1;
- d1<=din;//时序逻辑 d1 =din;//组合逻辑
- 除了wire/tri0/tri1外的其他数据类型,基本都属于寄存器类型,只能用于过程赋值语句;
- real 类似于float
- interger //等价reg [31:0]
- time/realtime
4.数据类型
- 一维标量类型(常量)
-
整数型(需要明确指定数据位宽):位宽代表转化为二进制数的位宽,达不到高位补0,多的,高位截断
16'd100 //10进制表示的“100”
16'h64 //16进制表示的“100”
16'b0110_0100 //2进制表示的“100”
-
实数型(可综合verilog不用)
-
字符型(每个字符按8bit ASCII码的整型存储)
“verilog” //占56bit
-
- 一维标量类型(变量)
-
整数型(需要明确指定数据位宽)
-
- 实数型(目前不可综合)
- 多维标量类型(变量)
{}是用来合并的
- 片选
在 assign 赋值操作中,如果等号左右两侧信号的位宽不同,那么就会进行截断或者补零操作。
- 左侧信号位宽大于右侧信号位宽,右值的低位赋予左值对应的低位,左值高位的部分赋零。
- 左侧信号位宽小于右侧信号位宽,右值的低位赋予左值对应的低位,右值高位的部分直接被截断。即保留右值的低位。
使用 [] 可以对信号进行片选,选择信号中特定几位比特,以下是一些片选的例子。
w[3:0] // Only the lower 4 bits of w
x[1] // The lowest bit of x
x[1:1] // ...also the lowest bit of x
z[-1:-2] // Z 最低两位
b[3:0] // 如果 b 在声明时 声明为 wire [0:3] b;则不能使用 b [3:0]进行选择
b[0:3] // b的高四位.
assign w[3:0] = b[0:3]; // 将 b 的高位赋予 w 的低位 w[3]=b[0], w[2]=b[1], etc.
5. 运算符
- 按bit位逻辑操作:&(and), |(or), ^(xor),~(inv)
- 按整体运算:&&,||,!,==,! =, 返回值为0/1
- 条件运算符(三目运算符) 描述MUX ?:
- bit位选择[]/拼接{} a[0 +:8] 从0开始向上取8位,等价于a[7:0]
- 算术运算符:+,-,*,<,>,< =,> =,**/,% (**慎用)
6.条件语句
- if-else
if begin
end
- case/casez/casex(casez/casex非必要不用)
case(*)
3'b001: out = 0;
...
end case
7.module 功能模块构建
- 对于需要instance的模块,input 只能是wire,output可以是wire/reg
8.杂项
四、Verilog描述组合逻辑
1.MUX(多路选择器)
module mux4to1(s,d,y);
input wire[1:0] s;
input wire[3:0] d;
output reg y;
always @(*) begin
case (s[1:0])
2'd0:y = d[0];
2'd1:y = d[1];
2'd2:y = d[2];
default: y = d[3];
endcase
end
endmodule
对应的电路图如下:
2.带有优先级的多路选择器(4选1)
module mux4to1(din,d,dout);
input wire[1:0] din;
input wire[3:0] d;
output reg dout;
always @(*) begin
if(din[2])
dout = d2;
else if(din[1])
dout = d1;
else if(din[0])
dout = d0;
else dout= d3;
end
endmodule
3.译码器
-
N-2^N译码器
独热码:每次只有一位输出。
-
3-8译码器
module dec_3to8(
din ,
dout
);
input wire [2:0] din;
ouput reg [7:0] dout;
always @(*) begin
case(din)
3'b000:dout = 8'h01;
3'b001:dout = 8'h02;
3'b010:dout = 8'h04;
3'b011:dout = 8'h08;
3'b100:dout = 8'h10;
3'b101:dout = 8'h20;
3'b110:dout = 8'h40;
3'b111:dout = 8'h80;
endcase
end
endmodule
4.可综合常用语法:组合逻辑
//实现输出a+b-c
wire clk,rstn;
wire [15:0]a,b,c;
wire [17:0]out0;
reg [17:0[ out1;
assign out0 = {2'b0,a}+{2'b0,b}-{2'b0,c}; //连续赋值语句
always @(*) begin //过程赋值语句
//always @(a or b or c) begin
out1 = {2'b0,a}+{2'b0,b}-{2'b0,c};
end
-
CSA(Carry Save Adder,进位保存加法器)
【HDL系列】进位保存加法器原理与设计 - 知乎
5.有符号数符号位扩展
- 有符号数符号位扩展
工具会默认把wire数当作有符号数
wire [15:0] a,b; //singned number
wire [16:0] out0;
wire [16:0] out1;
assign out0 = {a[15],a}-{b[15],b}; //
assign out1 = a-b; //这种会出错
五、Verilog描述时序逻辑
- 可综合常用语法:时序逻辑
//实现输出a+b-c
wire clk,rstn;
wire [15:0]a,b,c;
reg [17:0[ out2,out3;
//1.同步复位
always @(posedge clk) begin
if(!rstn)
out2 <= 'd0;
else
out2 <= {2'b0,a}+{2'b0,b}-{2'b0,c};
end
//2.异步复位
always @(posedge clk or negedge rstn) begin
if(rstn)
out3 <= {2'b0,a}+{2'b0,b}-{2'b0,c};
else
out3 <= 'd0;
end
[FPGA复位——同步复位和异步复位]https://www.jianshu.com/p/f2f78b4be64c
-
同步复位等效电路
顾名思义,同步复位就是指复位信号只有在时钟上升沿到来时,才能有效。否则,无法完成对系统的复位工作。
-
异步复位等效电路
它是指无论时钟沿是否到来,只要复位信号有效,就对系统进行复位。
六、Verilog语句执行顺序
- 连续赋值(assign)语句块,过程赋值(always,initial)语句块之间是并行执行的(只要触发条件满足,就开始执行)
连续赋值(assign x=y;):不能在过程块内使用;
过程阻塞性赋值(x=y;):只能在过程块中使用;
过程费阻塞性复制(x<=y):只能在过程块内使用。
- 组合逻辑用阻塞赋值,时序逻辑用非阻塞赋值,同一个模块中不要既用阻塞又用非阻塞
1.initial、always、assign
-
initial
只能执行一次,在仿真一开始就执行,
begin...end
之间是顺序执行(非阻塞赋值除外)
initial
begin
语句1;
语句2;
......
语句n;
end
-
always
always 语句是重复执行的。always 语句块从 0 时刻开始执行其中的行为语句;当执行完最后一条语句后,便再次执行语句块中的第一条语句,如此循环反复。(非阻塞赋值除外)
对于硬件综合来说,存在两种always块:
组合逻辑:`always @(*)` 时序逻辑:`always @(posedge clk)`
时序always块也会像组合always块一样生成一系列的组合电路,但同时在组合逻辑的输出生成了一组触发器(或寄存器)。该输出在下一个时钟上升沿(posedge clk)后可见,而不是之前的立即可见。
always @ (event)
[statement]
always @ (event) begin
[multiple statements]
end
-
assign
assign相当于连线,一般是将一个变量的值不间断地赋值给另一个变量,就像把这两个变量连在一起,所以习惯性的当做连线用,比如把一个模块的输出给另一个模块当输入。只能用于阻塞赋值。
要更好的把握assign的使用,Verilog中有几个要点需要深入理解和掌握: - 在Verilog module中的所有过程块(如initial块和always块)、连续赋值语句(如assign语句)和实例引用都是并行的。在同一module中这三者出现的先后顺序没有关系。 - 只有连续赋值语句assign和实例引用语句可以独立于过程块而存在于module的功能定义部分。 - 连续赋值assign语句独立于过程块,所以不能在always过程块中使用assign语句。
2.非阻塞赋值的执行顺序
- 所有过程赋值(always,initial)语句块中的非阻塞赋值(none blocking assignment:< =)是完全同时执行的
b<=a;
c<=b;
/*
clk0 b0=初值 c0也等于初值
clk1 b1=a c1=b0
clk2 b2=a c2=b1=a
*/
3.阻塞赋值的执行顺序
- 在同一个过程赋值(always,initial)语句块中,阻塞赋值(blocking assignment:=)是顺序执行的。
- 在同一个过程赋值(always,initial)语句块中,阻塞赋值的循序语法没有定义。
4.可综合code建议
- 描述组合逻辑使用"="赋值;
- 描述时序逻辑(DFF)使用"< ="赋值
- #1(delay)可以用,但是综合会被忽略
- 不要在多个语句块中,对同一个变量赋值
- 在一个always块内,该块的一次触发,对同一个信号最多只赋值一次
七.实际用处的电路
1.Edge检测
-
上升沿检测
- 波形图:
-
电路结构:
-
代码
- 波形图:
wire clk,rstn;
wire din;
reg din_dly;
wire pulse;
assign pulse = din & (!din_dly);
always @(posedge clk or negedge rstn)
if(!rstn)
din_dly <= 1'b0;
else
din_dly <= din;
2.全加器
assign sum = a ^ b ^ cin;
assign cout = a&b | a&cin | b&cin;
<-->
{cout,sum} = a + b + cin;
常见的错误来源
1.如何避免引入锁存器
在设计电路时,必须首先具体考虑电路:
1、我想实现一个逻辑门;
2、我想实现一个具有输入并产生输出的组合逻辑块;
3、我想实现一组组合逻辑,紧接着一组触发器。
不要上来就写代码,这样往往与你想象的电路相差很远。
除了你指定的情况以外,会发生些什么,答案是什么也不会发生,输出保持不变。而这往往就导致了电路的错误,所以说语法正确的代码不一定能产生合理的电路(组合逻辑+触发器)。
输出保持不变,这就意味着电路需要记住当前状态,从而产生锁存器。组合逻辑(比如逻辑门)不能记住任何状态。
最后
以上就是慈祥黑米为你收集整理的Verilog学习笔记的全部内容,希望文章能够帮你解决Verilog学习笔记所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复