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

        在做这个虚拟项目之前虽然也有参加有关的培训,但如此完整、正规的进行还是第一次。

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

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

  在写源代码的过程中:尽量用状态机写,刚开始本以为这么简单的功能只用一个always块就够了而且当时用的是quartus写的(quartus不同always块触发条件相同时就报错,这样就没法写状态机了)。但在老师的要求下用状态机重写的时候发现了很多问题:首先状态机的时序与组合逻辑是分离开的,这样有利于条件执行步骤的分离。而只是一个always块只是按着时间的顺序走,很容易遗漏东西,而且在以后的检查过程中很难检查出来;在定下基本的书写风格之后,要仔细研究要求文档,尽量做到完成每一步之后该怎么办,同时确认每一步的执行是否符合要求。直到最后的波形检验的时候仍然能发现与同队其他人功能上有出入的地方;在删减某个变量的时候用查找功能确认逻辑遗漏,不然很容易就发现波形走到莫名其妙。
  对于testbench来说,首先要明确它的功能:首先是机遇源代码激励信号;其次是检验执行过程是否有误。前者通过initial和always块完成,后者则通过各式各样的task来实现,目标明确才能做好。这里主要想说一点就是对于某个信号的控制及波形的选择最好是在一个块里完成,以便在执行的过程中不冲突,也便于之后的检查。

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


计数器代码:

module cntnub(
//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 







验证后所得实验图:虚拟项目——计数器的verilog实现_第1张图片







思考与总结

目前对于代码的注释还有很大的问题,而且端口、寄存器名称都是老师预先给的。距离独立设计或者是真正的上手还远远不够,同时状态机、一些基本的语法还不是很熟练的掌握,希望能在以后的学习中逐渐改变才行。

你可能感兴趣的:(fpga)