典型逻辑电路的Verilog-HDL描述
2012-11-06
常晓明 李媛媛

Verilog-HDL与CPLD/FPGA设计应用讲座

第 5 讲 典型基本逻辑路的Verilog-HDL描述

5.1 数据选择器

5.2 编码器

5.3 同步RS触发器

5.4 带有复位端的同步D触发器

本讲列举几个典型又简单的基本逻辑电路的Verilog-HDL描述。需要说明,仅有本讲的知识还不能具备用Verilog-HDL描述逻辑电路和系统的基本功,但却可以通过几个例子了解Verilog-HDL描述逻辑电路的过程和方法。

5.1 数据选择器

数据选择器又称为多路开关,简称MUX(Multiplexer)。它的逻辑功能是在地址选择信号SEL的控制下,从多路输入(A、B…)数据中选择某一路数据作为输出,一个2-1数据选择器的逻辑电路符号如图1所示。

图1 2-1数据选择器的逻辑符号

逻辑电路如图2所示,有关A、B及SEL的解释读者还可参考有关书籍(1)。

图2 2-1数据选择器的逻辑电路

例1 2-1数据选择器的Verilog-HDL描述

/* 2-1 SELECTOR */

module SEL ( A, B, SEL, F ); //模块名及端口参数,范围至endmodule。

inputA, B, SEL; //输入端口定义

outputF ; //输出端口定义

assign F = ~SEL & A | SEL & B; //assign语句,实现功能:F=(/SEL·A)+(SEL·B)。

endmodule //模块结束

例2 使用case语句的2-1数据选择器

/* 2-1 SELECTOR */

module SEL ( A, B, SEL, F );//模块名及参数定义,范围至endmodule。

input A, B, SEL;//输入端口定义

output F;//输出端口定义

assign F = SEL2_1_FUNC ( A, B, SEL );//用assign语句实现function函数调用

function SEL2_1_FUNC;//function函数及函数名,至endfunction为止。

input A, B, SEL;//输入端口定义

case ( SEL )//case语句,至于endcase为止。

0: SEL2_1_FUNC = A;//功能:SEL=0时,返回A。

1: SEL2_1_FUNC = B;//功能:SEL=1时,返回B。

endcase//case语句结束

endfunction//function函数结束

endmodule//模块结束

【解说】if_else语句的使用方法

if_else 语句用来先判定所给的条件是否满足,然后根据判定的结果(真或假)来执行所给出的两种操作之一。

例3 使用if_else语句的2-1数据选择器

/* 2-1 SELECTOR */

module SEL ( A, B, SEL, F );//模块名及参数定义,范围至endmodule。

input A, B, SEL;//输入端口定义

output F;//输出端口定义

assign F = SEL2_1_FUNC ( A, B, SEL ); //用assign语句实现function函数调用

function SEL2_1_FUNC;//function函数及函数名,至endfunction为止。

input A, B, SEL;//输入端口定义

if ( SEL == 0 )// if语句,与else配合使用。

SEL2_1_FUNC = A;//如果SEL= 0,则返回A。

else//if相呼应

SEL2_1_FUNC = B;//否则返回B

endfunction//function函数结束

endmodule//模块结束

【解说】function函数

function函数的目的是返回一个用于表达式的值 ,函数的形式为:

function <返回值位宽或类型说明> 函数名;

端口定义;

局部变量定义;

其他语句;

endfunction

在例2中,用到了function函数。

下面是2-1数据选择器的测试模块。

例4 2-1数据选择器的测试模块

/* 2-1 SELECTOR_TEST */

`timescale 1ns/1ns//将仿真的单位设定为 1ns

module SEL_TEST;//测试模块名及参数定义,范围至endmodule。

reg [1:0] IN;//寄存器定义,输入端口,数据宽度为2bit。

reg SEL_IN;//寄存器定义,输入端口。

wire F;//线网定义,输出端口。

SEL SEL ( IN[0], IN[1], SEL_IN, F ); //底层模块名、实例名及参数。

always #150 SEL_IN = ~SEL_IN;//每隔150ns,SEL_IN反相一次(注1)。

always #50 IN = IN + 1;//每隔50ns,IN = IN + 1(注2)。

initial begin//从initial始,输入信号变化。begin与end相呼应。

SEL_IN = 0; IN = 0;//一开始,SEL_IN = 0,IN = 0。

//之后,按(注1)、(注2)规律变化。

#450 $finish;//仿真到450ns时结束

end//与begin呼应

endmodule//模块结束

Verilog HDL 提供了多种编写测试模块的方法。上例中的 always 语句意指反复执行。例如"always #50 IN = IN + 1;"是说每隔一个仿真单位(在此为50ns)IN便加1;而"always #150 SEL_IN = ~SEL_IN;"是说每隔一个仿真单位(在此为150ns)SEL_IN便反转一次。在initial程序段中,将 "SEL_IN" 和 "IN" 初始化为0,并用"#450 $finish"标示仿真在450ns时结束。

图3给出了2-1数据选择器的仿真结果。通过该图我们可以验证用Verilog-HDL描述的数据选择器是否正确。例如:随机抽取其中的某个时刻(光标指向处)。此时,SEL_IN=0,IN[0]=0,IN[1]=1,F=0。符合数据选择器的功能。

图3 2-1数据选择器的仿真结果

5.2 编码器

在数字系统中,常需要将特定意义的信息编成若干二进制代码,这个过程称为编码,实现编码的数字电路称为编码器。

图4示出了一个2位二进制编码器的功能框图。它有4个输入端,即IN0、IN1、IN2和IN3,2个输出端Y0和Y1。在此,设图中的4个开关在同一时刻只有一个闭合,并规定开关处于打开状态时,对应的输入为逻辑"0",闭合时为逻辑"1"。

图4 二进制编码器的功能框图

上述二进制编码器的真值表如表1所示。

表1 二进制编码器的真值表

由此可知其逻辑表达式为:

Y0= IN1+ IN3

Y1= IN2+ IN3

由逻辑关系式便可以很容易地画出逻辑电路,如图5所示。由逻辑电路图可知,当Y0 和Y1均为逻辑"0"时,与输入IN0没有任何关系。因此,当4个开关都打开时,输出Y0 和Y1也为逻辑"0"。这在实际应用问题中是需要考虑的,在此不作进一步的讨论。

图5 二进制编码器的逻辑电路

例5 2位二进制编码器的Verilog-HDL描述

/* Data Difinision */

`define SW_IN0 4'b0001//编译时,将SW_IN0转换为4bit的二进制数0001

`define SW_IN1 4'b0010//编译时,将SW_IN0转换为4bit的二进制数0010

`define SW_IN2 4'b0100//编译时,将SW_IN0转换为4bit的二进制数0100

`define SW_IN3 4'b1000//编译时,将SW_IN0转换为4bit的二进制数1000

/* ENCORDER */

module ENC ( IN, Y );//模块名及参数定义,范围至endmodule。

input [3:0] IN;//输入端口定义

output [1:0] Y;//输出端口定义

assign Y = FUNC_ENC ( IN );//用assign语句实现function函数调用

function [1:0] FUNC_ENC;//function函数及函数名,至endfunction为止。

input [3:0] IN;//输入端口定义

case ( IN )//case语句,至于endcase为止。

`SW_IN0: FUNC_ENC = 0;//当IN= SW_IN0时,返回0。

`SW_IN1: FUNC_ENC = 1;//当IN= SW_IN1时,返回1。

`SW_IN2: FUNC_ENC = 2;//当IN= SW_IN2时,返回2。

`SW_IN3: FUNC_ENC = 3;//当IN= SW_IN3时,返回3。

endcase//case语句结束

endfunction//function函数结束

endmodule//模块结束

例6 2位二进制编码器的测试模块

`timescale 1ns/1ns

/* ENCORDER_TEST */

module ENC_TEST;//测试模块名及参数定义,范围至endmodule。

reg [3:0] IN;//寄存器定义,输入端口,数据宽度为4bit。

wire [1:0] Y;//线网定义,输出端口,数据宽度为2bit。

integer i,j;//定义i、j为抽象型整形数。

ENC ENC ( IN, Y );//底层模块名、实例名及参数。

initial begin//从initial始,输入信号变化。begin与end相呼应。

j = {2'b10, 2'b0, 1'b1};//位拼接运算,意为10与00与1拼接。

for ( i = 0; i <= 3; i = i + 1 )//循环,在begin与end之间。

begin// begin与end相呼应

j=j>>1;//右移1位

IN = j[3:0];//代入

#200;//经过200ns

end//与begin呼应

$finish;//停止运行

end//与begin呼应

endmodule//模块结束

2位二进制编码器的的仿真结果如图6所示,IN表示编码器的输入,Y表示输出。通过观察,可以得到仿真结果和前述设计的2位二进制编码器完全一致。即:当IN为"1000、0100、0010、0001"时,Y对应为"11、10、01、00"。

图6 2位二进制编码器的仿真结果

5.3 同步RS触发器

同步RS触发器有一个时钟端CLK。只有在时钟端的信号电平发生上跳(正触发)或下跳(负触发)时,触发器的输出才发生变化。

图7为正触发型同步RS触发器的逻辑符号。R和S为输入端,CLK为时钟端,上升沿触发有效,Q和 是互为反相的输出端。

图7 正触发型同步RS触发器的逻辑符号

图8为正触发型同步RS触发器的时序图。从图中可以看出,只有当时钟脉冲CLK的上升沿到来时电路的输出状态才有可能发生变化,而变化与否又取决于R端和S端。R端和S端均为低电平时,输出保持电路原有状态(在图8中,原有状态为"不定");R端和S端分别为高电平和低电平时,输出Q为低电平;R端和S 端分别为低电平和高电平时,输出Q为高电平;R端和S端均为高电平时,输出为"不定"。在实际应用中应避免这种R端和S端均为高电平的情况出现。

图8 正触发型同步RS触发器的时序图

例7 同步RS触发器的Verilog-HDL描述

/* SY_RS_FF */

module SY_RS_FF ( R, S, CLK, Q, QB ); //模块名及参数定义,范围至endmodule。

input R, S, CLK;//输入端口定义

output Q, QB;//输出端口定义

reg Q;//寄存器定义

assign QB = ~Q;//assign语句,QB=/Q。

always @( posedge CLK )//在CLK的上跳沿,执行以下语句。

case ({ R ,S })//case语句,至于endcase为止。

1:Q <= 1;//当R,S的组合为01,则令Q=1。

2:Q <= 0;//当R,S的组合为01,则令Q=1。

3:Q <= 1'bx;//当R,S的组合为11,则令Q为1bit的数,数值为不定(x)。

endcase//case语句结束

endmodule//模块结束

例8 同步RS触发器的测试模块

/* SY_RS_FF_TEST */

`timescale 1ns/1ns//将仿真的单位设定为 1ns

module SY_RS_FF_TEST;//测试模块名及参数定义,范围至endmodule。

reg R, S, CLK;//寄存器定义,输入端口。

wire Q, QB;//线网定义,输出端口。

Parameter STEP = 40;//定义STEP为40,在以后,凡STEP都视为40。

SY_RS_FF SY_RS_FF ( R, S, CLK, Q, QB );//底层模块名、实例名及参数。

always #( STEP/2 ) CLK = ~CLK;//每隔STEP/2,CLK就反转一次。

initial begin//从initial始,输入信号变化。begin与end相呼应。

CLK = 1; R = 0; S = 0;//参数初始化

#( 2*STEP-10 ) R = 1;//在经过( 2*STEP-10 )后,R = 1。

#( STEP/2 ) R = 0;//在经过( STEP/2 )后,R = 0。

#( STEP/2 ) S = 1;//在经过( STEP/2 )后,S = 1。

#STEP R = 1;//在经过STEP后,R = 1。

#( STEP/2 ) S = 0;//在经过( STEP/2 )后,S = 0。

#STEP R = 0;//在经过STEP后,R = 0。

#STEP $finish;//在经过STEP后,停止运行。

end//与begin呼应

endmodule//模块结束

图9为同步RS触发器的仿真结果。由图可以看出,当R端和S端均为相同电平时,输出波形为"不变"或"不定"。

图9 同步RS触发器的仿真结果

5.4 带有复位端的同步D触发器[To top]

在同步D触发器的实际应用中,有时需要有一个非同步的复位端。这种D触发器的逻辑符号如图10所示。其时序图如图11所示。

图10 带有复位端的同步D触发器的逻辑符号

图11 带有复位端的同步D触发器的时序图

从图11可以看出,在初始状态下,电路的逻辑处于"不定"状态,复位脉冲的到来将电路初始化为Q=0的状态。随后,在D端的控制下,电路的状态作相应的翻转。图中,t1-t6时刻说明了电路的逻辑状态。

带有复位端的同步D触发器的Verilog-HDL描述

例9 带有R端D触发器的Verilog-HDL描述

/* R_SY_D_FF */

module R_SY_D_FF ( RB, D, CLK, Q, QB );//模块名及参数定义,范围至endmodule。

input RB, D, CLK;//输入端口定义

output Q, QB ;//输出端口定义

reg Q;//寄存器定义

assign QB = ~Q;//assign语句,QB=/Q。

always @( posedge CLK or negedge RB ) //如果CLK端有上跳或RB端有下跳脉冲,

//则执行下面(Q <= ( !RB )? 0: D;)的语句。

Q <= ( !RB )? 0: D;//如果RB是低电平,则Q=0,否则Q=D。

endmodule//模块结束

例10 D触发器的测试模块

`timescale 1ns/1ns//将仿真的单位设定为 1ns

module R_SY_D_FF_TEST;//模块名及参数定义,范围至endmodule。

reg RB, D, CLK;//寄存器定义,输入端口。

wire Q, QB;//线网定义,输出端口。

parameter STEP = 50; //定义STEP为50,在以后,凡STEP都视为50。

R_SY_D_FF R_SY_D_FF ( RB, D, CLK, Q, QB ); //底层模块名、实例名及参数。 always #( STEP/2 )CLK = ~CLK;//每隔STEP/2,CLK就反转一次。

initial

begin//从initial始,输入信号变化。begin与end相呼应。

RB = 1; D = 0; CLK = 0;//参数初始化

#( STEP/4 ) RB = 0;//在经过( STEP/4 )后,RB = 0。

#( STEP*3/4 ) D = 1;//在经过( STEP*3/4 )后,D = 1。

#( STEP/4 ) RB = 1;//在经过( STEP/4 )后,RB = 1。

#( 2*STEP ) D = 0;//在经过( 2*STEP )后,D = 0。

#STEP D = 1;//在经过STEP后,D = 1。

#( STEP/2 ) RB = 0;//在经过( STEP/2 )后,RB = 0。

#( STEP/2 ) RB = 1;//在经过( STEP/2 )后,RB = 1。

#( STEP/4 ) $finish;//在经过STEP后,停止运行。

end//与begin呼应

endmodule//模块结束

图12示出了带有复位端的同步D触发器的仿真结果。RB是复位信号,当RB="0"时,Q="0"。从图中可以看出:在复位信号RB的作用下,Q="0"。其后,RB="1"。当CLK的上跳沿到来时,Q就变化为和D相同的状态;上跳沿以外的其它时刻,Q保持状态不变。

图12 带有复位端的同步D触发器的仿真结果

参考书

(1) John M. Yarbrough著,李书浩等译:数字逻辑应用与设计, 机械工业出版社,北京,pp141-145, 2000.4.

(2) 夏宇闻:复杂数字电路与系统的Verilog HDL设计技术,北京航空航天大学出版社,1998.

(3) 常晓明:Verilog-HDL实践与应用系统设计,北京航空航天大学出版社, 2003.1.

可能会用到的工具/仪表
本站简介 | 意见建议 | 免责声明 | 版权声明 | 联系我们
CopyRight@2024-2039 嵌入式资源网
蜀ICP备2021025729号