[2021-07-18]Verilog HDL语法总结

目录

1.引言

2.模块(block)

3.常量、数据类型、运算符

(1)常量

        1)数字

        2)x和z值

(2)数据类型

        1)wire型

        2)reg型

        3)参数型

(3)运算符

        1)算术运算符

        2)赋值运算符

        3)关系运算符

        4)逻辑运算符

        5)

条件运算符

        6)等式运算符

        7)移位运算符

        8)拼接运算符

         9)指数

        10)缩减运算符

        11)运算符优先级排序

4.常用关键词 块语句 生成块(未完待续)

        (1)always 

        (2)initial

        (3)assign

        (4)条件语句if_else

        (5)case语句

        (6)顺序块begin-end

        (7)系统任务

        (8)预编译处理语句


1.引言

        本篇内容是本人对于学习Verilog HDL语法过程中的总结,我的预期是将内容写的细一点,但作为一个初学者难免有所纰漏亦或是逻辑问题,所以会对内容进行长时间的修正、调整和补充。

        首先,Verilog是什么?是一种硬件描述语言( HDL,Hardware Description Language)。

                   描述的是什么?描述的是数字电路或数字系统的模型。

        个人理解就是将数字电路(或系统)的模型,转化为编程语言的形式,将其描述出来。

        我们编的不只是程序,而是可以具象化将一块一块的电路构造好后,连接起来,共同实现所需功能。(我们编写的也可能会是我们的梦想)

        在这之中,模型也分为很多层次:

Verilog语言对应的模型类型
描述级别 介绍
1.系统级(system-level) 后续补充
2.算法级(algorithm-level) 后续补充

3.RTL级(register transfer level)

描述如何控制和处理各类数据在寄存器之间的流动。

4.门级(gate-level)

描述逻辑门之间的连接
5.开关级(switch-level) 描述三极管和储存节点之间的连接

以上1~3都属于行为描述,1~3中只有RTL与逻辑电路有明确的对应关系。

现在明确了 Verilog是可以描述出整个电路系统,用一个不太恰当的比喻,就像我们知道了用砖,水泥或者钢筋、混凝土可以搭出房子一样;而房子是一间一间,一层一层盖起来的,复杂电路系统则是基于模块,一个一个配合共同实现功能的,每一个模块又可以由一个个子模块构成。

        因此,下一节对模块进行简要介绍。

2.模块(block)

        我个人理解:模块是实现特定功能的基本单元;我承认这句话看起来像是一句废话,还是那个不太恰当的比喻,就好像盖房子,用砖和水泥给自己盖一个新家用于生活,那你屋子里至少需要有厨房,卧室,客厅,卫生间等等,这些特定功能的基本单元。

        模块如何用Verilog定义呢?

//模块定义 模块名
module <module name>(
        CLK,
        RSTB,
        ……
        DOUT);

input wire CLK;        //时钟
input wire RSTB;       //复位下降沿有效

……

output wire DOUT;      //输出信号


//参数
parameter WIDTH=8;

//模块内容
………………
………………

//实例化 实例化命名
example example_inst(
       .CLK(CLK),
       .RSTB(RSTB),
       .DOUT(DOUT)//

);

endmodule

本人目前接触到的模块有,加法器,选择器,触发器,译码器,FIFO,锁存器(这个慎用)等。

以下以一个选择器作为例子。实现功能:

模块有三个输入分别为 in_1,in_2,sel

一个输出out

输入sel为1 模块out = in_1输入的值;

输入sel为0 模块out = in_2。

RTL文件:

//模块开始 模块名
module mux2_1
(   //输入输出列表(注意,除了列表结尾,每行后面都是逗号)

    //类型 数据类型[位宽] 变量名,
	input wire [0:0] in_1, 	//input signal 1
	input wire 		 in_2,	//input signal 2
	input wire       sel ,	//select signal
	
	output reg       out	//output
    //这里out后面不加逗号 直接括号分号结束
);
/*定义模块方法2
module mux2_1(
        in_1,
        in_2,
        
        out
);
input wire [0:0] in_1; 	//input signal 1
input wire 		 in_2;	//input signal 2
input wire       sel;	//select signal

output reg       out;
*/

//模块内容
//always表示块,块内的内容一直运行,后面(*)表示触发条件,即块内的变量sel、out只要有改变,
//就会运行块内语句
always@(*)
	if(sel == 1'b1)//这里一手简单的判断语句
		out = in_1;
	else 	
		out = in_2;
//快乐的时间就是这么短暂,模块结束了
endmodule

Test_bench文件实现功能:

每隔10us就给RTL文件中的 in_1,in_2,sel,赋值一个二进制随机数0或者1;

可以通过quartus和modelsim联合仿真观察out输出有没有严格实现功能

//时间尺度  单位时间间隔/时间精度
`timescale 1us/1us
//模块开始  输入输出列表为空
module tb_mux2_1();
//定义寄存器型变量
reg		in_1;
reg 	in_2;
reg		sel;
//定义线型变量
wire 	out;
//初始化块,注意这个初始化初学阶段只能用于Test_bench之中跑仿真用,千万不能写在rtl中
initial
	begin
	in_1 <= 1'b0;
	in_2 <= 1'b0;
	sel  <= 1'b0;
	end
//always块没有触发条件就代表一直执行 #10表示延迟10*单位时间间隔的时间 
// %2表示除以2的余数    {$random}%2表示产生随机数除以2求余数 
//                                限定产生的随机数<2 即0和1
always  #10 in_1 <=	{$random} % 2;
always  #10 in_2 <=	{$random} % 2;
always  #10 sel  <=	{$random} % 2;
//
initial
	begin
	//            1e-9s	==  ns
		$timeformat(-6,0,"us",6);
    //$monitor(参数列表)用于监控和输出参数列表个参数的值
    //写法类似于C语言中的printf。
		$monitor("@time %t:in_1 = %b sel=%b out=%b",$time,in_1,in_2,sel,out);
	end

//实例化,将rtl文件和test_bench文件之间的变量连接起来,
//连接起来后就可以通过test_bench文件给rtl文件中的输入赋值,来测试模块功能能否实现
mux2_1	mux2_1_inst
(
	.in_1(in_1),	//input signal 1
	.in_2(in_2),	//input signal 2
	.sel(sel),		//select signal
	.out(out)	//output
);
//模块结束
endmodule

以上代码是跟哔哩哔哩上野火FPGA 火哥学的,他的视频讲得很细,网址我附在结尾,如果以上代码或其他内容侵权请联系我,我删。

对了,鲁迅先生说:每个文件内最好只写一个模块,一种功能。

3.常量、数据类型、运算符

(1)常量

        1)数字

表示形式

二进制整数:b或B

八进制整数:o或O

十进制整数:d或D

十六进制整数:h或H

直接举例:

位宽'进制+数字
//例如
8'b0001_0010;//8位二进制数 下划线是为了方便看,不影响实际值,等价于8'b00010010
//注意下划线只能放在数字部分的中间如16'b0001_0000_0010_0100
//不能放在进制和数字之间,这样是错误的,如8'b_00010000
8'h80;//8位十六进制数 转换为二进制为1000_0000;

        2)x和z值

在数字电路中,x代表不定值,z代表高阻态值。

这个我还没用过,就不过多介绍,先放在这里,若果后续有接触到我会进行补充。

(2)数据类型

        就我现在所学,目前常用的就是wire型,reg型以及parameter型。(如果后面我所学有涉及到其他的我会补充)

        1)wire型

格式:

wire [位宽范围] 变量1,变量2……;
//例如
wire [3:0] a,b;//定义位宽为4的 两个wire型变量 a和b。
wire [0:0] c;//定义位宽为1的 一个wire型变量 c。
wire       d;//定义位宽为1的 一个wire型变量 d。

 
  • wire型数据大多用来表示用assign关键字指定的被赋值的信号;
  • 在模块中,输入输出信号没有特别定义的话,默认为wire型;
  • wire型信号可以用做任何方程式的输入,也可以用作assign语句或实例化元件的输出;
  • 如果不指定位宽的话,默认为1。

        2)reg型

格式1:

reg [位宽范围] 变量1,变量2……;
//例如
reg [4:1] a,b;//定义位宽为4的 两个reg型变量 a和b。
reg [0:0] c;//定义位宽为1的 一个reg型变量 c。
reg       d;//定义位宽为1的 一个reg型变量 d。
reg [4:0] e;
always(posedge clk or negedge rst)begin
if(~rst) 
    e <= 4'b0;
else
    e <= {1,2,3,4};
end
//e[0]=4,e[1]=3,e[2]=2,e[3]=1;

 
  • reg型数据初始值默认为x,即不定值。
  • reg型数据大多用来表示用always块内的被赋值的信号,常代表触发器;
  • always块中,被赋值的每一个信号都必须为reg型;
  • 如果不指定位宽的话,默认为1。

格式2:用于定义memory型的变量

reg [位宽-1:0]  memory名  [深度-1:0];
例如:
parameter FIFO_WIDTH = 8;
parameter FIFO_DEPTH = 8;

reg [FIFO_WIDTH-1 : 0] FIFO [FIFO_DEPTH-1:0]; 

        3)参数型

parameter 参数名=参数值;
//例如
parameter FIFO_DEPTH = 8;//定义FIFO_DEPTH为8
parameter FIFO_WIDTH = 8;
  • parameter型数据常用于定义位宽,FIFO深度,以及延迟时间

(3)运算符

        1)算术运算符

算术运算符(parameter a=8,  parameter b=2)
符号 使用示例 结果(十进制) 描述
+ a+b
  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值