基于Notion的Verilog HDL学习笔记(continuous updating )以及HDLBITS链接和答案

本文是我本人在寒假自学Verilog的过程总结的笔记,参考书:《Verilog HDL数字设计与综合(第二版)》
刷题网站是 老石推荐的hdlbits

笔记是个人总结,有可能会有纰漏,欢迎指正。(是直接从Notion复制过来的,Notion的会实时更新,这里可能不会)

新人入站,如果这篇文章对您有帮助,请不要吝啬手中的点赞,你们的点赞和关注是我持续更新的动力

先附Notion笔记链接和HDLbits链接 以及HDLBits先附Notion笔记链接和HDLbits链接 以及答案链接


Notion – The all-in-one workspace for your notes, tasks, wikis, and databases.https://purrfect-barnacle-2d8.notion.site/FPGA-329e6bb7c1074347bcfd133994cd0260HDLBitshttps://hdlbits.01xz.net/wiki/Main_PageHDLBits答案汇总_谦豫-CSDN博客_hdlbits答案前言该博客为本人做HDLBits习题时的心得记录总结,欢迎大家一起交流进步。HDLBits网站链接Verilog LanguageBasicsVectorsModules:HierarchyProceduresMore Verilog FeaturesCircuitsCombinational LogicBasic gatesMultiplexersArithmetic CircuitsKarnaugh Map to CircuitSequential LogicLatchehttps://blog.csdn.net/qq_42334072/article/details/113854656

文章较长,先附目录

目录

文章较长,先附目录

Verilog HDL基本概念

数字声明

字符串

关键字和标识符

值的种类

线网(net)

寄存器(reg)

整数(integer)

实数和时间寄存器

向量

向量的声明(declaring vector)

向量域的选择

参数(parameter)

局部参数(localparam)

系统任务和编译指令

系统任务

编译指令

操作符

基本运算符

按位运算符

等价操作符

关系操作符

拼接操作符

缩减运算符

连续赋值语句(数据流建模)

隐式线网声明

隐式连续赋值

过程赋值语句(行为级建模)

阻塞赋值(=)

非阻塞赋值(<=)

⚠️数值交换

🤔三种赋值语句对比

模块和端口

端口连接规则

端口与外部信号的连接

顺序端口连接

命名端口连接

门级建模

基本门的实例引用

实例数组

结构化过程语句

initial语句

always语句

延时

门延时

连续赋值语句的延时

普通赋值延时59

隐式连续赋值

线网声明延时

时序控制

基于延时

常规延时控制

内嵌延时


Verilog HDL基本概念


在程序运行过程中,其值不能被改变的量称为常量。下面先对Verilog HDL语言中使用的数字和表示方式进行总结。

数字声明

采用了一种很全面的表达数字的方式,即: < 位 宽 > < 进 制 > < 数 字 >的表达方式,

其中位宽和进制是可选项, 当省略位宽项时,表示采用默认位宽(由具体的机器系统决定,至少32位);当省略进制项时表达采用默认的十进制表示方式。

在Verilog HDL中,整型常量即整常数值有以下四种进制的表示形式: a. 二进制整数(b或 B) b. 十进制整数(d或D) c. 八进制整数(o或O) d. 十六进制整数(h或H)

<aside> ⚠️ 位宽是值存储带宽,不符合会截断,如1’d2 = 0 ;

</aside>

  • 举例如下:

    8'b10101100 //8位二进制数 12’habc //12位十六进制数

  • 此外,在Verilog中我们可以采用x和z来表示不定值和高阻值,这经常用于判断语句和case语句中,以提高程序的可读性,如下例:

    4'b10x0 //位宽为4的二进制数从低位数起第2位为不定值 12'dz //位宽为12的十进制数,其值为高阻值 8'h4x //位宽为8的十六进制数,其低4位值为不定值 0100_XXXX

值得注意的是,当采用不同进制表示时,x和z表示的位数也不同。

当我们需要表示负数时,只需要在表达式的最前面加上一个减号即可,如:

<aside> 💡 -8'd5//十进制 注意:写成8'-d5和8'd-5的形式都是错误的。

</aside>

最后,可以用下划线来分隔开数的表达以提高程序的可读性,如:

16'1010_1011_1111_1010

  • 如果定义的长度比为常量指定的长度长,通常在左边填0 补位。但是如果数最左边一位为x 或z ,就相应地用x 或z 在左边补位。例如:

    10'b10 左边添0 占位, 0000000010 10'bx0x1 左边添x 占位, x x x x x x x 0 x 1

  • 如果长度定义得更小,那么最左边的位相应地被截断。例如:

    3 ' b1001 _ 0011 与3'b011 相等 5'H0FFF 与5'H1F 相等

字符串

双引号括起来 Verilog将字符串当做一个字节的ACSII字符队列。 字符串保存在reg的类型变量中,每个字符占8位。

如果寄存器变量宽度大于字符串的大小,则Verilog使用0在填补左边空余位; 如果寄存器变量宽度小于字符串的大小,则截去最左边的位。

reg [8*18:1] string_value;
initial
		string_value = “Hello Verilog World”;

关键字和标识符

Verilog中关键字全部采用小写

标识符是程序代码中对象的名字,由字母数字字符、下划线(_)和美元($)组成。区分大小写,且开始必须是字母数字字符或下划线

  • reg value //reg是关键字;value是标识符
  • input CLK //input是关键字;CLK是标识符

值的种类

线网(net)

net表示硬件元件之间的连接,线网由其连接的元器件输出端驱动。 一般采用wire进行声明。如果没有显式说明为向量,则默认线网宽度为1。默认值为z(trireg类型除外,其为x)例如:

  • wire a = 1’b0 ;//声明a是wire类型,并赋逻辑值0
  • wire[width - 1 : 0 ] b;//定义一个width位的wire类型

<aside> 👉 注意:net是一组数据类型包括wire,wand,wor,tri,triand,trior和trireg等 RTL基本知识:线网类型知多少 - 魏老师说IC - 博客园

</aside>

寄存器(reg)

并非硬件寄存器,仅仅意味是一个保存数值的变量。定义语法:

reg [msb : lsb] name;

范围定义可选,若没有定义,则默认为1位寄存器。

  • reg signed [63 : 0] m;//64位带符号的值

整数(integer)

整数是一种通用的寄存器数据类型,默认位宽为宿主机的字的位数,但最小应为12位。整形类型为有符号数。

integer counter;
initial
		counter = -1;

实数和时间寄存器

向量

向量的声明(declaring vector)

net和register类型的数据均可以声明为向量(位宽>1)

向量通过[high# :low#]或[low# :high#]进行说明8

wire [7:0] w;         // 8-bit wire
reg  [4:1] x;         // 4-bit reg
output reg [0:0] y;   // 1-bit reg that is also an output port (this is still a vector)
input wire [3:-2] z;  // 6-bit wire input (negative ranges are allowed)
output [3:0] a;       // 4-bit output wire. Type is 'wire' unless specified otherwise.
wire [0:7] b;         // 8-bit wire where b[0] is the most-significant bit.

<aside> ⚠️ 方括号左边的数总是代表最高有效位。

</aside>

向量域的选择

可以指定某一位和若干位,用法同C;

  • bus [ 2 : 0 ] //向量bus的最低3位

可变域选择

[<starting_bit>+:width]:从起始开始递增,位宽为width [<starting_bit> - : width]:从起始开始递减,位宽为width

reg [255 : 0]   data1; 
for ( j = 0;j <= 31; j = j + 1)
		byte = data1 [(j*8)+:8];//次序是[7:0],[15:8]……[255:148]

参数(parameter)

在Verilog HDL中用parameter来定义常量,即用parameter来定义一个标识符代表一个常量,称为符号常量,即标识符形式的常量,采用标识符代表的一个常量可以提高程序的可读性和可维护性。格式如下:

parameter 参数名1 = 表达式, 参数名2 = 表达式, 参数名3 = 表达式, ..., 参数名n = 表达式;其中,parameter是参数性数据的确认符。

值得注意的是,表达式中只能包含之前定义的参数或者常量数字,如下例:

parameter	msb = 7;									     //定义参数msb为常量7
parameter	e = 25, f = 29;								 //定义两个常量参数
parameter 	r = 5.7;									   //声明r为一个实型参数
parameter	hyte_size = 8, byte_msb = byte_size - 1;	//用常数表达式赋值
parameter	average_delay = (r + f) / 2;	 //用常数表达式赋值

在模块或实例引用时,可通过参数传递改变在别引用模块或实例中已定义的参数,或者defparam(参数重载)语句改变参数值。

局部参数(localparam)

局部参数可以使用localparam,但其值不能改变,不能通过参数重载语句(defparam)或通过有序参数列表或命名参数赋值来直接修改。状态机编码是不能被修改的,故应定义为localparam

系统任务和编译指令

系统任务

$monitor

$display

编译指令

define 类似于C的#define结构,在编译阶段,当编译器遇到<宏定义名>时,使用预定义的文本宏进行互换。

`define WORD_SIZE 32;          //在代码中用`WORD_SIZE表示规定字长
`define S  $stop;              //定义别名,可以用`s来代替$stop

操作符

基本运算符

+、-、*、/、%、**(求幂)类似C

按位运算符

取反(~)、与(&)、或( | )、异或(^)、同或(^~,~^)

若两个操作数位宽不等,则使用0向左扩展较短的操作数。

<aside> 💡 注意和 x 有关的运算,0 * x = 0 ; 1 + x = 1 ; 异或同或均为x

</aside>

等价操作符

逻辑等(=)逻辑不等(!=)case等(===)case不等(!==)

逻辑等价操作符,若两个操作数有一个含有 x 或 z,则结果为 x ; case等价操作符,是对两个操作数严格进行比较,包括 x 和 z ,完全相等的情况下才为1。

关系操作符

拼接操作符

通过此操作符{}可以实现两个或以上信号的某些位拼接起来;

{信号 1 的某几位,信号 2 的某几位,.....}

位拼接还可以采用重复来简化表达式

//A = 1'b1 ;B = 2'b00 ;C = 2'b10 ;D = 3'b110
Y = { A , B , C , D , 3'b001};//11'b10010110001
Y = {4{A}}                   ;//4'b1111

缩减运算符

缩减与(&)、缩减与非( ~& )、缩减或( | )、缩减异或(~^,^~)、缩减或非(~ | )。缩减操作符只有一个操作符。它对向量操作数逐位(从左至右)运算。

// X = 4'b1010
&X //1 & 0 & 1 &0 结果为1'b0

连续赋值语句(数据流建模)

  • 总是处于激活状态:只要任意一个操作数发生变化,表达式就会被立即重新计算
  • 连续赋值语句的左值一定是一个标量或者向量线网,或者是标量或线网的拼接。
  • 操作数类似C语言,可以是向量或者标量或者是函数调用

Wire4 - HDLBits

When you have multiple assign statements, the order in which they appear in the code does not matter. Unlike a programming language, assign  statements ("continuous assignments") describe connections between things, not the action of copying a value from one thing to another.

assign {addr[15:0] ,sum [3:0]} = addr1_bits[15:0]^addr2_bits[15:0];
assign {c_out ,sum [3:0]} = a[3:0] + b[3:0] + c_in;

隐式线网声明

  • 如果一个信号名作为连续赋值函数的左值,Verilog认为其是一个隐式声明的线网
wire i1 , i2;
assign out = i1 & i2;//out是一个隐式声明的线网

隐式连续赋值

wire out ;
assign out = i1 & i2;

//使用隐式赋值实现上述两行代码
wire out = i1 & i2;

过程赋值语句(行为级建模)

  • 只有在执行的时候才起作用。

阻塞赋值(=)

Blocking Assignment:下一条语句的执行会被本条阻塞型赋值语句阻塞,只有在当前的阻塞型赋值语句执行完后,下条语句才会被执行。

在begin-end串行语句块中,将以他们的顺序块后排列次序一次得到执行; 先计算右边赋值表达式的值,然后立即将计算结果赋值给“=”左边被赋值量。

非阻塞赋值(<=)

Nonblocking Assignment

在begin-end的串行语句块中,一条非阻塞赋值语句的执行不会阻塞下一条语句的执行 先计算右边赋值表达式的值,等到仿真时间结束时在将该计算结果赋值变量。也就是说,这种情况下是在同一仿真时刻的其他操作完成时执行

a = 1;
b = 2;
c = 3;
initial
	begin
		a = b;
		c = a;
	end
//result:a=b=c=2
a = 1;
b = 2;
c = 3;
initial
	begin
		a <= b;
		c <= a;
	end
//result:a=b=2,c=1

⚠️数值交换

//阻塞赋值
always @(posedge)
	begin
		temp_a = a;
		temp_b = b;
		a = temp_a;
		b = temp_b;
	end
//先读后写
//非阻塞赋值
always @(posedge)
	a <= b;
always @(posedge)
	b <= a;
//若此处将 <= 改成 = ,会造成竞争

🤔三种赋值语句对比

There are three types of assignments in Verilog:

  • Continuous assignments (assign x = y;). Can only be used when not inside a procedure ("always block").
  • Procedural blocking assignment: (x = y;). Can only be used inside a procedure.
  • Procedural non-blocking assignment: (x <= y;). Can only be used inside a procedure.

In a combinational always block, use blocking assignments. In a clocked always block, use non-blocking assignments. A full understanding of why is not particularly useful for hardware design and requires a good understanding of how Verilog simulators keep track of events. Not following this rule results in extremely hard to find errors that are both non-deterministic and differ between simulation and synthesized hardware.

 ⚠️ A note on wire vs. reg: The left-hand-side of an assign statement must be a net type (e.g., wire), while the left-hand-side of a procedural assignment (in an always block) must be a variable type (e.g., reg). These types (wire vs. reg) have nothing to do with what hardware is synthesized, and is just syntax left over from Verilog's use as a hardware simulation language.

模块和端口

端口连接规则

端口与外部信号的连接

顺序端口连接

module fulladd4( sum , c_out , a , b , c_in);
endmodule
fulladd4 fa_ordered(SUM , C_OUT , A , B , C_IN );//调用(实例引用)fulladd4命名为fa_ordered

命名端口连接

module fulladd4( sum , c_out , a , b , c_in);
endmodule
fulladd4 fa_byname(.sum(SUM) , .c_out(C_OUT) , .a(A) , .b(B), .c_in(C_IN))
//调用(实例引用)fulladd4,命名为fa_byname

门级建模

  • 在使用门级原语时,我们可以不指定具体实例的名字
  • 输入端口(与或门)、输出端口(缓冲器、非门)可以超过两个,这时Verilog会自动根据端口数目选择合适的门

基本门的实例引用

and a1(OUT, IN1, IN2);
nand na1(OUT, IN1, IN2);
or or1(OUT, IN1, IN2);
nor nor1(OUT, IN1, IN2);
xor x1(OUT, IN1, IN2);  //异或
xnor nx1(OUT, IN1, IN2);
buf b1(OUT1, OUT2, IN);
not n1(OUT1, IN);

实例数组

简化对某种类型的门多次调用

wire [7:0] OUT, IN1, IN2;
nand n_gate[7:0](OUT, IN1, IN2);

<aside> ⚠️ 调用用户定义模块的实例时必须指定名字!

</aside>

结构化过程语句

是行为建模的基本语句,其他行为语句只能出现在这两种结构化语句中

initial语句

  • 从仿真0时刻开始运行
  • 若一个模块中含有若干个initial块,则这些快块时刻开始并发执行,且每个块的执行是各自独立的
  • 如果在块内包含了多条行为语句,那么需要将这些语句组成一组,一般式使用关键字begin和end将他们组合在一个块语句
module stimulus;
 
reg x,y, a,b, m
 
initial
	m = 1'b0;
	
initial
begin
	#5 a = 1'b1;
	#25 b = 1'b0;
end
 
initial
begin
	#10 x = 1'b0;
	#25 y = 1'b1;
end
 
initial
	#50 $finish;
	
endmodule

例子中,三条initial语句在仿真0时刻开始并行执行

时间 所执行的语句 0 m = 1'b0; 5 a = 1'b1; 10 x = 1'b0; 30 b = 1'b0; 35 y = 1'b1; 50 $finish;

always语句

  • 从仿真的0时刻开始顺序执行其中的行为语句,类似C中的while(1)
  • 真实地反映了硬件电路在通电以后连续反复执行
module clock_gen (output reg clock);
//建立时钟发生器的一种方法
initial
	clock = 1'b0;
always
	#10 clock = ~clock;
initial
	#1000 $finish;
endmodule

延时

门延时

连续赋值语句的延时

普通赋值延时59

隐式连续赋值

线网声明延时

wire #10 out = in1 & in2;

//等效于
wire out;
assign #10 out = in1 & in2;
wire #10 out = in1 & in2;
assign out = in1 & in2;
//等效于
wire out;
assign #10 out = in1 & in2;

时序控制

基于延时

💡 延时值可以是数字、标识符、表达式(unsigned_number | parameter_identifier | specparam_identifier | mintypmax_expression)

常规延时控制

遇到常规时延时,语句需要等待一定时间,然后将计算结果赋值给目标信号。

#10 procedural_statement //10个单位时间后,再执行后面的语句
#10 ;                    //单独写一句,表示延时10个时间单位;

内嵌延时

遇到内嵌延时时,该语句先将计算结果保存,然后等待一定的时间后赋值给目标信号。

value_embed = #10 value_test ;//内嵌时延控制加在赋值号之后;

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值