Verilog设计练习十例及答案.docx
《Verilog设计练习十例及答案.docx》由会员分享,可在线阅读,更多相关《Verilog设计练习十例及答案.docx(17页珍藏版)》请在冰豆网上搜索。
![Verilog设计练习十例及答案.docx](https://file1.bdocx.com/fileroot1/2023-2/24/d45989cb-2435-4f52-ba9c-29a70e0d4d0b/d45989cb-2435-4f52-ba9c-29a70e0d4d0b1.gif)
Verilog设计练习十例及答案
设计练习进阶
前言:
在前面九章学习的基础上,通过本章的练习,一定能逐步掌握VerilogHDL设计的要点。
我
们可以先理解样板模块中每一条语句的作用,然后对样板模块进行综合前和综合后仿真,再
独立完成每一阶段规定的练习。
当十个阶段的练习做完后,便可以开始设计一些简单的逻辑
电路和系统。
很快我们就能过渡到设计相当复杂的数字逻辑系统。
当然,复杂的数字逻辑系
统的设计和验证,不但需要系统结构的知识和经验的积累,还需要了解更多的语法现象和掌
握高级的VerilogHDL系统任务,以及与C语言模块接口的方法(即PLI),这些已超出的本书
的范围。
有兴趣的同学可以阅读Verilog语法参考资料和有关文献,自己学习,我们将在下一本书中介绍Verilog较高级的用法。
练习一.简单的组合逻辑设计
目的:
掌握基本组合逻辑电路的实现方法。
这是一个可综合的数据比较器,很容易看出它的功能是比较数据a与数据b,如果两个
数据相同,则给出结果1,否则给出结果0。
在VerilogHDL中,描述组合逻辑时常使用assign
结构。
注意equal=(a==b)1:
0,这是一种在组合逻辑实现分支判断时常使用的格式。
模块源代码:
'cDrnparelesVc:
目的:
掌握基本时序逻辑电路的实现。
在VerilogHDL中,相对于组合逻辑电路,时序逻辑电路也有规定的表述方式。
在可综
合白VVerilogHDL模型,我们通常使用always块和@(posedgeclk)或@(negedgeclk)的结构来
表述时序逻辑。
下面是一个1/2分频器的可综合模型。
eset(reset),.clk_in(clk),.clk_out(clk_out));
endmodule
仿真波形:
/clk_Top/clk_in|:
^clk_Top/reset^clk_Top/clk_out|||.
练习:
依然作clk_in的二分频clk_out,要求输出与上例的输出正好反相。
编写测试模块,给出仿真波形。
练习三.利用条件语句实现较复杂的时序逻辑电路
目的:
掌握条件语句在VerilogHDL中的使用。
与常用的高级程序语言一样,为了描述较为复杂的时序关系,VerilogHDL提供了条件语句供分支判断时使用。
在可综合风格的VerilogHDL模型中常用的条件语句有if-else和
case-endcase两种结构,用法和C程序语言中类似。
两者相较,if・・else用于不很复杂的分支关系,实际编写可综合风格的模块、特别是用状态机构成的模块时,更常用的是case--endcase
风格的代码。
这一节我们给的是有关if・・else的范例,有关case--endcase结构的代码已后会
经常用到。
下面给出的范例也是一个可综合风格的分频器,是将10M的时钟分频为500K的时钟。
1/20分频
基本原理与1/2分频器是一样的,但是需要定义一个计数器,以便准确获得
模块源代码:
ESET(RESET),.F10M(F10M_clk),.F500K(F500K_clk));
endmodule
仿真波形:
/divisionIop.l10M
/divtsior_Top/RESET
;divi&ion_TQiyF500K
:
™皿皿njinnnnnn啊iimiiminiuLui「u啊皿umnnrimiuun叫niuu
11:
练习:
利用10M的时钟,设计一个单周期形状如下的周期波形。
20us10us20us
0T
练习四.设计时序逻辑时采用阻塞赋值与非阻塞赋值的区别
目的:
1.明确掌握阻塞赋值与非阻塞赋值的概念和区别;
2.了解阻塞赋值的使用情况。
阻塞赋值与非阻塞赋值,在教材中我们已经了解了它们之间在语法上的区别以及综合后
所得到的电路结构上的区别。
在always块中,阻塞赋值可以理解为赋值语句是顺序执行的,
而非阻塞赋值可以理解为赋值语句是并发执行的。
实际的时序逻辑设计中,一般的情况下非阻塞赋值语句被更多地使用,有时为了在同一周期实现相互关联的操作,也使用了阻塞赋值语句。
(注意:
在实现组合逻辑的assign结构中,无一例外地都必须采用阻塞赋值语句。
下例通过分别采用阻塞赋值语句和非阻塞赋值语句的两个看上去非常相似的两个模块和
来阐明两者之间的区别。
模块源代码:
a,b,c);
end
endmodule
a,b,c);
end
endmodule
测试模块源代码:
"
'include"./"
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
仿真波形(部分)
IEM4
J0111
:
nn
|I1
MH
IHil
1UUII1
fiYH午
JOIfl
Jim
|WII!
■inn11
0111
0111
riir
iU,10
0011】
■■>
_r
tornpareTop/b1
/cofnpareTop?
c1
/compareTo(ipa
.BmparmTop依it
思考:
在blocking模块中按如下写法,仿真与综合的结果会有什么样的变化作出仿真
波形,分析综合结果。
1.always@(posedgeclk)
begin
c=b;
b=a;end
2.always@(posedgeclk)b=a;
always@(posedgeclk)c=b;
练习五.用always块实现较复杂的组合逻辑电路
目的:
1.掌握用always实现组合逻辑电路的方法;
2.了解assign与always两种组合逻辑电路实现方法之间的区别。
仅使用assign结构来实现组合逻辑电路,在设计中会发现很多地方会显得冗长且效率低
下。
而适当地采用always来设计组合逻辑,往往会更具实效。
已进行的范例和练习中,我们
仅在实现时序逻辑电路时使用always块。
从现在开始,我们对它的看法要稍稍改变。
下面是一个简单的指令译码电路的设计示例。
该电路通过对指令的判断,对输入数据执
行相应的操作,包括加、减、与、或和求反,并且无论是指令作用的数据还是指令本身发生
变化,结果都要作出及时的反应。
显然,这是一个较为复杂的组合逻辑电路,如果采用assign
语句,表达起来非常复杂。
示例中使用了电平敏感的always块,所谓电平敏感的触发条件是
指在@后的括号内电平列表中的任何一个电平发生变化,(与时序逻辑不同,它在@后的括号
内没有沿敏感关键词,如posedge或negedge)就能触发always块的动作,并且运用了case
结构来进行分支判断,不但设计思想得到直观的体现,而且代码看起来非常整齐、便于理解。
"
modulealutest;
wire[7:
0]out;
reg[7:
0]a,b;
reg[2:
0]opcode;
parametertimes=5;
initial
begina={$random}%256;radomb={$random}%256;radom
opcode=3'h0;
repeat(times)begin
#100a={$random}%256;radomb={$random}%256;
radomopcode=opcode+1;
end
#100$stop;
end
alualu1(out,opcode,a,b);
endmodule
仿真波形(部分)
练习:
运用always块设计一个八路数据选择器。
要求:
每路输入数据与输出数据均为4位2
进制数,当选择开关(至少3位)或输入数据发生变化时,输出数据也相应地变化。
练习六.在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)"
'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)实际上就是阶乘运算。
必须提醒大家注意的是,在实际的设计中,我们不希望设计中的运算过于复杂,以免在综合后带来不可预测的后果。
经常的情况是,我们把复杂的运算分成几个步骤,分别在不同的时钟周期完成。
仿真波形(部分):
/tryfoclTopwn1
\2
:
3
三
iTjyfuctTopfl2
4
5
.tryfuClTopfTcj&eE
山yluctTop化Ik|
1U
.tr/fuctTop/result
1
(00000007
口0000a
练习:
设计一个带控制端的逻辑运算电路,分别完成正整数的平方、立方和阶乘的运算。
编写测试模块,并给出仿真波形。
练习七.在VerilogHDL中使用任务(task)
目的:
掌握任务在结构化VerilogHDL设计中的应用。
仅有函数并不能完全满足VeirlogHDL中的运算需求。
当我们希望能够将一些信号进行运
算并输出多个结果时,采用函数结构就显得非常不方便,而任务结构在这方面的优势则十分
突出。
任务本身并不返回计算值,但是它通过类似C语言中形参与实参的数据交换,非常快
捷地实现运算结果的调用。
此外,我们还常常利用任务来帮助我们实现结构化的模块设计,
将批量的操作以任务的形式独立出来,这样设计的目的通常一眼看过去就很明了。
下面是一个利用task和电平敏感的always块设计比较后重组信号的组合逻辑的实例。
可
以看到,利用task非常方便地实现了数据之间的交换,如果要用函数实现相同的功能是非常
复杂的;另外,task也避免了直接用一般语句来描述所引起的不易理解和综合时产生冗余逻辑等问题。
模块源代码:
(a),.b(b),.c(c),.d(d),.ra(ra),.rb(rb),.rc(rc),.rd(rd));
endmodule
仿真波形(部分)
flBSk-Top^a^task_Toprti
/task_Top/d
出askTop/rb
Top/rc
AaskTop/rd
ooao
口0001
口1Q0
Oilo
0000;
moo;
0010
0100
oona;
Olli\
区1口1
0C11
ooou
ooio:
^111
UC10
non*\
OOI口
am:
L1U1
UG11
);
1QOO1
0111
0100
-1
।1vj
WS;
:
1i00:
WO
练习:
设计一个模块,通过任务完成3个8位2进制输入数据的冒泡排序。
要求:
时钟触发
任务的执行,每个时钟周期完成一次数据交换的操作。
练习八.利用有限状态机进行复杂时序逻辑的设计
目的:
掌握利用有限状态机实现复杂时序逻辑的方法;
在数字电路中我们已经学习过通过建立有限状态机来进行数字逻辑的设计,而在Verilog
HDL硬件描述语言中,这种设计方法得到进一步的发展。
通过VerilogHDL提供的语句,我们
可以直观地设计出适合更为复杂的时序逻辑的电路。
关于有限状态机的设计方法在教材中已
经作了较为详细的阐述,在此就不赘述了。
下例是一个简单的状态机设计,功能是检测一个5位二进制序列“10010”。
考虑到序列
重叠的可能,有限状态机共提供8个状态(包括初始状态IDLE)。
模块源代码:
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;
assignz=(state==E&&x==0)1:
0;"
moduleseqdet_Top;
regclk,rst;
reg[23:
0]data;
wire[2:
0]state;
wirez,x;
assignx=data[23];
always#10clk=〜clk;
always@(posedgeclk)
data={data[22:
0],data[23]};
initial
beginclk=0;
rst=1;
#2rst=0;
#30rst=1;
data='b1100_1001_0000_1001_0100;
#500$stop;
end
seqdetm(x,z,clk,rst,state);
endmodule
nnnnrnr
仿真波形:
_rLTLTLTLr^ni~in□厂
.TopJrst,_
op/data(~~)1丁IT11「
中,{OOP
joi「痴nfmI;””11
LTop/z.
l
练习:
设计一个串行数据检测器。
要求是:
连续4个或4个以上的1时输出为1,其他输入
情况下为0。
编写测试模块并给出仿真波形。
练习九.利用状态机的嵌套实现层次结构化设计
目的:
1.运用主状态机与子状态机产生层次化的逻辑设计;
2.在结构化设计中灵活使用任务(task)结构。
在上一节,我们学习了如何使用状态机的实例。
实际上,单个有限状态机控制整个逻辑电路的运转在实际设计中是不多见,往往是状态机套用状态机,从而形成树状的控制核心。
这一点也与我们提倡的层次化、结构化的自顶而下的设计方法相符,下面我们就将提供一个这样的示例以供大家学习。
该例是一个简化的EPROM的串行写入器。
事实上,它是一个EPROM读写器设计中实现写功能的部分经删节得到的,去除了EPROM的启动、结束和EPROM控制字的写入等功能,
只具备这样一个雏形。
工作的步骤是:
1.地址的串行写入;2.数据的串彳T写入;3.给信号源
应答,信号源给出下一个操作对象;4.结束写操作。
通过移位令并行数据得以一位一位输出。
模块源代码:
modulewriting(reset,clk,address,data,sda,ack);
inputreset,clk;
input[7:
0]data,address;
outputsda,ack;eset(reset),.clk(clk),.data(data),
.address(address),.ack(ack),.sda(sda));
endmodule
仿真波形:
.rrT
卬,启set
gw侦*JITJUIIUULTUIT
jinrLnrLRrLrLrLrTLruuLrLruuLrupuirLrLruLrLrLnr
rop■1二心i.Kxjqooou
(omoixjoi
^oddross00000000
100000001
|T印——1_
m
|Top/&da1_
:
।।।।[1।।
g临匕加一(kwoi
:
gio卜口。
网)0010
—L_JUUUU(JUL
K-M[It11I!
II1(IIEIP-TJ1III
1
练习:
仿照上例,编写一个实现EPROM内数据串行读取的模块。
编写测试模块,给出仿真
波形。
练习十.通过模块之间的调用实现自顶向下的设计
目的:
学习状态机的嵌套使用实现层次化、结构化设计。
现代硬件系统的设计过程与软件系统的开发相似,设计一个大规模的集成电路的往往由
模块多层次的引用和组合构成。
层次化、结构化的设计过程,能使复杂的系统容易控制和
调试。
在VerilogHDL中,上层模块引用下层模块与C语言中程序调用有些类似,被引用的
子模块在综合时作为其父模块的一部分被综合,形成相应的电路结构。
在进行模块实例引用时,必须注意的是模块之间对应的端口,即子模块的端口与父模块的内部信号必须明确无误
地一一对应,否则容易产生意想不到的后果。
下面给出的例子是设计中遇到的一个实例,其功能是将并行数据转化为串行数据送交外
部电路编码,并将解码后得到的串行数据转化为并行数据交由CPU处理。
显而易见,这实际
上是两个独立的逻辑功能,分别设计为独立的模块,然后再合并为一个模块显得目的明确、层次清晰。
assignD_in=DATA_Q[7];"
'include"./"
modulesys(D_in,T0,T1,data,D_out,SEND,ESC,DSC,TAKE,ADD_100,ADD_101);
inputD_out,SEND,ESC,DSC,TAKE,ADD_100,ADD_101;
inout[7:
0]data;
outputD_in,T0,T1;
p_to_sp_to_s(.D_in(D_in),.T0(T0),.data(data),
.SEND(SEND),.ESC(ESC),.ADD_100(ADD_100));
s_to_ps_to_p(.T1(T1),.data(data),.D_out(D_out),
.DSC(DSC),.TAKE(TAKE),.ADD_101(ADD_101));
endmodule
测试模块源代码:
"
moduleTop;
regD_out,SEND,ESC,DSC,TAKE,ADD_100,ADD_101;
reg[7:
0]data_buf;
wire[7:
0]data;
wireclk2;
assigndata=(ADD_101)data_buf:
8'bz;
_in(D_in),.T0(T0),.T1(T1),.data(data),.D_out(D_out),
.ADD_101(ADD_101),.SEND(SEND),.ESC(ESC),.DSC(DSC),
.TAKE(TAKE),.ADD_100(ADD_100));
endmodule
仿真波形:
练习:
设计一个序列发生器。
要求根据输入的8位并行数据输出串行数据,如果输入数据在
0—127之间则输出一位0,如果输入数据在128—255之间则输出一位1,同步时钟触发;并
且和范例8的序列检测器搭接,形成一个封闭系统。
编写测试模块,并给出仿真波形。