Verilog基础语法

前言

        Verilog 是一种硬件描述语言,用于描述数字电路的行为和结构。它通过模块化的方式来组织设计,每个模块包含输入、输出端口和内部逻辑。Verilog 提供了丰富的语法结构,可以描述从简单的逻辑门到复杂的处理器架构的各种数字电路设计。对于其格式也有一定的规范(如下图,该实例为一简易分频器),下面对其语法进行具体介绍。

//计数器分频:偶数分频
module cnt_freq(

	input clk,//基础时钟为50MHz,即周期为20ps
	input rst_n,
	
	output reg signal//周期为2us,占空比为50%
);

reg [6:0] cnt;

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)//复位有效
		cnt <= 7'd0;
	else
		if(cnt < 49)//记50次20ps的clk,即1us
			cnt <= cnt + 7'd1;
		else
			cnt <= 7'd0;
end 

always @(posedge clk,negedge rst_n) 
begin
	if(rst_n == 0)
		signal <= 1'b1;
	else
		if(cnt == 49)//1us后使输出取反,再过1us后又取反,即signal一个周期为2us
			signal <= ~signal;
		else
			signal <= signal;
end

endmodule 

正文

一、Verilog 基本格式

1.写作习惯 

       Verilog 是区分大小写的。格式自由,可以在一行内编写(不推荐),也可跨多行编写(推荐)。每个语句必须以分号为结束符。空白符(换行、制表、空格)都没有实际的意义,在编译阶段可忽略。例如下面两中编程方式都是等效的。

always @(posedge clk,negedge rst_n) begin	if(rst_n == 0)		signal <= 1'b1;	else		if(cnt == 49)			signal <= ~signal ;		else signal <= signal; end
//将所有代码写一行,编辑器不会报错,但可读度下降。
always @(posedge clk,negedge rst_n) 
begin
	if(rst_n == 0)
		signal <= 1'b1;
	else
		if(cnt == 49)
			signal <= ~signal;
		else
			signal <= signal;
end
//换行,代码逻辑明了,方便查错和修改。

2.注释

        代码注释在编程中起着非常重要的作用,可以解释代码的意图,帮助他人和自己理解代码的逻辑思路。便于项目的维护和交接。良好的注释是一个程序员编写优质代码的重要组成部分。因此,在编程过程中,应该养成注释的良好习惯,注释要清晰、简洁、具有针对性,避免过多或冗长的注释,确保注释与代码保持同步更新。Verilog 中有 2 种注释方式:(在Quartus II 中对应注释的内容会变色)

        用 // 进行单行注释:

        用 /* 与 */ 进行跨行注释:

3.关键字

        由于Verilog 语法参照C语言设计,其标识符也具有一定C语言特点,标识符(identifier)可以是任意一组字母、数字、$ 符号和 _(下划线)符号的合,但标识符的第一个字符必须是字母或者下划线,不能以数字或者美元符开始。另外,标识符是区分大小写的。关键字是 Verilog 中预留的用于定义语言结构的特殊标识符。Verilog 中关键字全部为小写。下面介绍一些常用关键字(后续会补充):

module 模块名称模块开始定义
input 端口名称输入端口定义
output 端口名称输出端口定义
inout 端口名称双向端口定义
parameter 信号名称 信号的参数定义
wire 信号名称信号定义
reg 信号名称信号定义
always  @(敏感信号)产生reg信号语句的关键字
assign 逻辑式产生wire信号语句的关键字
begin 语句的起始标志
end语句的结束标志
posedge 上升沿检查信号 /negedge 下降沿检测信号 时序电路的标志
case(多分枝判断目标)CASE语句起始标记
default :逻辑式语句的默认分支标志
endcase语句结束标记
if(判断式) if/else  end判断语句标记
else if(判断式)/else   选择语句标记
fo(初始条件;终止条件;增减计数)循环语句标记
endmodule模块结束定义
initial模拟器开始执行

4.结构

        模块定义必须以关键字 module 开始,以关键字 endmodule 结束。模块名,端口信号,端口声明和可选的参数声明等,出现在设计使用的 Verilog 语句(图中Declarations_and_Statements)之前。模块内部有可选的 5 部分组成,分别是变量声明,数据流语句,行为级语句,低层模块例化及任务和函数,如下图表示。这 5 部分出现顺序、出现位置都是任意的。但是,各种变量都应在使用之前声明。变量具体声明的位置不要求,但必须保证在使用之前的位置。
 

二、数据类型与数值表示

1.数值表示

        Verilog HDL 有下列四种基本的值来表示硬件电路中的电平逻辑:

0逻辑”0“或假
1逻辑”1“或真
X或x未知
Z或z高祖

        x 意味着信号数值的不确定,即在实际电路里,信号可能为 1,也可能为 0。

        z 意味着信号处于高阻状态,常见于信号(input, reg)没有驱动时的逻辑结果。例如一个 pad 的 input 呈现高阻状态时,其逻辑值和上下拉的状态有关系。上拉则逻辑值为 1,下拉则为 0 。

        合法的基数格式有 4 中,包括:十进制('d 或 'D),十六进制('h 或 'H),二进制('b 或 'B),八进制('o 或 'O)。数值可指明位宽,也可不指明位宽(默认为十进制表示)。

//整数表示
4'b1011         // 4bit 数值,表示二进制1011
32'h3022_c0de   // 32bit 的数值 表示十六进制是的3022_c0de
//下划线 _ 是为了增强代码的可读性。
'd100           //会根据编译器自动分频位宽,常见的为32bit

-6'd15          //负数表示
//-15 在 5 位二进制中的形式为 5'b10001, 
//在 6 位二进制中的形式为 6'b11_0001。需要注意的是,减号放在基数和数字之间是非法的。

//实数表示
30.123
1.2e4         //大小为12000
1E-3          //大小为0.001

//字符串表示
reg [0: 14*8-1]       shiyan;
initial 
begin
    shiyan= "asdfghjklqwert";
end

2.数据类型

        2.1线网(wire)

        wire 类型表示硬件单元之间的物理连线,由其连接的器件输出端连续驱动。如果没有驱动元件连接到 wire 型变量,缺省值一般为 "Z"。

wire   interrt ;
wire   gccc = 1'b0 ;
        2.2寄存器(reg)

        寄存器(reg)用来表示存储单元,它会保持数据原有的值,直到被改写。声明举例如下:

reg    clk;
reg    flag;
        2.3向量

        当位宽大于 1 时,wire 或 reg 即可声明为向量的形式。例如:

reg [3:0]      counter ;    //声明4bit位宽的寄存器counter
wire [32-1:0]  gpio;        //声明32bit位宽的线型变量gpio
wire [8:2]     addr ;       //声明7bit位宽的线型变量addr,位宽范围为8:2
reg [0:31]     data ;       //声明32bit位宽的寄存器变量data, 最高有效位为0

        对于上面的向量,我们可以指定某一位或若干相邻位,作为其他逻辑使用。Verilog 支持可变的向量域选择,Verillog 还支持指定 bit 位后固定位宽的向量域选择访问。

        [bit+: width] : 从起始 bit 位开始递增,位宽为 width。

        [bit-: width] : 从起始 bit 位开始递减,位宽为 width。

wire [9:0]     data_low = data[0:9] ;//data_low[9]=data[0]......
addr_temp[3:2] = addr[8:7] + 1'b1 ;//把addr的第7、8位数据加一后给到addr_temp3、4位

reg [31:0]     data1 ;
reg [7:0]      byte1 [3:0];
integer j ;
always@* begin
    for (j=0; j<=3;j=j+1) begin
        byte1[j] = data1[(j+1)*8-1 : j*8]; 
        //把data1[7:0]…data1[31:24]依次赋值给byte1[0][7:0]…byte[3][7:0]
    end
end

//下面 2 种赋值是等效的
A = data1[31-: 8] ;
A = data1[31:24] ;

//下面 2 种赋值是等效的
B = data1[0+ : 8] ;
B = data1[0:7] ;
//对信号重新进行组合成新的向量时,需要借助大括号。
wire [31:0]    temp1, temp2 ;
assign temp1 = {byte1[0][7:0], data1[31:8]};  //数据拼接
assign temp2 = {32{1'b0}};  //赋值32位的数值0
        2.4整数,实数,时间寄存器变量

        整数,实数,时间等数据类型实际也属于寄存器类型。整数(integer)整数类型用关键字 integer 来声明。声明时不用指明位宽,位宽和编译器有关,一般为32 bit。reg 型变量为无符号数,而 integer 型变量为有符号数。实数用关键字 real 来声明,可用十进制或科学计数法来表示。实数声明不能带有范围,默认值为 0。如果将一个实数赋值给一个整数,则只有实数的整数部分会赋值给整数。Verilog 使用特殊的时间寄存器 time 型变量,对仿真时间进行保存。其宽度一般为 64 bit,通过调用系统函数 $time 获取当前仿真时间。

reg [31:0]      data1 ;
reg [3:0]       byte1 [7:0]; //数组变量,后续介绍
integer j ;  //整型变量,用来辅助生成数字电路
always@* begin
    for (j=0; j<=3;j=j+1) begin
        byte1[j] = data1[(j+1)*8-1 : j*8]; 
        //把data1[7:0]…data1[31:24]依次赋值给byte1[0][7:0]…byte[3][7:0]
        end
end

real        data1 ;
integer     temp ;
initial begin
    data1 = 2e3 ;
    data1 = 3.75 ;
end
 
initial begin
    temp = data1 ; //temp 值的大小为3
end

time       current_time ;
initial begin
       #100 ;
       current_time = $time ; //current_time 的大小为 100
end
        2.5数组

        在 Verilog 中允许声明 reg, wire, integer, time, real 及其向量类型的数组。数组维数没有限制。线网数组也可以用于连接实例模块的端口。数组中的每个元素都可以作为一个标量或者向量,以同样的方式来使用,形如:<数组名>[<下标>]。对于多维数组来讲,用户需要说明其每一维的索引。

integer          flag [7:0] ; //8个整数组成的数组
reg  [3:0]       counter [3:0] ; //由4个4bit计数器组成的数组
wire [7:0]       addr_bus [3:0] ; //由4个8bit wire型变量组成的数组
wire             data_bit[7:0][5:0] ; //声明1bit wire型变量的二维数组
reg [31:0]       data_4d[11:0][3:0][3:0][255:0] ; //声明4维的32bit数据变量数组



flag [1]   = 32'd0 ; //将flag数组中第二个元素赋值为32bit的0值
counter[3] = 4'hF ;  //将数组counter中第4个元素的值赋值为4bit 十六进制数F,等效于counter[3][3:0] = 4'hF,即可省略宽度; 
assign addr_bus[0]        = 8'b0 ; //将数组addr_bus中第一个元素的值赋值为0
assign data_bit[0][1]     = 1'b1;  //将数组data_bit的第1行第2列的元素赋值为1,这里不能省略第二个访问标号,即 assign data_bit[0] = 1'b1; 是非法的。
data_4d[0][0][0][0][15:0] = 15'd3 ;  //将数组data_4d中标号为[0][0][0][0]的寄存器单元的15~0bit赋值为3
        2.6存储器

        存储器变量就是一种寄存器数组,可用来描述 RAM 或 ROM 的行为。

reg               membit[0:255] ;  //256bit的1bit存储器
reg  [7:0]        mem[0:1023] ;    //1Kbyte存储器,位宽8bit
mem[511] = 8'b0 ;                  //令第512个8bit的存储单元值为0
        2.7参数

        参数用来表示常量,用关键字 parameter 声明,只能赋值一次。

parameter      data_width = 10'd32 ;
parameter      i=1, j=2, k=3 ;
parameter      mem_size = data_width * 10 ;

参考资料

Verilog 数值表示

Verilog 数据类型

Verilog关键词

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

张明阳.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值