笔记:verilogHDL学习笔记2--组合逻辑与时序逻辑

本文详细介绍了Verilog HDL中的组合逻辑与时序逻辑。重点讲解了wire和reg类型、always语句、数字进制、运算符(包括加减、逻辑、移位等)、条件运算符以及if和case语句的使用。还探讨了同步复位和异步复位的时序逻辑,并举例展示了D触发器的实现。同时,对比了阻塞赋值与非阻塞赋值的区别。
摘要由CSDN通过智能技术生成

verilogHDL学习笔记2–组合逻辑与时序逻辑

一、组合逻辑

1.wire 和 reg
reg:寄存器类型,通常是对存储单元的描述,在下一个触发机制到来之前保留原值,用always描述
wire:线网型类型,相当于实际的连接线,变量的值随时发生变化,用assign连接(多条assign的赋值语句之间互相独立,并行执行)

2.always语句

always @(敏感事件)begin 
	程序语句 
end
例子:
always @(a or b or d or sel)begin
	 if(sel==0)
	 	  c = a + b;
	  else c = a + d;
end

注意:“ * ” 可以指“程序语句”中所有的条件信号(推荐写法),即 a、b、d、sel(不包括 c)

3.数字进制
<位宽>’<基数><数值>,如 4’b1011。
位宽:描述常量所含位数的十进制整数,是可选项。
基数:进制,是 b,B,d,D,o,O,h 或者 H,缺省默认为十进制数
数值:数值未写完整时,高 位补 0。注:如果基数为 b 或 B,可以 是 0,1,x,X,z 或 Z。其他进制不能有x 或 z

4.运算符
4.1 加减运算:
assign C=A + B;
assign C=A - B;
总结运算步骤如下: 1. 根据“人的常识”,预计结果的最大最小值,从而确定结果的信号位宽。 2. 将加数、减数等数据,位宽扩展成结果位宽一致。 3. 按二进制加减法进行计算。

4.2 逻辑运算:
与 &&
或 ||
非 !

assign a = 4’b0111 && 4’b1000; 
assign b = 4’b0111 || 4’b1000; 
assign c = !4’b0111;
结果为 a 为逻辑真,b 为逻辑真,c 为逻辑假。

4.3 判断逻辑
使用心得1:逻辑运算符两边对应的是 1 比特信号

其中 a 和 b 都是多比特信号,表示两个多比特信号进行逻辑与。
wire[3:0] a, b; 当 a 不等于 0 并且 b 不等于 0 时,d 的值为 1。
assign d = a &&b ;  不等于 0 就是表示逻辑真,等于 0 就表示逻辑假
assign d = (a!=0) && (b!=0) ; 等价于这种方式

使用心得2:多是用括号,优先级
优先级:
逻辑运算符中“&&”和“||”的优先级低于算数运算符;“!”的优先级高于双目逻辑运算符。

(a < b)&&(c > d)可写成:a < b && c > d 
(a = = b)| |(c = = d)可写成:a = = b | | c = = d 
(!a)| |(a > b)可写成:!a | | a > b 

逻辑运算符**“&&”和“||”的优先级算数运算符,的优先级高于双目逻辑运算符**
逻辑与“&&”和按位与“&”进行对比可以看出,逻辑与运算符的运算只有逻辑真或逻辑假两 种结果,即 1 或 0;而“&”是位运算符,用于两个多位宽数据操作。

assign a = 4’b0111 && 4’b1000;
assign b = 4’b0111 || 4’b1000; 
assign c = !4’b0111;
assign d = 4’b0111 & 4’b1000; 
assign e = 4’b0111 | 4’b1000; 
assign f = ~4’b0111;

a=1’b1,b=1’b1,c=1’b0,d=4’b000,e=4’b1111,f=4’b1000。

4.4 逻辑运算符

|| :逻辑或A B一个当中一个为1则为1
| :按位或 对应比特位相或
~^ ^~ :二元异或非即同或
~非 、 &与 、 | 或 、^ 异或 都是按位操作

4.5 关系运算符

大于>、 小于< 、大于等于>=、小于等于<=、 ==逻辑相等 !=逻辑不等于

4.6 移位运算符

“<<” 左移位运算符、 “>>” 右移位运算符
wire[3:0] a; assign a = 4’b0111<< 2;
结果:a = 4’b1100。

左移操作属于逻辑移位,需要用 0 来填补移出的空位,即在低位补 0。左移 n 位,就要补 n 个 0。
**左移操作是不消耗逻辑资源.
左移操作的操作数可以是常数,也可以是信号。同样,左移操作的移位数、常数也可以是 信号。
右移运算同理

通过左移乘法运算,利用右移实现除法运算,利用左移位产生独热码
例如:a*2,等价于 a<<1;

assign b = a*67; 
assign c = (a<<6) +(a<<1) +a;
b 和 c 都可以实现 a*67,但第 1 行消耗了一个乘法,
而第 2 行则只用到两个加法 器,从而节省了资源。

独热码在设计时非常有用,可以用来表示状态机的状态使状态机更健壮,也可以用于多选一的电 路中,表示选择其中的一个。 利用左移位操作,可以方便地产生独热码,例如产生 4’b0010,可以是 4’b1 << 1。类似地,也 可以产生 1 个比特为 0,其他为 1 的码制。例如产生 4’b1011,可以是~(4’b1 <<2)。
利用左移操作, 还可以产生其他需要的数字结果: 例如,产生 5’b00111,可以是(5’b1<<3)-1。 例如,产生 5’b11100,可以是~((5’b1<<2)-1)。

4.7 条件运算符
在这里插入图片描述
在这里插入图片描述
4.8 三目运算符

always@(*)begin 
	r =s ? t : u ;
end
表达式中 s 如果为真,则把 t 赋值给 r;如果 s 为假,则把 u 赋值给 r
可以使用if-else来替代

在这里插入图片描述
5 if语句
建议: 1、条件表达式需用括号括起来
2、若为 if - if 语句,请使用块语句 begin — end

if(Clk) 
	begin if(Reset) 
		Q = 0;
	else 
		Q = D; 
	end

6.case语句
可以在 1 个分支中定义多个分支项,且这些值不需要互斥。缺省分支覆盖所有没有被分支表达式覆盖的其他分支
书写建议: case 语句的缺省项必须写,防止产生锁存器。

case (HEX)
	 4'b0001 : LED = 7'b1111001; // 1 
	 4'b0010 : LED = 7'b0100100; // 2 
	 4'b0011 : LED = 7'b0110000; // 3 
	 4'b0100 : LED = 7'b0011001; // 4 
endcase 

7.选择语句
例 1:vect[7 +: 3]
其中,起始位置为 7,+代表着升序,宽度为 3。即从 7 开始向比 7 大的方向数 3 个数。其等价 形式为:vect[7 +: 3]==vect[7 : 9]。
例 2:vect[9 -: 4]
其中,起始位置为 9,-代表着降序,宽度为 4。即从 9 开始向比 9 小的方向数 4 个数。其等价 形式为:vect[9 -: 4]==vect[9 : 6]

在实际使用的过程中该语法最常用的形式是将 a 作为一个可变的数来使用。
当 cnt0 时,将 din[7:0]赋值给 data[15:8];当 cnt1 时将 din[7:0]赋值给 data[7:0]
在设计的时候便可以写成:data[15-8cnt -: 8] <= din[7:0](此时需要将 15-8cnt 视为一个整 体 a,其会随着 cnt 变化而发生变化),这样一来就完成了对代码的精简工作。
选择语句的硬件电路结构如下图所示,其本质上是一个选择器。当 cnt0 时,选中 data[15:8] 的锁存器,将 din[7:0]赋值给 data[15:8],而 data[7:0]的锁存器保持输出不变;当 cnt1 时,选中 d ata[7:0]的锁存器,将 din[7:0]赋值给 data[7:0],而 data[15:8]的锁存器保持输出不变。
在这里插入图片描述
经验总结
if 语句每个分支之间是有优先级的,综合得到的电路是类似级联的结构。case 语句每个分支是平等 的,综合得到的电路则是一个多路选择器。因此,多个 if else-if 语句综合得到的逻辑电路延时有可能 会比 case 语句稍大。

if语句综合后的RTL
module if_case( 
	input [1:0] en, 
	input [1:0] a, 
	output reg q 
);
	always @(*)begin 
		if(en[0]==1) 
		q = a[0]; 
		else if(en[1]==1) 
		q = a[1]; 
		else
		q = 0; 
		end 
		endmodule

在这里插入图片描述
从上图中所示的 RTL 图可以看到,此电路中含有两个二选一多路选择器,并且右边的优先级要 高于左边(因为 q 的值是直接与右边的二选一选择器连接),当 en[0]不为 1 时才会继续判断 en[1]。 也就是说,在 if 语句下综合出的电路含有优先级。

case语句综合后的RTL
always @(*)begin 
	case(en) 
	2'b01: q = a[0]; 
	2'b10: q = a[1]; 
default:q = 0;
	endcase 
end

在这里插入图片描述
使用 case 语句编写的逻辑代码综合出的电路是并行的,没有优先级,不影响电路的运行速度。
但在FPGA中映射后的电路没有差别。

拼接运算符
在这里插入图片描述
二、时序逻辑

  1. always语句
    同步复位的时序逻辑和异步复位的时序逻辑。在同步复位的时序逻 辑中复位不是立即有效,而在时钟上升沿时复位才有效。其代码结构如下:
always@(posedge clk) begin 
	if(rst_n==1’b0) 
		代码语句; 
	else begin 
		代码语句; 
	end 
end

在异步复位的时序逻辑中复位立即有效,与时钟无关。其代码结构如下:

always@(posedge clk or negedge rst_n) begin 
	if(rst_n==1’b0) 
		代码语句;
	 else begin 
		 代码语句; 
	 end 
 end
  1. D 触发器代码
    该段代码总是在“时钟 clk 上升沿或者复位 rst_n 下降沿” 的时候执行一次。具体执行方法如下: 1. 如果复位 rst_n=0,则 q 的值为 0; 2. 如果复位 rst_n=1,则将(a+d)的结果赋给 q(注意,前提条件是时钟上升沿的时候)。 假设用信号 c 表示 a+d 的结果,则第 2 点可改为:如果复位 rst_n=1,则将 c 的值赋给 q(注意, 前提条件是时钟上升沿的时刻)。很明显这是一个 D 触发器,输入信号为 d,输出为 q,时钟为 clk, 复位为 rst_n
always @(posedge clk or negedge rst_n)begin 
	if(rst_n==1'b0)begin 
		q <= 0; 
	end 
	else begin 
		q <= d; 
	end 
end
  1. 阻塞赋值和非阻塞赋值
    阻塞赋值使用 **“=”语句;非阻塞赋值使用“<=”**语句
    阻塞赋值:在一个“begin…end”的多行赋值语句,先执行当前行的赋值语句,再执行下一行 的赋值语句。
    非阻塞赋值:在一个“begin…end”的多行赋值语句,在同一时间内同时赋值。
begin
	c = a; 
	d = c + a; 
end
begin
	c <= a; 
	d <= c + a; 
end

上面两个例子中,1 到 4 行部分是阻塞赋值,程序会先执行第 2 行,得到结果后再执行第 3 行。 6 至 9 行这一段是非阻塞赋值,第 7 行和第 8 行的赋值语句是同时执行的。

  • 4
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sr_shirui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值