VerilogHDL设计练习.docx
《VerilogHDL设计练习.docx》由会员分享,可在线阅读,更多相关《VerilogHDL设计练习.docx(29页珍藏版)》请在冰豆网上搜索。
VerilogHDL设计练习
VerilogHDL设计练习进阶
(一)
练习一•简单的组合逻辑设计
目的:
掌握基本组合逻辑电路的实现方法。
这是一个可综合的数据比较器,很容易看出它的功能是比较数据a与数据b,如果两个数据
相同,则给出结果1否则给出结果0。
在VerilogHDL中,描述组合逻辑时常使用assign结构。
注意equal=(a==b)?
1:
0,这是一种在组合逻辑实现分支判断时常使用的格式。
模块源代码:
//compare.v
modulecompare(equal,a,b);
inputa,b;
outputequal;
assignequal=(a==b)?
1:
0;//a等于b时,equal输出为1;a不等于b时,
//equal输出为0。
endmodule
测试模块用于检测模块设计得正确与否,它给出模块的输入信号,观察模块的内部信号和输出信号,如果发现结果与预期的有所偏差,则要对设计模块进行修改。
comparecompare1(.equal(equal),.a(a),.b(b));//endmodule
仿真波形(部分)
练习:
设计一个字节(8位)比较器。
要求:
比较两个字节的大小,如a[7:
0]大于b[7:
0]输出高电平,否则输出低电平,改写测试模型,使其能进行比较全面的测试。
VerilogHDL设计练习进阶
(二)练习二、简单时序逻辑电路的设计目的:
掌握基本时序逻辑电路的实现
>>>>
>>>在VerilogHDL中,相对于组合逻辑电路,时序逻辑电路也有规定的表述方式。
在可综合的VerilogHDL模型,我们通常使用always块和@(posedgeclk)或@(negedgeclk)的结构来表述时序逻辑。
下面是一个1/2分频器的可综合模型。
//half_clk.v:
modulehalf_clk(reset,clk_in,clk_out);
inputclk_in,reset;
outputclk_out;
regclk_out;
always@(posedgeclk_in)
begin
if(!
reset)clk_out=0;
elseclk_out=~clk_out;
end
endmodule
在always块中,被赋值的信号都必须定义为reg型,这是由时序逻辑电路的特点所决定的。
对于reg型数据,如果未对它进行赋值,仿真工具会认为它是不定态。
为了能正确地观察到仿真结果,在可综合风格的模块中我们通常定义一个复位信号reset,当reset为低电平时,对电路中的寄存器进行复位。
测试模块的源代码:
//clk_Top.v
'timescale1ns/100ps'defineclk_cycle50moduleclk_Top.vregclk,reset;
wireclk_out;
always#'clk_cycleclk=~clk;initial
begin
clk=0;
reset=1;
#100reset=0;
#100reset=1;
#10000$stop;
end
half_clkhalf_clk(.reset(reset),.clk_in(clk),.clk_out(clk_out));endmodule
仿真波形:
1i
忙lk_T
.dk..T
1
>>>
练习:
依然作clk_in的二分频clk_out,要求输出与上例的输出正好反相。
编写测试模块,给出仿真波形
VerilogHDL设计练习进阶(三)
>>>>
练习三.利用条件语句实现较复杂的时序逻辑电路
目的:
掌握条件语句在VerilogHDL中的使用。
与常用的高级程序语言一样,为了描述较为复杂的时序关系,VerilogHDL提供了条件语句供
分支判断时使用。
在可综合风格的VerilogHDL模型中常用的条件语句有if•-else和
case…endcase两种结构,用法和C程序语言中类似。
两者相较,if…else用于不很复杂的分
支关系,实际编写可综合风格的模块、特别是用状态机构成的模块时,更常用的是
case…endcase风格的代码。
这一节我们给的是有关if…else的范例,有关case…endcase
结构的代码已后会经常用到。
下面给出的范例也是一个可综合风格的分频器,是将10M的时钟分频为500K的时钟。
基本原理
与1/2分频器是一样的,但是需要定义一个计数器,以便准确获得1/20分频
模块源代码:
//fdivision.v
modulefdivision(RESET,F10M,F500K);
inputF10M,RESET;
outputF500K;
regF500K;
reg[7:
0]j;
always@(posedgeF10M)
if(!
RESET)//低电平复位。
begin
F500K<=0;
j<=0;
end
else
begin
if(j==19)//对计数器进行判断,以确定F500K信号是否反转。
begin
j<=0;
F500K<=~F500K;
end
else
j<=j+1;
end
endmodule
测试模块源代码:
//fdivision_Top.v
'timescale1ns/100ps
'defineclk_cycle50
moduledivision_Top;
regF10M_clk,RESET;
wireF500K_clk;
always#'clk_cycleF10M_clk=~F10M_clk;
initial
begin
RESET=1;
F10M=0;
#100RESET=0;
#100RESET=1;
#10000$stop;
end
fdivisionfdivision(.RESET(RESET),.F10M(F10M_clk),.F500K(F500K_clk));
endmodule
仿真波形:
练习:
利用10M的时钟,设计一个单周期形状如下的周期波形。
VerilogHDL设计练习进阶(四)>>>>
练习四.设计时序逻辑时采用阻塞赋值与非阻塞赋值的区别目的:
1.明确掌握阻塞赋值与非阻塞赋值的概念和区别;2.了解阻塞赋值的使用情况。
阻塞赋值与非阻塞赋值,在教材中我们已经了解了它们之间在语法上的区别以及综合后所得到的电路结构上的区别。
在always块中,阻塞赋值可以理解为赋值语句是顺序执行的,而非阻
塞赋值可以理解为赋值语句是并发执行的。
实际的时序逻辑设计中,一般的情况下非阻塞赋值语句被更多地使用,有时为了在同一周期实现相互关联的操作,也使用了阻塞赋值语句。
(注意:
在实现组合逻辑的assign结构中,无一例外地都必须采用阻塞赋值语句。
下例通过分别采用阻塞赋值语句和非阻塞赋值语句的两个看上去非常相似的两个模块blocking.v和non_blocking.v来阐明两者之间的区别。
模块源代码:
//blocking.v
moduleblocking(clk,a,b,c);
output[3:
0]b,c;
input[3:
0]a;
inputclk;
reg[3:
0]b,c;
always@(posedgeclk)
begin
b=a;
c=b;
$display("Blocking:
a=%d,b=%d,c=%d.",a,b,c);end
endmodule
//non_blocking.v——
modulenon_blocking(clk,a,b,c);
output[3:
0]b,c;
input[3:
0]a;
inputclk;
reg[3:
0]b,c;
always@(posedgeclk)begin
b<=a;
c<=b;
$display("Non_Blocking:
a=%d,b=%d,c=%d.",a,b,c);endendmodule
测试模块源代码:
//compareTop.v
'timescale1ns/100ps
'inelude"./blocking.v"
'include"./non_blocking.v"modulecompareTop;
wire[3:
0]b1,c1,b2,c2;
reg[3:
0]a;
regclk;
initial
begin
clk=0;
forever#50clk=~clk;
end
initial
begin
a=4'h3;
$display("");
#100a=4'h7;
$display("");
#100a=4'hf;
$display("");
#100a=4'ha;
$display("");
#100a=4'h2;
$display("");
#100$display("");
$stop;
end
non_blockingnon_blocking(clk,a,b2,c2);blockingblocking(clk,a,b1,c1);
endmodule
仿真波形(部分)
仲川
:
1H1
MH
Jill
fem
■non
OTtl
till
把ismpaiwTtjpSlh
厂
思考:
在blocking模块中按如下写法,仿真与综合的结果会有什么样的变化?
作出仿真波形,分析综合结果。
1.always@(posedgeclk)
begin
c=b;
b=a;
end
2.always@(posedgeclk)b=a;always@(posedgeclk)c=b;
VerilogHDL设计练习进阶(五)
>>>>
练习五.用always块实现较复杂的组合逻辑电路
目的:
1.掌握用always实现组合逻辑电路的方法;
2.了解assign与always两种组合逻辑电路实现方法之间的区别。
仅使用assign结构来实现组合逻辑电路,在设计中会发现很多地方会显得冗长且效率低下。
而适当地采用always来设计组合逻辑,往往会更具实效。
已进行的范例和练习中,我们仅在实现时序逻辑电路时使用always块。
从现在开始,我们对它的看法要稍稍改变。
下面是一个简单的指令译码电路的设计示例。
该电路通过对指令的判断,对输入数据执行相应的操作,包括加、减、与、或和求反,并且无论是指令作用的数据还是指令本身发生变化,结果都要作出及时的反应。
显然,这是一个较为复杂的组合逻辑电路,如果采用assign语句,
表达起来非常复杂。
示例中使用了电平敏感的always块,所谓电平敏感的触发条件是指在寥
的括号内电平列表中的任何一个电平发生变化,(与时序逻辑不同,它在朗的括号内没有沿
敏感关键词,如posedge或negedge)就能触发always块的动作,并且运用了case结构来进
行分支判断,不但设计思想得到直观的体现,而且代码看起来非常整齐、便于理解。
//alu.v
'defineplus3'dO
'defineminus3'd1
'defineband3'd2
'definebor3'd3
同一组合逻辑电路分别用always块和连续赋值语句assign描述时,代码的形式大相径庭,但是在always中适当运用default(在case结构中)和else(在if…else结构中),通常可以综合为纯组合逻辑,尽管被赋值的变量一定要定义为reg型。
不过,如果不使用default或else对缺省项进行说明,则易生成意想不到的锁存器,这一点一定要加以注意。
指令译码器的测试模块源代码:
//alu_Top.v
'timescale1ns/1ns
'include"./alu.v"modulealutest;
wire[7:
0]out;
reg[7:
0]a,b;
reg[2:
0]opcode;
parametertimes=5;
initial
begin
a={$random}%256;//Givearadomnumberblongsto[0,255].b={$random}%256;//Givearadomnumberblongsto[0,255].opcode=3'h0;
repeat(times)
begin
#100a={$random}%256;//Givearadomnumber.
b={$random}%256;//Givearadomnumber.opcode=opcode+1;
end
#100$stop;
end
alualu1(out,opcode,a,b);endmodule
练习:
运用always块设计一个八路数据选择器。
要求:
每路输入数据与输出数据均为4位2进
制数,当选择开关(至少3位)或输入数据发生变化时,输出数据也相应地变化。
VerilogHDL设计练习进阶(六)
>>>
练习六.在VerilogHDL中使用函数
目的:
掌握函数在模块设计中的使用。
与一般的程序设计语言一样,VeirlogHDL也可使用函数以适应对不同变量采取同一运算的操
作。
VeirlogHDL函数在综合时被理解成具有独立运算功能的电路,每调用一次函数相当于改变这部分电路的输入以得到相应的计算结果。
下例是函数调用的一个简单示范,采用同步时钟触发运算的执行,每个clk时钟周期都会执行
一次运算。
并且在测试模块中,通过调用系统任务$display在时钟的下降沿显示每次计算的
结果。
模块源代码:
moduletryfunct(clk,n,result,reset);
output[31:
0]result;
input[3:
0]n;
inputreset,clk;
reg[31:
0]result;
always@(posedgeclk)//clk的上沿触发同步运算。
begin
if(!
reset)//reset为低时复位。
result<=0;
else
begin
result<=n*factorial(n)/((n*2)+1);
end
end
函数定义。
function[31:
0]factorial;//input[3:
0]operand;
reg[3:
0]index;
begin
factorial=operand?
1:
0;
for(index=2;index<=operand;index=index+1)factorial=index*factorial;
end
endfunction
endmodule
测试模块源代码:
'include"./step6.v"
'timescale1ns/100ps
'defineclk_cycle50
moduletryfuctTop;
reg[3:
0]n,i;
regreset,clk;
wire[31:
0]result;
initial
begin
n=0;
reset=1;
clk=0;
#100reset=0;
#100reset=1;
for(i=0;i<=15;i=i+1)
begin
#200n=i;
end
#100$stop;
end
always#'clk_cycleclk=~clk;
tryfuncttryfunct(.clk(clk),.n(n),.result(result),.reset(reset));
endmodule
上例中函数factorial(n)实际上就是阶乘运算。
必须提醒大家注意的是,在实际的设计
中,我们不希望设计中的运算过于复杂,以免在综合后带来不可预测的后果。
经常的情况是,我们把复杂的运算分成几个步骤,分别在不同的时钟周期完成。
仿真波形(部分):
帕■•曲iTopJn1~
;5
fl-
巾y亦旳咔、>
a
——j
—
r111J
-1J
s
1[
■IrvluCfFixi沃:
0£加:
r
练习:
设计一个带控制端的逻辑运算电路,分别完成正整数的平方、立方和阶乘的运算。
编写测试模块,并给出仿真波形。
VerilogHDL设计练习进阶(七)
>>>>
练习七.在VerilogHDL中使用任务(task)目的:
掌握任务在结构化VerilogHDL设计中的应用。
仅有函数并不能完全满足VeirlogHDL中的运算需求。
当我们希望能够将一些信号进行运算并输出多个结果时,采用函数结构就显得非常不方便,而任务结构在这方面的优势则十分突出。
任务本身并不返回计算值,但是它通过类似C语言中形参与实参的数据交换,非常快捷地实现运算结果的调用。
此外,我们还常常利用任务来帮助我们实现结构化的模块设计,将批量的操作以任务的形式独立出来,这样设计的目的通常一眼看过去就很明了。
下面是一个利用task和电平敏感的always块设计比较后重组信号的组合逻辑的实例。
可以看到,利用task非常方便地实现了数据之间的交换,如果要用函数实现相同的功能是非常复杂的;另外,task也避免了直接用一般语句来描述所引起的不易理解和综合时产生冗余逻辑等问题。
模块源代码:
//sort4.v
modulesort4(ra,rb,rc,rd,a,b,c,d);
output[3:
0]ra,rb,rc,rd;
input[3:
0]a,b,c,d;
reg[3:
0]ra,rb,rc,rd;
reg[3:
0]va,vb,vc,vd;
always@(aorborcord)
begin
{va,vb,vc,vd}={a,b,c,d};
sort2(va,vc);
//va
与vc互换。
sort2(vb,vd);
//vb
与vd互换。
sort2(va,vb);
//va
与vb互换。
sort2(vc,vd);
//vc
与vd互换。
sort2(vb,vc);
//vb
与vc互换。
{ra,rb,rc,rd}={va,vb,vc,vd};
end
tasksort2;
inout[3:
0]x,y;
reg[3:
0]tmp;
if(x>y)
begin
tmp=x;//x与y变量的内容互换,要求顺序执行,所以采用阻塞赋值方式。
x=y;
y=tmp;
end
endtaskendmodule
值得注意的是task中的变量定义与模块中的变量定义不尽相同,它们并不受输入输出类型的限制。
如此例,x与y对于tasksort2来说虽然是inout型,但实际上它们对应的是always
块中变量,都是reg型变量。
测试模块源代码:
'timescale1ns/100ps
'include"sort4.v"
moduletask_Top;
reg[3:
0]a,b,c,d;
wire[3:
0]ra,rb,rc,rd;
initial
begin
a=0;b=0;c=0;d=0;
repeat(5)
begin
#100a={$random}%15;
b={$random}%15;
c={$random}%15;
d={$random}%15;
end
#100$stop;
sort4sort4(.a(a),.b(b),.c(c),.d(d),.ra(ra),.rb(rb),.rc(rc),.rd(rd));endmodule
仿真波形(部分)
r
MM
110&
01TO
/可』ilT"7^V:
i
丿口韭Taplb
inoQ
團10
piOa
:
一l:
woa
^011f
.1
ni3
BoFi-
<■
+
OTOO
1
fiti-
[呢坨
■-
oaoo
■OQW
4
aicj
oan
4
1
'山汕
1■冲乂rcp/rtf
OOQO
[J110
r
练习:
设计一个模块,通过任务完成3个8位2进制输入数据的冒泡排序。
要求:
时钟触发任务的执行,每个时钟周期完成一次数据交换的操作。
VerilogHDL设计练习进阶(八)
>>>
练习八.利用有限状态机进行复杂时序逻辑的设计
目的:
掌握利用有限状态机实现复杂时序逻辑的方法;在数字电路中我们已经学习过通过建立有限状态机来进行数字逻辑的设计,而在VerilogHDL硬件描述语言中,这种设计方法得到进一步的发展。
通过VerilogHDL提供的语句,我们可以直观地设计出适合更为复杂的时序逻辑的电路。
关于有限状态机的设计方法在教材中已经作了较为详细的阐述,在此就不赘述了。
下例是一个简单的状态机设计,功能是检测一个5位二进制序列“10010”。
考虑到序列重叠的
可能,有限状态机共提供8个状态(包括初始状态IDLE)。
模块源代码:
seqdet.v
moduleseqdet(x,z,clk,rst,state);
inputx,clk,rst;
outputz;
output[2:
0]state;
reg[2:
0]state;
wirez;
parameterIDLE='d0,A='d1,B='d2,
C='d3,D='d4,
E='d5,F='d6,
G='d7