Verilog设计练习十例及答案.docx

上传人:b****3 文档编号:4000657 上传时间:2022-11-27 格式:DOCX 页数:32 大小:108.98KB
下载 相关 举报
Verilog设计练习十例及答案.docx_第1页
第1页 / 共32页
Verilog设计练习十例及答案.docx_第2页
第2页 / 共32页
Verilog设计练习十例及答案.docx_第3页
第3页 / 共32页
Verilog设计练习十例及答案.docx_第4页
第4页 / 共32页
Verilog设计练习十例及答案.docx_第5页
第5页 / 共32页
点击查看更多>>
下载资源
资源描述

Verilog设计练习十例及答案.docx

《Verilog设计练习十例及答案.docx》由会员分享,可在线阅读,更多相关《Verilog设计练习十例及答案.docx(32页珍藏版)》请在冰豆网上搜索。

Verilog设计练习十例及答案.docx

Verilog设计练习十例及答案

设计练习进阶

前言:

在前面九章学习的基础上,通过本章的练习,一定能逐步掌握VerilogHDL设计的要点。

我们可以先理解样板模块中每一条语句的作用,然后对样板模块进行综合前和综合后仿真,再独立完成每一阶段规定的练习。

当十个阶段的练习做完后,便可以开始设计一些简单的逻辑电路和系统。

很快我们就能过渡到设计相当复杂的数字逻辑系统。

当然,复杂的数字逻辑系统的设计和验证,不但需要系统结构的知识和经验的积累,还需要了解更多的语法现象和掌握高级的VerilogHDL系统任务,以及与C语言模块接口的方法(即PLI),这些已超出的本书的范围。

有兴趣的同学可以阅读Verilog语法参考资料和有关文献,自己学习,我们将在下一本书中介绍Verilog较高级的用法。

练习一.简单的组合逻辑设计

目的:

掌握基本组合逻辑电路的实现方法。

这是一个可综合的数据比较器,很容易看出它的功能是比较数据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

测试模块用于检测模块设计得正确与否,它给出模块的输入信号,观察模块的内部信号和输出信号,如果发现结果与预期的有所偏差,则要对设计模块进行修改。

测试模块源代码:

`timescale1ns/1ns//定义时间单位。

`include"./compare.v"//包含模块文件。

在有的仿真调试环境中并不需要此语句。

//而需要从调试环境的菜单中键入有关模块文件的路径和名称

modulecomparetest;

rega,b;

wireequal;

initial//initial常用于仿真时信号的给出。

begin

a=0;

b=0;

#100a=0;b=1;

#100a=1;b=1;

#100a=1;b=0;

#100$stop;//系统任务,暂停仿真以便观察仿真波形。

end

comparecompare1(.equal(equal),.a(a),.b(b));//调用模块。

endmodule

仿真波形(部分):

练习:

设计一个字节(8位)比较器。

要求:

比较两个字节的大小,如a[7:

0]大于b[7:

0]输出高电平,否则输出低电平,改写测试模型,使其能进行比较全面的测试。

练习二.简单时序逻辑电路的设计

目的:

掌握基本时序逻辑电路的实现。

在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_cycle50

moduleclk_Top.v

regclk,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

仿真波形:

练习:

依然作clk_in的二分频clk_out,要求输出与上例的输出正好反相。

编写测试模块,给出仿真波形。

 

练习三.利用条件语句实现较复杂的时序逻辑电路

目的:

掌握条件语句在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的时钟,设计一个单周期形状如下的周期波形。

T

0

练习四.设计时序逻辑时采用阻塞赋值与非阻塞赋值的区别

目的:

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);

end

endmodule

测试模块源代码:

//-------------compareTop.v-----------------------------

`timescale1ns/100ps

`include"./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

 

仿真波形(部分):

思考:

在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结构来进行分支判断,不但设计思想得到直观的体现,而且代码看起来非常整齐、便于理解。

//---------------alu.v--------------------------

`defineplus3'd0`defineminus3'd1`defineband3'd2

`definebor3'd3

`defineunegate3'd4

modulealu(out,opcode,a,b);

output[7:

0]out;

reg[7:

0]out;

input[2:

0]opcode;

input[7:

0]a,b;//操作数。

always@(opcodeoraorb)//电平敏感的always块

begin

case(opcode)

`plus:

out=a+b;//加操作。

`minus:

out=a-b;//减操作。

`band:

out=a&b;//求与。

`bor:

out=a|b;//求或。

`unegate:

out=~a;//求反。

default:

out=8'hx;//未收到指令时,输出任意态。

endcase

end

endmodule

同一组合逻辑电路分别用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中使用函数

 目的:

掌握函数在模块设计中的使用。

  

与一般的程序设计语言一样,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)实际上就是阶乘运算。

必须提醒大家注意的是,在实际的设计中,我们不希望设计中的运算过于复杂,以免在综合后带来不可预测的后果。

经常的情况是,我们把复杂的运算分成几个步骤,分别在不同的时钟周期完成。

仿真波形(部分):

练习:

设计一个带控制端的逻辑运算电路,分别完成正整数的平方、立方和阶乘的运算。

编写测试模块,并给出仿真波形。

 

练习七.在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

endtask

endmodule

值得注意的是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),.r

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 能源化工

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1