본문 바로가기
나의 전자 공부방/FPGA

vivado, FPGA로 Stopwatch 만들기

by 나만의생각 2023. 5. 15.

FPGA의 FND와 BUTTON을 이용하여 스탑워치를 만들어보겠습니다. 만들기전에 FSM을 제작하여 로직에 대하여 확고히 해야 verilog 코딩하기 편해집니다.
 
 
클럭이 0.005초마다 토글이된다 (클럭이 0.01초마다 발생)
 
 

STOP Watch FSM

 
1) 스탑워치 버튼을 만듭니다 (1) runStop Button, (2) clear Button (3)reset Button
 
 
2) (1)과 (2) 와(3)을 위의 FSM reg를 만들어줍니다.  
 
3) FND를 계속해서 켜주는  ClockDivider 1개와  FND를 밀리초부터 초까지 변경시켜주는 ClockDiver 1개 총 2개를 사용합니다.  같은 클럭을 주게되면 우리 눈에 보이는 숫자는 바뀌지 않는것으로 보이기때문에 크기가 다른 클럭을 줍니다. 
제가 설계한 경우에는 FND에 4개를 번갈아 가는 출력하는것을 좀더 낮은 Hz를 설정하고 시간이 움직이는 Hz를 좀 더 높게 주었습니다.
 
4) 시간을 변경해주는 counter의 경우에는 1의자리, 10의자리, 100의자리, 1000의자리를 설정해주는 reg를 하나 만들어줍니다.
 
5) fnd 4개를 번갈아가면서 을 출력하는 counter의 경우에는 decoder 2x4에 넣어줍니다.'
 
6) fnd에 나타내가 되는 시간의 경우에는 decoder 3x8에 제공하게됩니다.
 
 
위의과정을 다 연결하고 RTL Schematic으로 회로 합성을하게 되면 다음과 같습니다.
 

StopWatch RTL Schematic

 
module stopwatch_button
 

module stopwatch_button(
input in_clk, in_button,
output out_button
);
		


		
parameter PUSHED = 1'b1;
parameter RELEASED = 1'b0;
parameter TRUE = 1'b1;
parameter FALSE = 1'b0;
parameter DEBOUNCE_LIMIT = 500_000;


reg reg_returnValue = FALSE;
reg reg_preState = RELEASED;
reg [31:0] reg_count = 0;
		
assign out_button = reg_returnValue;


always@(posedge in_clk) begin
	if ( (in_button == PUSHED) &&  (reg_preState == RELEASED) && (reg_count < DEBOUNCE_LIMIT) ) begin
		reg_count <= reg_count +1;
		reg_returnValue <= FALSE;
	end
	
	else if ( (in_button==PUSHED) && (reg_preState == RELEASED) && (reg_count == DEBOUNCE_LIMIT) ) begin
	reg_count <= 0;
	reg_preState <= PUSHED;
	reg_returnValue <= TRUE;
	end
	
	else if( (in_button==RELEASED) && (reg_preState == PUSHED) && (reg_count < DEBOUNCE_LIMIT) ) begin
	reg_count <= reg_count +1;
	reg_returnValue <= FALSE;
	end
	
	else if( (in_button==RELEASED) && (reg_preState == PUSHED) && (reg_count == DEBOUNCE_LIMIT) ) begin
	reg_count <= 0;
	reg_preState <= RELEASED;
	reg_returnValue <= FALSE;
	end
	
	else begin
	reg_count<=0;
	reg_returnValue<=FALSE;
	end
end
endmodule

module stopwatch_fsm
 

module stopwatch_fsm
(	input in_clk, in_reset,
	input in_run_stop_button, in_clear_button,
	output out_run_stop, out_reset

);

parameter RUN = 2'b00;
parameter STOP = 2'b01;
parameter RESET = 2'b10;

reg reg_run_stop, reg_reset;
reg [1:0] state =  STOP, next_state;

assign out_run_stop = reg_run_stop;
assign out_reset = reg_reset;

always@ (posedge in_clk or posedge in_reset) begin
	if(in_reset)begin
	state <= STOP;
	end

	else begin
	state <= next_state;
	end
end

always @ (*) begin
	next_state <= 2'bxx;

	case(state)
	RUN: if (in_run_stop_button) next_state <= STOP;
		 else if (in_clear_button) next_state <= RUN;
	
	STOP : if(in_run_stop_button) next_state <= RUN;
			else if (in_clear_button) next_state <= RESET;
			else next_state <= STOP;
	RESET : next_state <= STOP;
	endcase
end

always@(*) begin
	reg_run_stop = 1'bx;
	reg_reset = 1'bx;
    case (state)
	STOP :begin
	       reg_run_stop <= 1'b0;
			reg_reset <= 1'b0;
	end
	RUN : reg_run_stop <= 1'b1;
	RESET : reg_reset <= 1'b1;
	
	endcase
end

endmodule

FSM을 만드는 과정을 이용하였습니다.
 
reg_run_stop, reg_reset의 경우에는 0과 1이 둘다올수있는데, state가 STOP, RUN, RESET일때의 상태를 나타내어 주는 코드입니다.
 
 
module clk_1khz

module clk_1khz(in_clk, in_reset, out_clk);

input in_clk, in_reset;
output out_clk;

reg out_clk = 0;
reg [31:0] count = 0;

always@(posedge in_clk or posedge in_reset) begin
    if(in_reset) begin
		out_clk <= 0;
       count <= 0;
    end
    else begin
        if(count == 50_000-1)begin
       out_clk <= ~out_clk;
       count<=0;
        end
        else begin
            count <= count +1;
        end
    end
end
endmodule

 
클럭의 주파수는 100 MHz이므로, 1초 동안 클럭 신호는 100,000,000 번 발생합니다. MAX_COUNT가 50000이므로, 50000번의 클럭 신호 발생 후에 카운터가 리셋됩니다.

따라서, 50000번의 클럭 신호 발생에 걸리는 시간은 50000 / 100000000 = 0.0005초입니다.

따라서, MAX_COUNT가 50000일 때는 0.0005초가 소요됩니다.
 

module clk_1Mhz(in_clk, in_reset, out_clk);

input in_clk, in_reset;
output out_clk;

reg out_clk = 0;
reg [31:0] count = 0;

always@(posedge in_clk or posedge in_reset) begin
    if(in_reset) begin
		out_clk <= 0;
       count <= 0;
    end
    else begin
        if(count == 500_000-1)begin
       out_clk <= ~out_clk;
       count<=0;
        end
        else begin
            count <= count +1;
        end
    end
end
endmodule

1MHz라 이름을 명명하였지만 중간에 prescale을 수정하였습니다. 
 
클럭의 주파수는 100 MHz이므로, 1초 동안 클럭 신호는 100,000,000 번 발생합니다. MAX_COUNT가 500000이므로, 500000번의 클럭 신호 발생 후에 카운터가 리셋됩니다.

따라서, 500000번의 클럭 신호 발생에 걸리는 시간은 500000 / 100000000 = 0.005초입니다.

따라서, MAX_COUNT가 500000일 때는 0.005초가 소요됩니다.
 
 
 module_counter_10k

module counter_10k(
    input in_clk, in_reset, in_run_stop, in_clear,
    output [13:0] out_count_value
    );

    reg [13:0] out_count_value = 0;

    always @(posedge in_clk or posedge in_reset or posedge in_clear) begin
        if (in_reset || in_clear) begin
            out_count_value <= 0;
        end
        else begin
            if (out_count_value == 10000 - 1) begin
                out_count_value <= 0;
            end
            else begin
                if (in_run_stop) begin
                    out_count_value <= out_count_value + 1;
                end
            end
        end
    end
endmodule

9999까지 나타내기위한 코드입니다.
 
 
 
 
module digit_divider

module digit_divider(
    input [13:0] in_counter,
    output [3:0] out_digit_1, out_digit_10, out_digit_100, out_digit_1000
    );

    assign out_digit_1    = in_counter % 10;
    assign out_digit_10   = in_counter / 10 % 10;
    assign out_digit_100  = in_counter / 100 % 10;
    assign out_digit_1000 = in_counter / 1000 % 6;

endmodule

module mux_4x1

module mux_4x1(in_sel,in_a, in_b, in_c, in_d,out_y);
    input [1:0] in_sel;
    input [3:0] in_a, in_b, in_c, in_d;
    output [3:0] out_y;
    

    reg [3:0] reg_y;

    assign out_y = reg_y;

    always @(in_sel) begin
        reg_y = 0;
        case (in_sel)
        2'd0 : reg_y = in_a;
        2'd1 : reg_y = in_b;
        2'd2 : reg_y = in_c;
        2'd3 : reg_y = in_d;
        endcase
    end
endmodule

 
 
module fnd_decoder

module fnd_decoder(in_data,out_fndFont);
input [3:0]in_data;
output [7:0] out_fndFont;

reg[7:0] out_fndFont;
always@(*)begin

//out_fndFont = 8'hff;
   case (in_data)
            default : out_fndFont = 8'hc0;//
            1 : out_fndFont = 8'hf9;
            2 : out_fndFont = 8'ha4;
            3 : out_fndFont = 8'hb0;
            4 : out_fndFont = 8'h99;
            5 : out_fndFont = 8'h92;
            6 : out_fndFont = 8'h82;
            7 : out_fndFont = 8'hf8;
            8 : out_fndFont = 8'h80;
            9 : out_fndFont = 8'h90;
            
   endcase
end
endmodule

 
module counter

module counter(in_clk, in_reset, out_value);

input in_clk, in_reset;
output [3:0] out_value;

reg[3:0] out_value =0;

always@(posedge in_clk or posedge in_reset) begin


if(in_reset) begin
   out_value <=0;
end

else if (in_clk) begin
   out_value <= out_value+1;
   if(out_value == 9)
   out_value<= 0;
end
end

endmodule

각 자리를 9까지 나타내기 위한 코드입니다.
 
module dec_2x4

module dec_2x4(in_sel, out_sel);
input [1:0] in_sel;
output [3:0] out_sel;

reg[3:0] out_sel;

always@(*) begin
    case(in_sel)
        2'd0: out_sel = 4'b1110;
		2'd1: out_sel = 4'b1101;
		2'd2: out_sel = 4'b1011;
		2'd3: out_sel = 4'b0111;
        
 
    endcase
    end
endmodule

 

module top_stopwatch(
	input in_clk, in_reset,
	input in_run_stop_button, in_clear_button,
	output [7:0] out_fndData,
	output [3:0] out_fndSelect
);

//-------------------button part

clk_1Mhz inst_100MHz(.in_clk(in_clk), .in_reset(in_reset), .out_clk(w_10khz));


wire w_run_stop_button;

stopwatch_button inst_stopwatch_runStop_button(
	.in_clk(in_clk), .in_button(in_run_stop_button),
	.out_button(w_run_stop_button)
	);
wire w_reset_button;
stopwatch_button inst_stopwatch_reset_button(
	.in_clk(in_clk), .in_button(in_clear_button),
	.out_button(w_reset_button)
	);
	
wire w_run_stop, w_reset;

stopwatch_fsm inst_stopwatch_fsm
(	
	.in_clk(in_clk), .in_reset(in_reset),
	.in_run_stop_button(w_run_stop_button), .in_clear_button(w_reset_button),
	.out_run_stop(w_run_stop), .out_reset(w_reset)

);

wire [3:0] w_count_data;
wire [13:0] w_count_10k;

counter_10k inst_counter_10k(
    .in_clk(w_10khz), .in_reset(in_reset), .in_run_stop(w_run_stop), .in_clear(w_reset),
    .out_count_value(w_count_10k)
    );
	
wire [3:0] w_d1,w_d10,w_d100,w_d1000;
digit_divider inst_digit_divider(
    .in_counter(w_count_10k),
    .out_digit_1(w_d1), .out_digit_10(w_d10), .out_digit_100(w_d100), .out_digit_1000(w_d1000)
    );
	
mux_4x1 inst_mux_4x1(.in_sel(w_count), .in_a(w_d1), .in_b(w_d10), .in_c(w_d100), .in_d(w_d1000),.out_y(w_count_data));


fnd_decoder inst_fnd_decoder(.in_data(w_count_data), .out_fndFont(out_fndData) );
//-----------------clock, decoder part

wire w_1khz;
wire [1:0] w_count;
wire w_count_2bit;

clk_1khz inst_clk_1khz(.in_clk(in_clk), .in_reset(in_reset), .out_clk(w_1khz));


counter inst_counter(.in_clk(w_1khz), 
					.in_reset(in_reset), 
					.out_value(w_count)
					);
					
dec_2x4 inst_dec2x4(.in_sel(w_count), .out_sel(out_fndSelect));
//----------------------------

	

endmodule

 

 
다음내용은 다음에