设计示范和上机习题
练习一.简单的组合逻辑设计
//(方法一):
//---------------文件名 compare.v -----------------
module compare(equal,a,b);
input a,b;
output equal;
assign equal = (a==b)? 1 : 0;
//a等于b时,equal输出为1;a不等于b时,equal输出为0。
endmodule
//(方法二):
module compare(equal,a,b);
input a,b;
output equal;
reg equal;
always @(a or b)
if(a==b) //a等于b时,equal输出为1;
equal =1;
else //a不等于b时,equal输出为0。
equal = 0; //思考:如果不写else 部分会产生什么逻辑?
endmodule
//-------------------------------------------------------------
//----------测试模块源代码(方法之一):
`timescale 1ns/1ns // 定义时间单位。
`include "./compare.v" // 包含模块文件。在有的仿真调试环境中并不需要此语句。
//而需要从调试环境的菜单中键入有关模块文件的路径和名称
module t;
reg a,b;
wire equal;
initial // initial常用于仿真时信号的给出。
begin
a=0;
b=0;
#100 a=0; b=1;
#100 a=1; b=1;
#100 a=1; b=0;
#100 a=0; b=0;
#100 $stop; //系统任务,暂停仿真以便观察仿真波形。
end
compare m(.equal(equal),.a(a),.b(b)); //调用被测试模块t.m
endmodule
//---------- 测试模块源代码(方法之二):-------------------------
`timescale 1ns/1ns // 定义时间单位。
`include "./compare.v" // 包含模块文件。在有的仿真调试环境中并不需要此语句。
//而需要从调试环境的菜单中键入有关模块文件的路径和名称
module t;
reg a,b;
reg clock;
wire equal;
initial // initial常用于仿真时信号的给出。
begin
a=0;
b=0;
clock = 0; //定义一个时钟变量
end
always #50 clock = ~clock; //产生周期性的时钟
always @ (posedge clock) //在每次时钟正跳变沿时刻产生不同的a 和 b
begin
a = {$random}%2; // 每次a是 0还是1是随机的。
b = {$random}%2; // 每次b是 0还是1是随机的。
end
initial
begin #100000 $stop; end //系统任务,暂停仿真以便观察仿真波形。
compare m(.equal(equal),.a(a),.b(b)); //调用被测试模块t.m
endmodule
练习二. 简单分频时序逻辑电路的设计
//------------------------- 文件名:half_clk.v --------------------------------
module half_clk(reset,clk_in,clk_out);
input clk_in,reset;
output clk_out;
reg clk_out;
always @(posedge clk_in)
begin
if(!reset) clk_out=0;
else clk_out=~clk_out;
end
endmodule
//---------- 测试模块的源代码:------------------------
//------------------- 文件名top.v -----------------------------
`timescale 1ns/100ps
`define clk_cycle 50
module top;
reg clk,reset;
wire clk_out;
always #`clk_cycle clk = ~clk; //产生测试时钟
initial
begin
clk = 0;
reset = 1;
#10 reset = 0;
#110 reset = 1;
#100000 $stop;
end
half_clk m0(.reset(reset),.clk_in(clk),.clk_out(clk_out));
endmodule
练习三. 利用条件语句实现计数分频时序电路
//-------------- 模块源代码:-----------------------------
// --------------- fdivision.v -----------------------------
module fdivision(RESET,F10M,F500K);
input F10M,RESET;
output F500K;
reg F500K;
reg [7:0]j;
always @(posedge F10M)
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 ------------------------
`timescale 1ns/100ps
`define clk_cycle 50
module division_Top;
reg F10M,RESET;
wire F500K_clk;
always #`clk_cycle F10M_clk = ~ F10M_clk;
initial
begin
RESET=1;
F10M=0;
#100 RESET=0;
#100 RESET=1;
#10000 $stop;
end
fdivision fdivision (.RESET(RESET),.F10M(F10M),.F500K(F500K_clk));
endmodule
练习四. 阻塞赋值与非阻塞赋值的区别
// ---------- 模块源代码:----------------------
// ------------- blocking.v ---------------
module blocking(clk,a,b,c);
output [3:0] b,c;
input [3:0] a;
input clk;
reg [3:0] b,c;
always @(posedge clk)
begin
b = a;
c = b;
$display("Blocking: a = %d, b = %d, c = %d ",a,b,c);
end
endmodule
//------------- non_blocking.v -------------------
module non_blocking(clk,a,b,c);
output [3:0] b,c;
input [3:0] a;
input clk;
reg [3:0] b,c;
always @(posedge clk)
begin
b <= a;
c <= b;
$display("Non_Blocking: a = %d, b = %d, c = %d ",a,b,c);
end
endmodule
// ---------- 测试模块源代码:--------------------------
//------------- compareTop.v -----------------------------
`timescale 1ns/100ps
`include "./blocking.v"
`include "./non_blocking.v"
module compareTop;
wire [3:0] b1,c1,b2,c2;
reg [3:0] a;
reg clk;
initial
begin
clk = 0;
forever #50 clk = ~clk; //思考:如果在本句后还有语句,能否执行?为什么?
end
initial
begin
a = 4'h3;
$display("____________________________");
# 100 a = 4'h7;
$display("____________________________");
# 100 a = 4'hf;
$display("____________________________");
# 100 a = 4'ha;
$display("____________________________");
# 100 a = 4'h2;
$display("____________________________");
# 100 $display("____________________________");
$stop;
end
non_blocking non_blocking(clk,a,b2,c2);
blocking blocking(clk,a,b1,c1);
endmodule
练习五. 用always块实现较复杂的组合逻辑电路
//---------------文件名 alu.v --------------------------
`define plus 3'd0
`define minus 3'd1
`define band 3'd2
`define bor 3'd3
`define unegate 3'd4
module alu(out,opcode,a,b);
output[7:0] out;
reg[7:0] out;
input[2:0] opcode;
input[7:0] a,b; //操作数。
always@(opcode or a or b) //电平敏感的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
//----------- 指令译码器的测试模块源代码:--------------
//------------- alutest.v -----------------
`timescale 1ns/1ns
`include "./alu.v"
module alutest;
wire[7:0] out;
reg[7:0] a,b;
reg[2:0] opcode;
parameter times=5;
initial
begin
a={$random}%256; //Give a radom number blongs to [0,255] .
b={$random}%256; //Give a radom number blongs to [0,255].
opcode=3'h0;
repeat(times)
begin
#100 a={$random}%256; //Give a radom number.
b={$random}%256; //Give a radom number.
opcode=opcode+1;
end
#100 $stop;
end
alu alu1(out,opcode,a,b);
endmodule
练习六. 在Verilog HDL中使用函数
//--------------- 模块源代码:---------------
//----------- 文件名 tryfunct.v --------------------
module tryfunct(clk,n,result,reset);
output[31:0] result;
input[3:0] n;
input reset,clk;
reg[31:0] result;
always @(posedge clk) //clk的上沿触发同步运算。
begin
if(!reset) //reset为低时复位。
result<=0;
else
begin
result <= n * factorial(n)/((n*2)+1);
end //verilog在整数除法运算结果中不考虑余数
end
function [31:0] factorial; //函数定义,返回的是一个32位的数
input [3:0] operand; //输入只有一个四位的操作数
reg [3:0] index; //函数内部计数用中间变量
begin
factorial = operand ? 1 : 0; //先定义操作数为零时函数的输出为零,不为零时为1
for(index = 2; index <= operand; index = index + 1)
factorial = index * factorial; //表示阶乘的算术迭代运算
end
endfunction
endmodule
//-------------- 测试模块源代码:------------------
`include "./tryfunct.v"
`timescale 1ns/100ps
`define clk_cycle 50
module tryfuctTop;
reg[3:0] n,i;
reg reset,clk;
wire[31:0] result;
initial
begin
clk=0;
n=0;
reset=1;
#100 reset=0; //产生复位信号的负跳沿
#100 reset=1; //复位信号恢复高电平后才开始输入n
for(i=0;i<=15;i=i+1)
begin
#200 n=i;
end
#100 $stop;
end
always #`clk_cycle clk=~clk;
tryfunct m(.clk(clk),.n(n),.result(result),.reset(reset));
endmodule
练习七. 在Verilog HDL中使用任务(task)
//-------------- 模块源代码:----------------
//-----------------文件名 sort4.v ------------------
module sort4(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 @ (a or b or c or d)
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
task sort2;
inout[3:0] x,y;
reg[3:0] tmp;
if(x>y)
begin
tmp=x; //x与y变量的内容互换,要求顺序执行,所以采用阻塞赋值方式。
x=y;
y=tmp;
end
endtask
endmodule
// --------- 测试模块源代码:---------------
`timescale 1ns/100ps
`include "sort4.v"
module task_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(50)
begin
#100 a ={$random}%15;
b ={$random}%15;
c ={$random}%15;
d ={$random}%15;
end
#100 $stop;
sort4 sort4 (.a(a),.b(b),.c(c),.d(d), .ra(ra),.rb(rb),.rc(rc),.rd(rd));
endmodule
练习八. 利用有限状态机进行时序逻辑的设计
// ------------- 模块源代码:------------------
//---------------- 文件名 seqdet.v -----------------
module seqdet(x,z,clk,rst,state);
input x,clk,rst;
output z;
output[2:0] state;
reg[2:0] state;
wire z;
parameter IDLE='d0, A='d1, B='d2,
C='d3, D='d4,
E='d5, F='d6,
G='d7;
assign z = ( state==E && x==0 )? 1 : 0;
//当x 序列10010最后一个0刚到时刻,时钟沿立刻将状态变为E,此时z 应该变为高
always @(posedge clk)
if(!rst)
begin
state <= IDLE;
end
else
casex(state)
IDLE : if(x==1) //第一个码位对,记状态A
begin
state <= A;
end
A: if(x==0) //第二个码位对,记状态B
begin
state <= B;
end
B: if(x==0) //第三个码位对,记状态C
begin
state <= C;
end
else //第三个码位不对,前功尽弃,记状态为F
begin
state <= F;
end
C: if(x==1) //第四个码位对
begin
state <= D;
end
else //第四个码位不对,前功尽弃,记状态为G
begin
state <= G;
end
D: if(x==0) //第五个码位对,记状态E
begin
state <= E; //此时开始应有z 的输出
end
else
//第五个码位不对,前功尽弃,只有刚进入的1有用,回到第一个码位对状态,记状态A
begin
state <= A;
end
E: if(x==0)
//连着前面已经输入的x 序列10010考虑,又输入了0码位可以认为第三个码位已对,记状态C
begin
state <= C;
end
else //前功尽弃,只有刚输入的1码位对,记状态为A
begin
state <= A;
end
F: if(x==1) //只有刚输入的1码位对,记状态为A
begin
state <= A;
end
else //又有1码位对,记状态为B
begin
state <= B;
end
G: if(x==1) //只有刚输入的1码位对,记状态为A
begin
state <= F;
end
default:state=IDLE; //缺省状态为初始状态。
endcase
endmodule
//----------------- 测试模块源代码:----------------
//---------------文件名 seqdet.v -------------------
`timescale 1ns/1ns
`include "./seqdet.v"
module seqdet_Top;
reg clk,rst;
reg[23:0] data;
wire[2:0] state;
wire z,x;
assign x=data[23];
always #10 clk = ~clk;
always @(posedge clk)
data={data[22:0],data[23]}; //形成数据向左移环行流,最高位与x 连接
initial
begin
clk=0;
rst=1;
#2 rst=0;
#30 rst=1;
data ='b1100_1001_0000_1001_0100;
#500 $stop;
end
seqdet m(x,z,clk,rst,state);
endmodule
练习九.利用状态机实现比较复杂的接口设计
//--------- 模块源代码:----------------------------
module writing(reset,clk,address,data,sda,ack);
input reset,clk;
input[7:0] data,address;
output sda,ack; //sda负责串行数据输出;
//ack是一个对象操作完毕后,模块给出的应答信号。
reg link_write; //link_write 决定何时输出。
reg[3:0] state; //主状态机的状态字。
reg[4:0] sh8out_state; //从状态机的状态字。
reg[7:0] sh8out_buf; //输入数据缓冲。
reg finish_F; //用以判断是否处理完一个操作对象。
reg ack;
parameter
idle=0,addr_write=1,data_write=2,stop_ack=3;
parameter
bit0=1,bit1=2,bit2=3,bit3=4,bit4=5,bit5=6,bit6=7,bit7=8;
assign sda = link_write? sh8out_buf[7] : 1'bz;
always @(posedge clk)
begin
if(!reset) //复位。
begin
link_write<= 0; //挂起串行单总线
state <= idle;
finish_F <= 0; //结束标志清零
sh8out_state<=idle;
ack<= 0;
sh8out_buf<=0;
end
else
case(state)
idle:
begin
link_write <= 0; //断开串行单总线
finish_F <= 0;
sh8out_state<=idle;
ack<= 0;
sh8out_buf<=address; //并行地址存入寄存器
state <= addr_write; //进入下一个状态
end
addr_write: //地址的输入。
begin
if(finish_F==0)
begin shift8_out; end //地址的串行输出
else
begin
sh8out_state <= idle;
sh8out_buf <= data; //并行数据存入寄存器
state <= data_write;
finish_F <= 0;
end
end
data_write: //数据的写入。
begin
if(finish_F==0)
begin shift8_out; end //数据的串行输出
else
begin
link_write <= 0;
state <= stop_ack;
finish_F <= 0;
ack <= 1; //向信号源发出应答。
end
end
stop_ack: //向信号源发出应答结束。
begin
ack <= 0;
state <= idle;
end
endcase
end
task shift8_out; // 地址和数据的串行输出。
begin
case(sh8out_state)
idle:
begin
link_write <= 1; //连接串行单总线,立即输出地址或数据的最高位(MSB)
sh8out_state <= bit7;
end
bit7:
begin
link_write <= 1; //连接串行单总线
sh8out_state <= bit6;
sh8out_buf <= sh8out_buf<<1; //输出地址或数据的次高位(bit 6)
end
bit6:
begin
sh8out_state<=bit5;
sh8out_buf<=sh8out_buf<<1;
end
bit5:
begin
sh8out_state<=bit4;
sh8out_buf<=sh8out_buf<<1;
end
bit4:
begin
sh8out_state<=bit3;
sh8out_buf<=sh8out_buf<<1;
end
bit3:
begin
sh8out_state<=bit2;
sh8out_buf<=sh8out_buf<<1;
end
bit2:
begin
sh8out_state<=bit1;
sh8out_buf<=sh8out_buf<<1;
end
bit1:
begin
sh8out_state<=bit0;
sh8out_buf<=sh8out_buf<<1; //输出地址或数据的最低位(LSB)
end
bit0:
begin
link_write<= 0; //挂起串行单总线
finish_F<= 1; //建立结束标志
end
endcase
end
endtask
endmodule
// ------------- 测试模块源代码:----------------
`timescale 1ns/100ps
`define clk_cycle 50
module writingTop;
reg reset,clk;
reg[7:0] data,address;
wire ack,sda;
always #`clk_cycle clk = ~clk;
initial
begin
clk=0;
reset=1;
data=0;
address=0;
#(2*`clk_cycle) reset=0;
#(2*`clk_cycle) reset=1;
#(100*`clk_cycle) $stop;
end
always @(posedge ack) //接收到应答信号后,给出下一个处理对象。
begin
data=data+1;
address=address+1;
end
writing writing(.reset(reset),.clk(clk),.data(data),
.address(address),.ack(ack),.sda(sda));
endmodule
1
1
本文档为【Verilog数字系统设计教程(夏宇闻)例题源下册例题练习1-9】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。