【VerilogHDL】模块

【VerilogHDL】模块

  • 模块基本语法
  • 2选1多路选择器实例
  • 模块描述方式
    • 行为级或算法级的描述方式(行为级建模)
      • 4bit 的二进制行波计数器(带进位)
    • 数据流描述方式(数据流级建模)
    • 门级描述方式(门级建模)
    • 开关级描述方式(开关级建模)
    • 描述方式总结
  • 设计的仿真与测试
  • 模块的调用

模块基本语法

Module<模块名>(<端口列表>)
	端口说明
	参数定义
	数据类型定义
	连续赋值语句(assign)
	过程块(initial和always)
		-行为描述语句
	底层模块实例
	任务和函数
	延时说明块
endmodule

2选1多路选择器实例

【VerilogHDL】模块_第1张图片

module mux2_1(out,a,b,sel);		//端口定义
	output out;					//输入输出列表
	input a,b,sel;
	
	not i5(sel_n,sel);			//结构描述
	and i6(sel_a,a,sel);
	and i7(sel_b,sel_n,b);
	or i8(out,sel_a,sel_b);
endmodule

下面逐行解释上例代码:

module mux2_1(out,a,b,sel);		//端口定义

声明模块名及其端口列表。

output out;

指定端口out的方向为输出(output),output是用于声明端口方向的一个Verilog关键字。

input a,b,sel;

指定端口a,b,sel的方向为输入(input)。

not i5(sel_n,sel);			//结构描述

生成一个Verilog内建基本门级元件not(非门)的实例(也叫做模块的调用,类似于C语言中的函数调用),该实例名为i5。第一个端口sel_n是输出端口,信号sel连接到该not元件的输入端口。

and i6(sel_a,a,sel);
and i7(sel_b,sel_n,b);

生成Verilog内建基本门级元件and(与门)的两个实例,实例名分别是i6和i7。

or i8(out,sel_a,sel_b);

生成Verilog内建基本门级元件or(或门)的实例i8。

endmodule

用关键字endmodule示意模块结束。

模块描述方式

模块的描述方式又称为建模方式。大致可以按以下四类抽象级别来进行描述。

行为级或算法级的描述方式(行为级建模)

这是VerilogHDL最高抽象级别的描述方式。行为描述是通过行为语句来实现的,行为功能可使用下述过程语句结构描述。

  • initial语句:此语句只执行一次。
  • always语句:此语句循环执行。

只有寄存器类型数据能够在这两种语句中被赋值。寄存器型数据在被赋新值之前保持原有值不变。所有的 initial 语句和 always 语句在零时刻并发执行。

4bit 的二进制行波计数器(带进位)

【VerilogHDL】模块_第2张图片

module cnt_4bit(q, clear, clock);
	output [3:0] q;
	input clear, clock;

	reg [3:0] q;
	always @(posedge clear or negedge clock)
	begin
		if (clear)
			q = 4'd0;
		else
			q = (q + 1) % 16;
	end
endmodule

模块 cnt_4bit 的输出端口 q 是一个 4bit 的位矢量,代表 4 根输出端口线 q[3],q[2],q[1],q[0]。由于该输出端口要在always语句中被赋值,所以它被定义为 reg 型(寄存器型)数据。
clear,输入端口,清零端。
clock,输入端口,时钟信号。
always语句中包含一个或事件控制,以及相关联的顺序过程(begin-end对)。此或事件控制的作用是当输入端口clear、clock上发生事件,即clear上升沿(posedge)到来或clock下降沿(negedge)到来时,就执行下面的顺序过程。
顺序语句执行完后被挂起,always语句再次等待clear、clock的值发生变化。

数据流描述方式(数据流级建模)

也称为RTL(寄存器传输级)描述方式。在这种描述方式下,设计者需要知道数据是如何在寄存器之间传输的以及将被如何处理。
在Verilog中数据流描述方式主要用来描述组合逻辑,具体由连续赋值语句“assign”来实现。下面仍以 4bit 二进制行波计数器为例,根据自顶向下(Top-Down)的设计方法,用数据流描述的方式来实现它。

第一步,设计顶层模块 cnt_4bit_1 ,代码包含了4个T触发器模块T_ff实例(模块调用)。

module cnt_4bit_1 (q, clear, clock);
	output [3:0] q;
	input clear,clock;

	T_ff tff0(q[0], clear, clock);
	T_ff tff1(q[1], clear, q[0]);
	T_ff tff2(q[2], clear, q[1]);
	T_ff tff3(q[3], clear, q[2]);
endmodule

第二步,设计T_ff触发器模块,该模块内又包含了其下一层的D触发器模块 edge_dff 实例。

module T_ff(q, clear, clock);
	output q;
	input clear, clock;

	edge_dff ff1(q, , ~q, clear, clock);
endmodule

数据流操作符"~"代表对信号q取反,与Verilog内建基本门级元件not实现功能相同。

edge_dff ff1(q, , ~q, clear, clock);

其中的空格表示默认调用。
第三步,运用数据流描述语句设计最底层模块负边沿触发D触发器 edge_dff。
【VerilogHDL】模块_第3张图片

module edge_dff(q, qbar, d, clear, clock);
	output q, qbar;
	input d, clear, clock;

	wire s, sbar, r, rbar, cbar;
//输入锁存器:锁存器是电平敏感的。而一个边沿敏感的触发器需要使用3个RS锁存器来实现
assign sbar = ~(rbar & s),
		s = ~(sbar & cbar & ~clock),
		r = ~(rbar & ~clock & s),
		rbar = ~(r & cbar & d);
//输出锁存
assign cbar = ~clear,
		qbar = ~(q & r & cbar);
endmodule

s、sbar、r、rbar、cbar被定义为 wire(连线)型数据,wire也是Verilog的关键字。可以理解为它们都是指导线。
在其后的代码中使用assign语句对模块的输入、输出端口和连线型数据之间的数据流传输关系进行了描述。

assign cbar = ~clear,

例如此行,可以理解为 cbar 导线上的电平为 clear 上的电平取反。体现到物理电路上,就如下图。
【VerilogHDL】模块_第4张图片

门级描述方式(门级建模)

在这种描述方式下,模块是按照逻辑门和它们之间的互连来体现的。具体来说,门级描述方式就是指调用Verilog内建的基本门级元件来对硬件电路进行结构设计。
仍以行波计数器为例,其第三步的模块edge_dff的设计可以采用门级描述的方式来实现。

module edge_dff_1 (q, qbar, d, clear, clock);
	output q, qbar;
	input d, clear, clock;
	wire cbar, clkbar, sbar, s, r, rbar;

	not N1(cbar, clear),
		N2(clkbar,clock);
	nand NA1(sbar, rbar, s),
		NA2(s, sbar, cbar, clkbar),
		NA3(r, s, clbar, rbar),
		NA4(rbar, r, cbar, d),
		NA5(q, s, qbar),
		NA6(qbar, q, cbar, r);
endmodule

内置门级元件有14种,分4类:

  • 多输入门:and(与)、nand(与非)、or(或)、nor(或非)、xor(异或)、xnor(同或)
  • 多输出门:buf(缓冲器)、not(非门)
  • 三态门:bufif0(低电平使能缓冲器)、bufif1(高电平使能缓冲器)、notif0(低电平使能非门)、notif1(高电平使能非门)
  • 上拉、下拉电阻:pullup、pulldown

具体定义即调用会在单独的文章中写到。

开关级描述方式(开关级建模)

也称为晶体管级描述方式,是Verilog最低抽象级别的描述方式。是调用Verilog内建的基本开关级元件来对硬件电路进行结构设计。
由于有了这一级别的描述方式,使得用户在MOS(Metal-Oxide Semiconductor,金属氧化物半导体)晶体管级别进行设计成为可能。
可以使用CMOS(互补金属氧化物半导体)设计nor门(或非门)。
【VerilogHDL】模块_第5张图片

module my_nor(out, a, b);
	output out;
	input a, b;
	wire c;

	supply1 pwr;
	supply0 gnd;
	pmos(c, pwr, b);
	pmos(out, c, a);
	nmos(out, gnd, a);
	nmos(out, gnd, b);
endmodule

程序中的supply1、supply0是Verilog的关键字,分别定义电路的电源和地。pmos、nmos都是Verilog的基本开关级元件。

描述方式总结

可以在同一个设计中混合使用这四种描述方式。从设计的成熟性上考虑,大多数模块都可以化为门级描述来实现。
通常来说,描述方式越抽象,设计的灵活性和技术独立性也越强,而越靠近开关级的描述方式,对技术的依赖性也越强,设计本身也就越不灵活,一个小改动就可能导致整个设计的大改变。

抽象程度:
行为级>数据流级>门级>开关级

灵活性:
行为级>数据流级>门级>开关级

设计的仿真与测试

一个设计一旦完成就应该对它进行测试。通过编写激励块,输入激励信号然后检测结果可以检测一个设计功能的正确性。

将激励块和设计块分开来是设计者应该养成的一个好习惯。

通常测试块也被称为测试凳(Test Bench),应用不同的测试凳可以对一个设计块进行全方位的测试。

激励信号的应用方式大致被分为两种:
,在激励块内调用设计块,并且直接驱动设计块的信号。
【VerilogHDL】模块_第6张图片
,在顶层的假模块内同时调用激励块和设计块,激励块和设计块仅通过接口相互作用。顶层模块的作用仅仅是为了调用设计块和激励块。
【VerilogHDL】模块_第7张图片
将行波计数器设计模块进行改写:

module cnt_4bit(q, clear, clock);
	output [3:0] q;
	input clear, clock;

	reg [3:0] q;
	
	always @(posedge clear or negedge clock)
	begin
		if (clear)
			q = 4'd0;
		else
			q = (q + 1) % 16;
	end
endmodule

编写激励块

module stimulus1;
reg clk;
reg reset;
wire [3:0] q;
cnt_4bit r1(q, reset, clk);		//调用设计块cnt_4bit生成实例r1
//控制信号clk以驱动设计块,时钟周期设为10个时间单位
initial
	clk = 1'b0;			//设置clk到0
always
	#5 clk = ~clk		//clk每隔5个时间单位反转一次
//控制复位信号reset以驱动设计块,0~15为高,15~195为低,195~205为高,然后变低
initial
begin
	reset = 1'b1;
	#15 reset = 1'b0;
	#180 reset = 1'b1;
	#10 reset = 1'b0;
	#20 $finish;
end
//监视输出
initial
	$monitor($time,"output q = %d", q);
endmodule

接下来就可以运行仿真器,以检测设计块功能的正确性。

模块的调用

指从模块模板生成实际的电路结构对象,这样的电路结构对象被称为模块实例,模块调用也被称为实例化。

每一个实例都有它自己的名字、变量、参数和I/O接口。

被复制的电路块将一直存在,因为硬件电路结构不会随着时间而发生变化。

Verilog中,模块不能被嵌套定义,但可以包含其他模块的拷贝,即实例。

模块调用语句的基本格式:
<模块名><参数值列表><实例名> (<端口连接表>);

参考文献:
1.《精通Verilog HDL语言编程》,刘 波 编著,电子工业出版社,2007年5月第一次印刷。

你可能感兴趣的:(VerilogHDL)