Verilog语法入门

Verilog语法入门

Verilog HDL是一种硬件描述语言(HDL:Hardware Description Language),以文本形式来描述数字系统硬件的结构和行为的语言,用它可以表示逻辑电路图、逻辑表达式,还可以表示数字逻辑系统所完成的逻辑功能。 Verilog HDL和VHDL是世界上最流行的两种硬件描述语言,都是在20世纪80年代中期开发出来的。前者由Gateway Design Automation公司(该公司于1989年被Cadence公司收购)开发。两种HDL均为IEEE标准。

Verilog可以从五个层次对电路(系统)进行描述,包括:系统级、算法级、寄存器传输级(即RTL级)、门级、开关级。我们平时用的最多的为RTL级,故Verilog代码也经常被称为RTL代码。

掌握一门语言最快速的方法就是在实际应用中去学习,Verilog HDL也不例外。下面我们就通过一系列实例来学习Verilog HDL

1. 简单组合逻辑电路

// module/endmodule 模块开始与结束
// example 模块名
// input/output 输入、输出
// assign 赋值操作(英文单词:分配)
// ~ NOT & AND | OR ^ XOR
module example(
input a,
input b,
input c,
output y);

assign y = ~a & ~b & ~c | a & ~b & ~c | a & ~b & c;

endmodule

2.多bit逻辑门

// [3:0]:表征该信号的位宽,实例中是推荐写法,[0:3]、[4:1]等写法也是合法的
// 代码注释://、/* */ ,增加代码可读性,//为单行注释,/* */为多行注释
module gates(
input [3:0] a,
input [3:0] b,
output [3:0] y1,
output [3:0] y2,
output [3:0] y3,
output [3:0] y4,
output [3:0] y5
);
	
	assign y1 = a & b;
	assign y2 = a | b;
	assign y3 = a ^ b;
	assign y4 = ~(a & b);
	assign y5 = ~(a | b);
	
endmodule

3.八输入与门

module and8(
input [7:0] a,
output      y
);
// & :按位与、归并与操作,如该操作符只有一个操作数时,则将该操作数的所有位进行相与操作
// 可以实现与注释部分相同的功能,但写法更简洁
assign y = &a;

endmodule
// a[7]:可将一个多位宽信号中的一位或多位以此种方式进行单独处理

4.一位全加器

module fulladder(
input a,
input b,
input cin,
output s,
output cout
);
wire p,g;

assign p = a ^ b;
assign g = a & b;

assign s = p ^ cin;
assign cout = g | (p & cin);

endmodule

// wire :线网型数据类型,verilog语法中的一种主要数据类型,用于表示线网型信号,与实际电路中的信号连线相对应
// wire是verilog中的默认数据类型,此例中的输入输出信号没有指定数据类型,则默认为wire型
// 除wire外,另外一种主要数据类型为reg,表示寄存器类型数据

5.三态门

module tristate(
input [3:0] a,
input      en,
output [3:0] y
);

assign y = en ? a : 4'bz

// z :高阻态,verilog中,信号共有4种状态"0、1、x、z",分别表示低电平、高电平、不确定态和高阻态。对于没有进行初始化的信号,一般处于不确定态(x),高阻态表示该信号没有被其他信号驱动,经常用于有多个驱动源的总线型数据上
// 4'bz :数据格式,表示该信号为4bit位宽,用二进制方式表示,对于数据格式的进一步说明,可见下表

6.八位两路选择器

// 第一种方式
module mux2(
input [7:0] d0,
input [7:0] d1,
input        s,
output [7:0] y
);
assign y = s ? d1 : d0;
endmodule

// 第二种方式
module mux2_8(
input [7:0] d0,
input [7:0] d1,
input        s,
output [7:0] y
);
mux2 lsbmux(d0[3:0],d1[3:0],s,y[3:0]);
mux2 msbmux(
.do  (d0[7:4]),
.d1  (d1[7:4]),
.s   (s),
.y   (y[7:4])
);
endmodule
//模块例化 :可通过实例化已经设计好的模块来达到重用模块,简化设计的目的。可将一个模块重用多次,
//在同一模块中,实例化名称(本例中为lsbmux、msbmux)可任意指定,但不能相同,也不能使用verilog中的关键字
//本例中列举了verilog语法支持的两种实例化方式,推荐使用第二种方式,虽然代码量增加了一些,但增加了可读性,
//同时降低了出错的风险。

//对于第一种模块例化方法,应严格保证实例化模块(lsbmux)中的参数排列顺序
//与被实例化模块(mux2)的参数排列顺序严格一致。

//对于第二种方式点后面是被例化模块(mux2)的接口信号,括号内的是实例化模块(msbmux)的接口信号。

7.D触发器

module flop(
input clk,
input [3:0] d,
input reg[3:0] q
);

always@(posedge clk)
	q <= d;
	
endmodule
// 时序逻辑 :电路具有记忆功能,电路状态不但与当前输入有关,还与前一时刻的状态有关。
// 同步逻辑 :在同一的时钟信号激励下工作,输出只在时钟的上升沿(或者下降沿)发生变化。
// reg :除wire类型外,另外一种常用的数据类型,一般表示寄存器类型数据,不过并不绝对,
// 记住一条原则:在always块内被赋值的信号应定义成reg型,用assign语句赋值的信号应定义成wire型。

//而always即能实现组合逻辑赋值,又能实现时序逻辑赋值操作,且可以包含多条赋值表达式,多条赋值表达式,
//则应位于begin/end对中间
//always@()括号里面是敏感信号。
//always@(posedge Clk)敏感信号是posedge Clk含义是在上升沿的时候有效。
//敏感信号还可以negedge Clk含义是下降沿的时候有效,
//这种形式一般时序逻辑都会用到。
//还可以是这个一符号,如果是一个则表示一直是敏感的,一般用于组合逻辑。

8.D触发器

module flopr(
input clk,
input reset,
input [3:0] d,
input reg[3:0] q
);

always@(posedge clk)
	if (reset)
		q <= 4'b0;
	else 
		q <= d;
endmodule
// 同步复位 :复位只能发生在在clk信号的上升沿,若clk信号出现问题,则无法进行复位。

9.异步复位电路

module flopren(
input clk,
input reset,
input en,
input [3:0] d,
input reg[3:0] q
);

always@(posedge clk,posedge reset)
	if (reset)
		q <= 4'b0;
	else if(en)
		q <= d;
endmodule		
// 异步复位 :在always的敏感变量列表中,包含了posedge clk(clk信号上升沿) 
// 和posedge reset(reset信号下降沿)
// 两个条件,只要有一个条件发生,便会执行always块内的逻辑。复位处理逻辑应具有最高的优先级。	

10.七段数码管

module sevenseg(
input  [3:0] data,
output [6:0] segments
);		
always@(*)
	case (data)
		
		0: segments = 7'b111_1110
		1: segments = 7'b111_1110
		2: segments = 7'b111_1110
		3: segments = 7'b111_1110
		4: segments = 7'b111_1110
		5: segments = 7'b111_1110
		6: segments = 7'b111_1110
		7: segments = 7'b111_1110
		8: segments = 7'b111_1110
		9: segments = 7'b111_1110
		default: segments = 7'b111_1110
	endcase
endmodule	

11.有限状态机(FSM)

module divideby3FSM(
input clk,
input reset,
output q
);
parameter s0 =2'b00
parameter s1 =2'b00
parameter s2 =2'b00

reg [1:0] state;
reg [1:0] nextstate;

always@(posedge clk,posedge reset)
	if(reset) state <= s0;
	else      state <= nextstate;

always@(*)
	case (state)
		s0:		nextstate = s1;
		s1:		nextstate = s2;		
		s2:		nextstate = s0;	
		default:nextstate = s0;
	endcase

assign q = (state == s0);
endmodule
// 其中,第一个always为FSM的第一段;第二个always块为FSM的第二段;
// 最后的assign语句为FSM的第三段。如有多个信号,可继续使用assign或always语句进行处理。		

12.参数传递

module mux2
#(parameter width = 8)
(
input [width-1:0] d0,d1,
input  				s,
output [width-1:0]  y
);
assign y = s ? d1 : d0;
endmodule
// 如要例化一8bit位宽的选择器,则使用默认参数即可,不需进行参数传递(默认值)
munx2 myMux(d0,d1,s,out);
// 如要例化一12bit位宽的选择器,则可使用如下方式,将参数传递过去
mux2 #(12) lowmux(d0,d1,s,out)

13.测试文件

// initial:通过initial块构造输入信号的波形,
// 同一initial块内部的语句是串行执行的,多个initial块之间并发执行。
// 模块的输入信号给定之后,就有有结果输出,将实际输出结果于预期结果做比较,如果不同,则打印出错误信息。

module testbench2();
reg a,b,c;
wire y;
sillyfunction dut(a,b,c,y);
initial begin
	a = 0;b = 0;c = 0;#10;
	if (y !== 1) $display("000 failed.");
	c = 1; #10;
	if (y !== 0) $display("001 failed.");
	b = 1;c = 1; #10;	
	if (y !== 0) $display("010 failed.");
	c = 1; #10;
	if (y !== 0) $display("011 failed.");
	a = 1;b = 0;c = 1; #10;
	if (y !== 0) $display("100 failed.");
	c = 1; #10;
	if (y !== 1) $display("101 failed.");
	b = 1;c = 0; #10;
	if (y !== 0) $display("110 failed.");
	c = 1; #10;
	if (y !== 0) $display("111 failed.");
end
endmodule

猜你喜欢

转载自blog.csdn.net/weixin_45355387/article/details/119142983