0 代码模板
所有代码放到:
这里:所有代码的地址
写代码的模板(时序、逻辑、testbench的模板):
SOC课程——③——Verilog程序(典型电路的模板)
实验报告在:这里
SOC课程——⑥——常见例题和面试题
1例题1
- 请画出以下程序综合后的电路图,并写Testbench,画出仿真波形:
always@(posedge clk)
begin
q0<=~q2;
q1<= q0;
q2<= q1;
end
module代码:
module lianxi01(clk,q_init,q0,q1,q2);
input clk;
input q_init;
output q0,q1,q2;
reg q0,q1,q2;
always@(*)begin
q0 = q_init;
q1 = q_init;
q2 = q_init;
end
always@(posedge clk)begin
q0 <= ~q2;
q1 <= q0 ;
q2 <= q1 ;
end
endmodule
testbench:
`timescale 1ns/1ns
module lianxi01_tb();
reg clk,q_init;
parameter CYCLE = 20;
lianxi01 uut(.clk(clk),.q_init(q_init),.q0(q0),.q1(q1),.q2(q2));
initial begin
clk = 0;
forever
#(CYCLE/2)
clk =~clk;
end
initial begin
# 1
q_init = 0;
end
endmodule
结果分析:
将q0,q1,q2初始化为0,然后开始仿真,得到的仿真波形图如下:
将q0,q1,q2初始化为1,然后开始仿真,得到的仿真波形图如下:
2 例题2
根据HA模块程序,写出引用HA模块描述FA模块的Verilog程序及其Testbench,画出仿真波形。
module
module HA(A,B,S,C);
input A,B ;
output S,C ;
assign {C,S} = A + B;
endmodule
module lianxi02(A,B,Ci,Co,S);
input A,B,Ci;
output Co,S ;
reg Co,S ;
wire cout1,cout2,S1,FA; //定义中间变量
HA ha1(.A(A),.B(B),.S(FA),.C(cout1));
HA ha2(.A(FA),.B(Ci),.S(S1),.C(cout2));
always@(*)begin
Co = cout1 | cout2;
S = S1;
end
endmodule
testbench:
module lianxi02_tb();
reg A,B,Ci;
wire Co,S;
lianxi02 lianxi(.A(A),.B(B),.Ci(Ci),.Co(Co),.S(S));
initial begin
#1
A = 0; B=0; Ci=0;
#10
A = 1; B=1; Ci=0;
#15
A = 0; B=1; Ci=1;
#8
A = 1; B=1; Ci=1;
#10
A = 1; B=0; Ci=1;
#10
A = 0; B=0; Ci=1;
end
endmodule
结果如下图:
结果分析:在这个例子中,进行了五段时间的仿真,分别是:
- A = 0; B=0; Ci=0:当三者都为0时,输出Co和S也为0,验证正确;
- A = 1; B=1; Ci=0:此时HA1输出的为cout1=1,FA=0,HA2输出的cout2=0,S1=0,则Co是cout1或cout2,结果为1,S=S1=0,验证正确;
- A = 0; B=1; Ci=1:此时HA1输出的为cout1=0,FA=1,HA2输出的cout2=1,S1=0,则Co是cout1或cout2,结果为1,S=S1=0,验证正确;
- A = 1; B=1; Ci=1:此时HA1输出的为cout1=1,FA=0,HA2输出的cout2=0,S1=1,则Co是cout1或cout2,结果为1,S=S1=1,验证正确;
- A = 1; B=0; Ci=1:此时HA1输出的为cout1=0,FA=1,HA2输出的cout2=1,S1=0,则Co是cout1或cout2,结果为1,S=S1=0,验证正确;
- A = 0; B=0; Ci=1:此时HA1输出的为cout1=0,FA=0,HA2输出的cout2=0,S1=1,则Co是cout1或cout2,结果为0,S=S1=1,验证正确;
3 例题3
设计一奇偶校验位生成电路,输入八位总线信号bus,输出奇校验位odd信号,偶校验位even信号,写Testbench,并画出仿真波形。
module
module lianxi03(bus,even,odd);
input bus;
output even,odd;
reg even,odd;
always@(*)begin
even = ^bus;
odd = ^~bus;
end
endmodule
testbench
module lianxi03_tb();
reg[7:0] bus;
wire even,odd;
lianxi03 lianxi(.bus(bus),.even(even),.odd(odd));
initial begin
# 1
bus = 8'b00000000;
# 10
bus = 8'b10110101;
# 10
bus = 8'b00110110;
# 10
bus = 8'b11111111;
# 10
bus = 8'b00000001;
end
endmodule
用到逻辑操作的知识点:
仿真结果如下图:
结果说明:
- 当bus里含奇数个1时,even=1,odd=0;
- 当bus里含偶数个1时,even=0,odd=1。
4 例题4,时钟分频问题
利用一个1MHz的输入时钟clk,设计一个带复位端且对输入时钟clk进行三分频模块,写Testbench,并画出仿真波形。设计要求:复位信号为异步复位、低电平有效。
4.1 非50%分频
三分频(时钟为1MHz,占空比为2/3)代码如下:
module
module lianxi04(clk,reset,dout);
input clk,reset;
output dout;
reg dout;
reg[1:0] count;
always@(posedge clk or negedge reset)begin
if(reset == 1'b0) begin
dout <= 0;
count <= 2'b00;
end
else begin
if(count == 0)begin
dout <= ~dout;
count <= count + 1'b1;
end
else if(count == 2)begin
dout <= ~dout;
count <= 2'b00;
end
else begin
count <= count + 1'b1;
end
end
end
endmodule
testbench:
`timescale 1ns/1ns
module lianxi04_tb();
reg clk,reset;
wire out;
parameter CYCLE = 1000;
lianxi04 lianxi(.clk(clk),.reset(reset),.dout(out));
initial begin
clk = 0;
forever
# (CYCLE/2)
clk = ~clk;
end
initial begin
# (2*CYCLE)
reset = 0;
# CYCLE
reset = 1;
end
endmodule
时钟为1MHz,占空比为2/3的三分频仿真结果如下:
4.2 50%分频
三分频(时钟为1MHz,占空比为50%)代码如下:
module:
module lianxi04_3(input clk, input arst, output clk_div );
parameter N = 3;
reg [2:0] cnt;
reg clk_a;
reg clk_b;
wire clk_c;
always@(posedge clk or negedge arst)
begin
if(arst == 1'b0)
cnt <= 0;
else if(cnt == N-1)
cnt <= 0;
else
cnt <= cnt + 1;
end
always@(posedge clk or negedge arst)
begin
if(arst == 1'b0)
clk_a<= 0;
else if(cnt == (N-1)/2 || cnt == N-1)
clk_a<= ~clk_a;
else
clk_a<= clk_a;
end
always@(negedge clk or negedge arst)
begin
if(arst == 1'b0)
clk_b <= 0;
else
clk_b <= clk_a;
end
assign clk_c = clk_a | clk_b;
//N[0]=1奇数,否则偶数
assign clk_div = N[0] ? clk_c : clk_a;
endmodule
testbench
`timescale 1ns/1ns
module lianxi04_3_tb();
reg clk,reset;
wire out;
parameter CYCLE = 1000;
lianxi04_3 lianxi_3(
.clk(clk),
.arst(reset),
.clk_div(out)
);
initial begin
clk = 0;
forever
# (CYCLE/2)
clk = ~clk;
end
initial begin
# (2*CYCLE)
reset = 0;
# CYCLE
reset = 1;
end
endmodule
时钟为1MHz,占空比为50% 的三分频仿真结果如下:
5 例题5
用verilog语言实现一个输出脉冲,要求是12个时钟周期是低电平、5个时钟周期是高电平,写Testbench,并画出仿真波形。
module
module lianxi05(clk,reset,dout);
input clk,reset;
output dout;
reg dout;
reg[4:0] count1;
always@(posedge clk or negedge reset)begin
if(reset == 1'b0)begin
dout<=1'b0;
count1 <= 5'd0;
end
else begin
if(count1 == 5'd0)begin
dout <= ~dout;
count1 <= count1 + 1'b1;
end
else if(count1 == 5'd5) begin
dout <= ~dout;
count1 <= count1 + 1'b1;
end
else if(count1 == 5'd16)begin
dout <= dout;
count1 <= 5'd0;
end
else begin
count1 <= count1 +1;
end
end
end
endmodule
testbench
`timescale 1ns/1ns
module lianxi05_tb();
reg clk,reset;
wire dout;
parameter CYCLE =10;
parameter RST_TIME =2;
lianxi05 lianxi_05(.clk(clk),.reset(reset),.dout(dout));
initial begin
clk = 0;
forever
# (CYCLE/2)
clk = ~clk;
end
initial begin
reset = 1;
# 2
reset = 0;
# (CYCLE * RST_TIME)
reset = 1;
end
endmodule
6 例题6 :平方、立方和阶乘
设计一个带控制端的逻辑运算电路,采用调用function函数的方式,分别完成正整数的平方、立方和阶乘的运算。编写测试模块,并给出仿真波形。
设输入的数据为三位数(0-7之间的正整数),代码如下:
module
module lianxi06(in,s,out);
input[2:0] in;
input[1:0] s;
output[12:0] out;
reg [12:0] out;
always@(*)begin
case(s)
2'b00: out <= 0;
2'b01: out <= square(in);
2'b10: out <= cube(in);
2'b11: out <= factorial(in);
//default:out<= 0;
endcase
end
function [12:0] square;
input[2:0] x;
assign square = x*x;
endfunction
function [12:0] cube;
input[2:0] y;
assign cube = y*y*y;
endfunction
function [12:0] factorial;
input[2:0] z;
reg [2:0] index;
begin
factorial = z?1:0;
for(index = 2;index<=z;index=index+1)
factorial = index*factorial;
end
endfunction
endmodule
testbench
`timescale 1ns/1ns
module lianxi06_tb();
reg [2:0] num_in;
reg [1:0] select_mode;
wire[12:0] num_out;
lianxi06 lianxi_06_6(.in(num_in),.s(select_mode),.out(num_out));
initial begin
#1
num_in = 3'b000;
select_mode = 2'b00;
# 10
num_in = 3'b101;
select_mode = 2'b01;
# 10
num_in = 3'b100;
select_mode = 2'b10;
# 10
num_in = 3'b101;
select_mode = 2'b11;
# 10
num_in = 3'b110;
select_mode = 2'b00;
end
endmodule
结果分析:
- 当num_in=0,select_mode =00时,归0模式,num_out =0,正确;
- 当num_in=5,select_mode =01时,平方模式,num_out =h19=16*1+9=25,正确;
- 当num_in=4,select_mode =10时,立方模式,num_out =h40=16*4+0=64,正确;
- 当num_in=5,select_mode =11时,阶乘模式,num_out =h78=16*7+8=125,正确;
- 当num_in=6,select_mode =00时,归0模式,num_out =0,正确;
7 例题7:状态机
设计一个串行数据检测器,要求是:连续4个或4个以上的1时输出为1,其它输入情况下为0。采用FM状态机编写该模块及测试模块,并存储仿真波形为data_detect.vcd文件。
画出状态图如下:(用笔画)
module
module lianxi07(x,z,clk,rst,state);
input x,clk,rst;
output z;
output[2:0] state;
reg[2:0] state;
wire z;
parameter IDLE=3'd0,A=3'd1,B=3'd2,C=3'd3,D=3'd4;
assign z = ((state==C && x==1)||(state==D && x==1))?1:0;
always @(posedge clk or negedge rst)
begin
if(rst==1'b0) state<=IDLE;
else
casex(state)
IDLE: if(x==1) state<=A;
else state<=IDLE;
A: if(x==1) state<=B;
else state<=IDLE;
B: if(x==1) state<=C;
else state<=IDLE;
C: if(x==1) state<=D;
else state<=IDLE;
D: if(x==1) state<=D;
else state<=IDLE;
default: state<=IDLE;
endcase
end
endmodule
testbench
`timescale 1ns / 1ns
module lianxi07_tb();
reg clk, rst,x;
wire[2:0] state;
wire z;
always #10 clk=~clk;
always @(posedge clk)
begin
x<={$random}%2;
end
initial
begin
clk=0;
rst=1;
#2 rst=0;
#10 rst=1;
#1000 $stop;
end
initial
begin
$dumpfile("lianxi07_dumpfile.vcd");
$dumpvars ;
end
lianxi07 lianxi_07(.x(x),.z(z),.clk(clk),.rst(rst),.state(state));
endmodule
由波形可以看出,当出现4个或4个以上连1的时候,输出1,否则输出0,符合题目的要求。
保存的波形文件如下:于是生成vcd文件,此时可通过vcd2wlf转换为wlf格式,在modelsim中方便查看输入:
vcd2wlf data_detect.vcd data_detect.wlf
vsim -view data_detect.wlf
8 例题8
根据时序图写verilog代码,已知时钟信号clk和输入信号a,要获得如图所示的b.编写该模块及测试模块,并存储仿真波形为b.vcd文件。
module
module lianxi08(clk,a,b);
input clk,a;
output b;
reg b;
always@(a)begin
b <= 1'b1;
end
always@(posedge clk)begin
if(b==1'b1)begin
b<=1'b0;
end
end
endmodule
testbench
`timescale 1ns/1ns
module lianxi08_tb();
reg clk,a;
wire b;
lianxi08 lianxi_08(.clk(clk),.a(a),.b(b));
always #4 clk =~clk;
initial begin
clk = 0;
a = 0;
#13
a = 1;
#34
a =0;
# 17
a =1;
#21
a = 0;
end
initial
begin
$dumpfile("lianxi08_b.vcd");
$dumpvars ;
end
endmodule
由仿真结果可知,当a信号发生变化(包括上升沿和下降沿)的那一刻,B的值立即变化为1,等到时钟信号clk的下一个上升沿到来时,B的值恢复为0。由此验证了模型设计符合要求。
保存的VCD文件如下图:
9 例题9
编写代码完成5个8bits输入数据的比较排序,输入任意8个数据,输出数据为中间值(先比较排列),并编写Testbench。
输入信号定义: clk:时钟输入;ngreset:复位信号;data0、data1……data4:输入数据8bits
输出信号定义: middata:输出数据8bits,中间值
1)两个数比较采用子模块实现;
2)两个数比较调用task方式;
3)两个数比较定义为function ;
module
module lianxi09_compare(a,b,out);
input [7:0] a,b;
output out;
assign out = (a>b)?1:0;
endmodule
module lianxi09(clk,ngreset, data0,data1,data2,data3,
data4,middata);
input clk,ngreset;
input [7:0] data0,data1,data2,data3,data4;
output [7:0] middata;
reg [7:0] middata;
reg [2:0] com_0,com_1,com_2,com_3,com_4;
wire out0_1,out0_2,out0_3,out0_4;
wire out1_0,out1_2,out1_3,out1_4;
wire out2_0,out2_1,out2_3,out2_4;
wire out3_0,out3_1,out3_2,out3_4;
wire out4_0,out4_1,out4_2,out4_3;
always@(posedge clk or negedge ngreset)begin
if(ngreset == 1'b0)begin
com_0 = 3'b000;com_1 = 3'b000;
com_2 = 3'b000;com_3 = 3'b000;
com_4 = 3'b000;
middata <= 0;
end
else begin
if (com_0 == 3'b010) middata <= data0;
else if (com_1 == 3'b010) middata <= data1;
else if (com_2 == 3'b010) middata <= data2;
else if (com_3 == 3'b010) middata <= data3;
else if (com_4 == 3'b010) middata <= data4;
end
end
lianxi09_compare c0_1(.a(data0),.b(data1),.out(out0_1));
lianxi09_compare c0_2(.a(data0),.b(data2),.out(out0_2));
lianxi09_compare c0_3(.a(data0),.b(data3),.out(out0_3));
lianxi09_compare c0_4(.a(data0),.b(data4),.out(out0_4));
lianxi09_compare c1_0(.a(data1),.b(data0),.out(out1_0));
lianxi09_compare c1_2(.a(data1),.b(data2),.out(out1_2));
lianxi09_compare c1_3(.a(data1),.b(data3),.out(out1_3));
lianxi09_compare c1_4(.a(data1),.b(data4),.out(out1_4));
lianxi09_compare c2_0(.a(data2),.b(data0),.out(out2_0));
lianxi09_compare c2_1(.a(data2),.b(data1),.out(out2_1));
lianxi09_compare c2_3(.a(data2),.b(data3),.out(out2_3));
lianxi09_compare c2_4(.a(data2),.b(data4),.out(out2_4));
lianxi09_compare c3_0(.a(data3),.b(data0),.out(out3_0));
lianxi09_compare c3_1(.a(data3),.b(data1),.out(out3_1));
lianxi09_compare c3_2(.a(data3),.b(data2),.out(out3_2));
lianxi09_compare c3_4(.a(data3),.b(data4),.out(out3_4));
lianxi09_compare c4_0(.a(data4),.b(data0),.out(out4_0));
lianxi09_compare c4_1(.a(data4),.b(data1),.out(out4_1));
lianxi09_compare c4_2(.a(data4),.b(data2),.out(out4_2));
lianxi09_compare c4_4(.a(data4),.b(data3),.out(out4_3));
always@(*)begin
com_0 = out0_1 + out0_2 + out0_3 + out0_4;
com_1 = out1_0 + out1_2 + out1_3 + out1_4;
com_2 = out2_0 + out2_1 + out2_3 + out2_4;
com_3 = out3_0 + out3_1 + out3_2 + out3_4;
com_4 = out4_0 + out4_1 + out4_2 + out4_3;
end
endmodule
testbench
`timescale 1ns/1ns
module lianxi09_tb();
reg clk,ngreset;
reg [7:0] data0,data1;
reg [7:0] data2,data3,data4;
wire [7:0] middata;
lianxi09 lianxi_09(.clk(clk),
.ngreset(ngreset),
.data0(data0),.data1(data1),
.data2(data2),
.data3(data3),.data4(data4),
.middata(middata));
always #4 clk =~clk;
initial begin
ngreset = 1;
# 10
ngreset = 0;
# 5 ngreset = 1;
end
initial begin
# 1
clk = 0;
data0=6;data1=8;
data2=9;data3=3;data4=12;
#17
data0=5;data1=4;
data2=6;data3=9;data4=3;
#33
data0=11;data1=6;
data2=25;data3=16;data4=71;
#25
data0=6;data1=83;
data2=5;data3=17;data4=34;
end
endmodule
结果分析:
- 当data0=6;data1=8; data2=9;data3=3;data4=12时:中间值为8,middata=8’h08,结果正确;
- 遇到复位信号(异步复位,低电平有效),复位为0,middata=8’h00,正确;
- 当输入的值发生了变化,data0=5;data1=4; data2=6;data3=9;data4=3;时,要等到时钟信号clk的上升沿到来时,middata的值发生变化,为middata=8’h05,输出的正确;
- 当输入的值发生了变化,data0=11;data1=6; data2=25;data3=16;data4=71;时,要等到时钟信号clk的上升沿到来时,middata的值发生变化,为middata=8’h10=16,输出的正确;
- 当输入的值发生了变化,data0=6;data1=83; data2=5;data3=17;data4=34;时,要等到时钟信号clk的上升沿到来时,middata的值发生变化,为middata=8’h11=17,输出的正确;
- 所以设计的模块符合要求。
采用调用task的方式和function的方式,主体代码和上面的相同,不同点如下:
10 例题10
设计一个序列发生器,根据输入数据(8bits)判别输出1bits的数据,如输入数据属于0-127则输出为0、输入数据属于128-255则输出为1。编写主模块及Testbench。
module
module lianxi10(clk,index,out);
input clk;
output[7:0] index;
output out;
reg[7:0] index;
reg out;
always@(posedge clk)begin
index <= {$random}%256;
if (index <=127) out<=0;
else out<=1;
end
endmodule
testbench
`timescale 1ns/1ns
module lianxi10_tb();
reg clk;
wire[7:0] index;
wire out;
lianxi10 lianxi_10(.clk(clk),.index(index),.out(out));
always #4 clk =~clk;
initial begin
clk = 0;
end
endmodule
在module中设计:当时钟信号clk的上升沿来到时,随机产生一个8位的数字,赋值给变量index,然后对其值进行判断,如果在0-127之间,out输出为0,如果在128-255之间,out输出为1。
由以上的仿真结果可以验证:设计的模型符合要求。
11 例题11
输入时钟clk及复位信号ngreset,输出行同步hsyn、场同步vsyn(场频30fps);要求写成可以合成的RTL code,编写Testbench。
- 其中clk频率为:12MHz(83ns);
- hsyn信号=行消隐(160点) + 行有效(640点);
- vsyn信号=场消隐 (20行)+ 场有效(480行);
- 1s = 时钟周期hsyn总点数vsyn总行数*30fps。
module
module lianxi11(clk,ngreset,hsyn,vsyn);
input clk,ngreset;
output hsyn,vsyn;
reg hsyn,vsyn;
reg[9:0] counter1;
reg[9:0] counter2;
always@(posedge clk or ngreset)begin
if(ngreset == 1'b0)begin
hsyn <= 1'b0;
vsyn <= 1'b0;
counter1 <= 1'b0;
counter2 <= 1'b0;
end
else begin
if(counter1 == 799)begin
counter1 <= 1'b0;
end
else counter1 <= counter1 + 1'b1;
end
end
always@(*)begin
if(counter1<160) hsyn = 1'b0;
else hsyn = 1'b1;
end
always@(posedge hsyn or negedge ngreset)begin
if(ngreset == 1'b0)begin
hsyn <= 1'b0;
vsyn <= 1'b0;
counter1 <= 1'b0;
counter2 <= 1'b0;
end
else begin
if(counter2 == 499)begin
counter2 <= 1'b0;
end
else counter2 <= counter2 + 1'b1;
end
end
always@(*)begin
if(counter2<20) vsyn = 1'b0;
else vsyn = 1'b1;
end
endmodule
testbench
`timescale 1ns/1ns
module lianxi11_tb();
reg clk,ngreset;
wire hsyn,vsyn;
lianxi11 lianxi_11(.clk(clk),.ngreset(ngreset),
.hsyn(hsyn),.vsyn(vsyn));
always #41.5 clk = ~clk;
initial begin
clk=0;
ngreset =1;
# 5;
ngreset =0;
# 3;
ngreset =1;
end
endmodule
由图中可知:
- 每当时钟信号为160个时,hsyn信号变成1,再过640个时钟信号后,hsyn信号变成0;
- 当hsyn信号有20个时,vsyn信号从0变成1,再过480个hsyn信号时,vsyn信号有从0变成了1.
测试复位信号:(由图可知,当复位信号下降沿到来且为0时,hsyn和vsyn信号的置为0,符合设计的要求)
12 例题12 交通灯
module
module lianxi12(clk,reset,count,state);
input clk ;
input reset ;
output [5:0] count;
output [1:0] state ;
reg [5:0] count;
reg [1:0] state ;
parameter S0=0,S1_GREEN=1,S2_YELLOW=2,S3_RED=3;
always@(*)begin
if (count<25) state = S1_GREEN;
else if ((count>=25)&(count<=27))state =S2_YELLOW;
else if ((count>=28)&(count<42))state=S3_RED;
end
always@(posedge clk or posedge reset)begin
if(reset == 1'b1)begin
count <= 0;
state <= S0;
end
else begin
if(count==41)count <= 0;
else count <= count + 1;
end
end
endmodule
testbench
`timescale 1ns/1ns
module lianxi12_tb();
reg clk,reset;
wire[5:0] counter;
wire[1:0] state;
lianxi12 lianxi_12(.clk(clk),.reset(reset),
.count(counter),.state(state));
always #5 clk =~clk;
initial begin
clk = 0;
reset = 0;
# 4
reset = 1;
# 3
reset = 0;
end
endmodule
一个周期后重新计数,以及交通灯的状态从3(红灯)变成1(绿灯)
复位reset为1时,交通灯的状态变成0。