您的位置:首页 > 其它

虚拟项目——计数器的verilog实现

2015-10-27 17:57 375 查看
在做这个虚拟项目之前虽然也有参加有关的培训,但如此完整、正规的进行还是第一次。

首先来说一说我所做项目的要求,简单来讲就是在各种条件允许的情况下控制一个寄存器进行向上或向下的计数。当达到门限值的时候进入报警系统,产生一个信号输出持续八个单位并清零寄存器。这个项目的难点在于满足文档要求的同时还要使得报警装置与其相配。再有一个就是testbench的书写,要考虑各种可能出现的错误(源代码与testbench的书写风格完全不同)。

下面说一下在做项目过程中所得到的一些经验。

在写源代码的过程中:尽量用状态机写,刚开始本以为这么简单的功能只用一个always块就够了而且当时用的是quartus写的(quartus不同always块触发条件相同时就报错,这样就没法写状态机了)。但在老师的要求下用状态机重写的时候发现了很多问题:首先状态机的时序与组合逻辑是分离开的,这样有利于条件执行步骤的分离。而只是一个always块只是按着时间的顺序走,很容易遗漏东西,而且在以后的检查过程中很难检查出来;在定下基本的书写风格之后,要仔细研究要求文档,尽量做到完成每一步之后该怎么办,同时确认每一步的执行是否符合要求。直到最后的波形检验的时候仍然能发现与同队其他人功能上有出入的地方;在删减某个变量的时候用查找功能确认逻辑遗漏,不然很容易就发现波形走到莫名其妙。

对于testbench来说,首先要明确它的功能:首先是机遇源代码激励信号;其次是检验执行过程是否有误。前者通过initial和always块完成,后者则通过各式各样的task来实现,目标明确才能做好。这里主要想说一点就是对于某个信号的控制及波形的选择最好是在一个块里完成,以便在执行的过程中不冲突,也便于之后的检查。

最后就是对于波形的检测,通过对testbench和被测代码的仿真通过波形图来确认功能的实现,在遇到问题后要考虑其中的问题,既可能是原来代码有问题也有可能是testbench的问题,这里要想一个侦探一样的通过蛛丝马迹开确定问题所在,并找到解决方法。在检验的过程中我习惯于把所有的信号都列出来以便于查找的过程中没有遗漏,但考虑到之后功能的复杂化,想到的办法是对各个块进行命名,这样便能准确的拉出所有想要的信号,其可行性还有待实践。

计数器代码:

<span style="font-family: Arial, Helvetica, sans-serif;">module cntnub(</span>
//reset & clk//
rst,
clk,
//parameter//
start,
cnt_type,
up_gate,
down_gate,
//output signal//
cpu_irq
);

//clk & reset//
input      rst;
input      clk;
//input signal//
input      start;
input      cnt_type;
input[7:0] up_gate;
input[7:0] down_gate;
//output//
output     cpu_irq;

reg[2:0]   current_state;
reg[2:0]   next_state;
//save the count data//
reg[7:0]   count_data;
reg        cpu_irq;
reg[3:0]   irq_cnt;

parameter s_idle=0;
parameter s_1=1;
parameter s_11=2;
parameter s_12=3;
parameter s_2=4;

//this is a always which let the module stop and rsest when the rst is on//
//if the rst is being on the state will stay s_idle//
//the current_state will run next_state unless the rst is off //
always@(posedge clk) begin
if(!rst)
current_state<=s_idle;
else
current_state<=next_state;
end

//this is an always which is drscribed the combinatorial logic//
//the current_state will run s_1 unless !state//
//if the count_data is the gate ,the cpu_irq will be on and keep on 8 clk//
always@(*) begin
if(rst) begin
case(current_state)
s_idle:
if(start)
next_state=s_1;
else
next_state=s_idle;
s_1://check the datat up or count down//
if(cnt_type)
next_state=s_11;
else
next_state=s_12;
s_11://check the data is the up_gate//
begin
if(count_data==up_gate)
cpu_irq=1;
else
next_state=s_11;
end
s_12://check the data is the down_gate//
begin
if(count_data==down_gate)
cpu_irq=1;
else
next_state=s_12;
end
default: next_state=s_idle;
endcase
end
end

//keep the cpu_irq for 8 clk//
//make sure the codition which the state go//
always@(posedge cpu_irq or negedge rst or posedge clk)	begin
if(!rst)
irq_cnt<=0;
else if(cpu_irq) begin
irq_cnt<=0;

if(clk)
irq_cnt<=irq_cnt+1;
if(irq_cnt==8)begin
cpu_irq<=0;
irq_cnt<=0;
count_data<=0;
end
end
end

//describe the temporal logic //
//make sure the step what you do//
always@(posedge clk or negedge rst) begin
if(!rst)
next_state<=s_idle;
else begin
case(next_state)
s_idle:
begin
cpu_irq<=0;
count_data<=0;
end
s_11://data up count//
begin
count_data<=count_data+1;
end
s_12://data down count//
begin
count_data<=count_data-1;
end
default next_state<=s_idle;
endcase
end
end
endmodule




testbench代码:

`define tc01_00
module top(
);

//clk and rst//
reg         clk;
reg         rst;

//choice data count up or down//
reg         cnt_type;

reg				    start;//drive cntnub count//
reg  [7:0]	 up_gate; //up warning//
reg		[7:0]	 down_gate;//down warning//
reg		[7:0]	cnt_test;

wire			cpu_irq;//waining output signal//
parameter		clk_time=10;

//sampling the system timing//
integer 		timea;
integer 		timeb;

//count the en_start timing//
integer  	cnt;

//count test number//
integer			run_number;

always			#clk_time	clk = ~clk; //time cycle 20ns//

//intital start condition//
initial		begin
up_gate		 = 8'h50;
down_gate	= 8'hef;
clk			    = 1'b0;
rst			    = 1'b0;
#10;
rst			    = 1'b1;

end

//module instantiation//
cntnub   DUT(
.rst(rst),
.clk(clk),
.start(start),
.up_gate(up_gate),
.down_gate(down_gate),
.cpu_irq(cpu_irq),
.cnt_type(cnt_type)
);

//make the tesk run when every posclk//
initial begin
@(posedge rst) begin  end
fork
irq_check();
timer_check();
cnt_type<=0;
join
end

//run `define //
initial begin
@(posedge clk)begin  end
`ifdef
tc01_00 tc01_00(); //start the tesk tc01_00//
`endif
end

//drive signal,cnt:the time of en_start//
task	tc01_00;
begin
for(run_number=0; run_number<10; run_number=run_number+1) begin
for(cnt=0; cnt<1000; cnt=cnt + 1) begin
@(posedge clk)begin  end
start <=	1'b1;
end
start <= 1'b0;
repeat(100) begin
@(posedge clk)begin  end
end
end
$finish;
end
endtask

//check the cpu_irq timeing is 8clk//
task   irq_check;
begin
forever	begin //count the time between cou_irq up and down//
@(posedge cpu_irq) begin
timea		= $time;
end
@(negedge cpu_irq) begin
timeb		=$time;
end
if((timeb-timea)/(2*clk_time) != 8) begin
$display("The Timer cpu_irq output cycle not equal to 8 clock");
$finish;
end else begin
$display("The Timer cpu_irq output cycle is equal to 8 clock");
end
end
end
endtask

//check the timer cou_irq output is right//
task  timer_check;
begin
forever begin
@(posedge clk) begin
if(start) begin
if(cnt_type) begin
@(negedge cpu_irq) begin
up_gate<=up_gate+1;
end
cnt_test = cnt_test + 1;
end else begin
cnt_test = cnt_test - 1;
@(negedge cpu_irq) begin
down_gate<=down_gate-1;
end
end
end else begin
cnt_test	= 0;
end
if(cnt_type) begin
if(cnt < up_gate && cpu_irq) begin //check the timer when up_cout//
$display("The Timer cpu_irq output is Wrong !!!!!");
$finish;
end else if(cnt_test == up_gate+2 && !cpu_irq) begin
$display("The Timer cpu_irq output is Wrong !!!!!");
$finish;
end
else if (cnt_test > (up_gate+9) && cpu_irq) begin
$display("The Timer cpu_irq output is Wrong !!!!!");
$finish;
end
end
else begin//check the tier when down_count//
if(cnt_test > down_gate && cpu_irq) begin
$display("The Timer cpu_irq output is Wrong !!!!!");
$finish;
end else if(cnt_test == down_gate-2 && !cpu_irq) begin
$display("The Timer cpu_irq output is Wrong !!!!!");
$finish;
end else if(cnt_test < (down_gate-9) && cpu_irq) begin
$display("The Timer cpu_irq output is Wrong !!!!!");
$finish;
end
end
end
end
end
endtask

endmodule


验证后所得实验图:


思考与总结

目前对于代码的注释还有很大的问题,而且端口、寄存器名称都是老师预先给的。距离独立设计或者是真正的上手还远远不够,同时状态机、一些基本的语法还不是很熟练的掌握,希望能在以后的学习中逐渐改变才行。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: