1. 程式人生 > 實用技巧 >verilog入門例項一

verilog入門例項一

verilog入門例項一

  1. 分頻器設計,要求:對輸入時鐘clk,進行分2、5、10分頻。例如輸入時鐘50Mhz,輸出時鐘就是25、10、5Mhz。
    主要思路:
     偶數分頻:假設偶數為EVEN,對時鐘訊號週期進行計數,則先寫一個模(EVEN)的計數器,只要計數至EVEN-1則使輸出訊號翻轉,便形成了偶數分頻。
     奇數分頻:由兩個訊號作或運算而成。假設奇數為ODD。第一個訊號,以時鐘訊號的上跳沿進行計數,形成一個模(ODD)的計數器,令計數至0,1,2...(EVEN-3)/2全部為高電平,其他為低電平,形成一個佔空比不為0.5的訊號。第二個訊號,以時鐘的下跳沿進行計數,形成一個模(ODD)的計數器令計數至0,1,2...(EVEN-1)/2全部為高電平,其他為低電平,形成一個佔空比不為0.5的訊號。這兩個訊號做或運算則可形成奇數分頻。舉例:5分頻。以時鐘上跳沿計數,計數為0,1時為高電平,計數為2,3,4為低電平,如此形成一個訊號1。同理以時鐘下跳沿進行計數,計數為0,1時為高電平,計數為2,3,4為低電平,如此形成一個訊號2。兩個訊號做或運算可以形成奇數分頻訊號。
module divider(
    input clk;
    input rst_n;
    output clk_div2;
    output clk_div5;
    output clk_div10
);
    reg [3:0]cnt1;
    reg [3:0]cnt2;
    reg [3:0]cnt3;
    
 reg clk_div51,clk_div52;
    
    parameter NUM_DIV_ODD ==5;
    parameter NUM_DIV_EVEN ==10;
    
    //二分頻
    always@(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                clk_div2 <= 1'b0;
            else 
                clk_div2 <= ~clk_div2;
        end
    
    //奇數五分頻由兩個佔空比為0.4的訊號相和而成
    
    //第一個上跳沿計數的訊號clk_div51
    always@(posedge clk or negedge rst_n)
        begin
        	if(!rst_n)
                cnt1 <= 1'b0;
            else if (cnt < NUM_DIV - 1)
                cnt1 <= cnt1+1'b1;
            else
                cnt1 <= 1'b0;
        end
    
    always@(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
               clk_div51 <= 1'b0;
            else if(cnt1<NUM_DIV/2)
                clk_div51 <=1'b1;
            else
                clk_div51 <= 1'b0;
        end
    
    //第二個下跳沿計數的訊號clk_div52
    always@(negedge clk or negedge rst_n)
        begin
            if(!rst_n)
                cnt2 <= 1'b0;
            else if (cnt < NUM_DIV -1 )
                cnt2 <= cnt2 + 1'b1;
            else
                cnt <= 1'b0;
        end
    
    always@(negedge clk or negedge rst_n)
        begin
            if(!rst_n)
                clk_div52 <= 1'b0;
            else if (cnt2 < NUM_DIV/2)
                clk_div52 <= 1'b1;
            else
                clk_div52 <= 1'b0;
            
        end
    assign clk_div5 <= clk_div51 | clk_div52;
    
    //十分頻
    always@(posedge clk or negedge rst_n)
        begin
            if (!rst_n)
                begin
                	clk_div10 <= 1'b0;
            		cnt3 <=0;
                end
            else if (cnt3 < NUM_DIV_EVEN/2)
                begin
                    cnt3 <= cnt3 + 1'b1;
                    clk_div10 <= clk_div10;
                end
            else
                begin
                    cnt3 <= 1'b0;
                    clk_div10 <= ~clk_div10;
                end
        end
    

endmodule

testbench

`timescale 1ns/1ps
module tb_divider;
    reg clk;
    reg rst_n;
    
    wire clk_div2;
    wire clk_div5;
    wire clk_div10;
    
    parameter TIME = 20;
    divider uut(
        .clk(clk),
        .rst_div(rst_n),
        .clk_div2(clk_div2),
        .clk_div5(clk_div5),
        .clk_div10(clk_div10)
    );
    
    always@ #(TIME/2) clk = ~clk;
    
    initial
        begin
            clk = 0;
            rst_n = 0;
            #TIME rst_n = 1;
            #(TiME * 80000) $finish;
        end
endmodule

模擬效果:

其中clk_div51和clk_div52是奇數5分頻的訊號1和訊號2。

  1. 訊號燈
    要求:東西方向和南北方向各有四盞燈,分別為左拐燈、綠燈、黃燈和紅燈
    東西方向訊號燈的時間為:紅燈55s,黃燈5s,綠燈40s,左拐燈15s;
    南北方向訊號燈的時間為:紅燈65,黃燈5,綠燈30,左拐燈15s;

思路:把訊號燈分為以上7種狀態,由於有時間要求,則以5s記一次數,訊號燈跑完整個過程,需要120s,則形成一個模24計數器,下面流程圖中白色中的數字就是計數值。

真值表:

Light_ns(南北訊號燈) Light_ew(東西訊號燈) 狀態
左 綠 黃 紅 左 綠 黃 紅
0 0 1 0 0 0 1 0 IDLE
1 0 0 0 0 0 0 1 S1
0 1 0 0 0 0 0 1 S2
0 0 1 0 0 0 0 1 S3
0 0 0 1 1 0 0 0 S4
0 0 0 1 0 1 0 0 S5
0 0 0 1 0 0 1 0 S6

流程圖:

module signal_light(clk,rst_n,light_ns,light_ew,count);
    input clk,rst_n;
    output light_ns,light_ew;//ns:north sourth,ew:east west
    output count;
    //output clk_count;
    reg[3:0]light_ns,light_ew;
    reg[4:0]count;
    reg[2:0]current_state,next_state;

    
    
    parameter IDLE=3'b000;
    parameter S1=3'b001;
    parameter S2=3'b010;
    parameter S3=3'b011;
    parameter S4=3'b100;
    parameter S5=3'b101;
    parameter S6=3'b110;

   // reg clk_count;
    //reg count1;
    //parameter T=9'd250000000;
/*
 always@(posedge clk or negedge rst_n)
        begin
            if (!rst_n)
                begin
                	clk_count <= 1'b0;
            		cnt1 <=0;
                end
            else if (cnt1 < T/2)
                begin
                    cnt1 <= cnt1 + 1'b1;
                    clk_count <= clk_count;
                end
            else
                begin
                    cnt1 <= 1'b0;
                    clk_count <= ~clk_count;
                end
        end
    */
    //counter
    always@(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                count<=1'b0;
            else if(count==5'b10111)
                count<=1'd0;
            else
                count<=count+1'b1;
                
        end
    
    always@(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                current_state<=IDLE;
            else
                current_state<=next_state;
        end
    
    always@(*)
        begin
            case(current_state)
                IDLE:if(!count)next_state=S1;
              		 else next_state=IDLE;
                
                S1:if(count==2)next_state=S2;
                   else next_state=S3;
                
                S2:if(count==9)next_state=S3;
                   else next_state=S2;
                
                S3:begin
			if(count==3)next_state=S2;
                   	else 
                           begin
                               if(count==10)next_state=S4;
                               else next_state=S3;
			    end
		   end
                
                S4:if(count==13)next_state=S6;
                   else next_state=S4;
                
                S5:if(count==22)next_state=S6;
                   else next_state=S5;
                
                S6:begin
			if(count==14)next_state=S5;
                           else 
                                 begin
                                       if(count==23)next_state=S1;
                                       else next_state=S6;
                                 end
                end
                
             default:next_state=IDLE;
            endcase
         end

    always@(posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                begin
                    light_ns<=4'b0010;
                    light_ew<=4'b0010;
                end
            else 
		case(next_state)
                IDLE   :begin
			light_ns<=4'b0010;
			light_ew<=4'b0010;
			end
                S1     :begin
			light_ns<=4'b1000;
                        light_ew<=4'b0001;
			end
                S2     :begin
			light_ns<=4'b0100;
                        light_ew<=4'b0001;
			end
                S3     :begin
			light_ns<=4'b0010;
                        light_ew<=4'b0001;
			end
                S4     :begin
		light_ns<=4'b0001;
                        light_ew<=4'b1000;
			end
                S5     :begin
			light_ns<=4'b0001;
                        light_ew<=4'b0100;
			end
                S6     :begin
			light_ns<=4'b0001;
                        light_ew<=4'b0010;
			end
                default:begin
			light_ns<=4'b0010;
                        light_ew<=4'b0010;
			end
            endcase
        end
    
endmodule

testbench

`timescale 1ns/1ps
module tb_signal_light;
reg clk;
reg rst_n;

wire [4:0]count;

wire [3:0]light_ew;
wire [3:0]light_ns;
//wire clk_count;

parameter TIME=20;

signal_light uut(
    .clk(clk),
    .rst_n(rst_n),
    .light_ns(light_ns),
    .light_ew(light_ew),
    .count(count)
    //.clk_count(clk_count)
);
 always #10 clk = ~clk;
    
    initial
        begin
            clk = 0;
            rst_n = 0;
            #TIME rst_n = 1;
            //#(TiME * 80000) $finish;
        end
endmodule

模擬結果:

結果中還有一個問題就是未分頻,應該是要寫一個週期的5s的時鐘訊號。