Verilog 스톱워치
`timescale 1ns / 1ps
module watch_top(
input clk,
input [1:0] btn,
output [3:0] com_an,
output [6:0] seg
);
// wire clk_sec;
reg [3:0] sec [1:0];
reg [3:0] min [1:0];
wire [1:0] debounced_btn;
clock_usec uCLK (.clk(clk), .clk_usec(clk_usec));
clock_msec mCLK (.clk_usec(clk_usec), .clk_msec(clk_msec));
clock_sec sCLK (.clk_msec(clk_msec), .clk_sec(clk_sec));
clock_min MCLK (.clk_sec(clk_sec), .clk_min(clk_min));
D_flip_flop_posedge dff_sec (.D(btn[0]), .E(clk_msec), .Q(debounced_btn[0]));
D_flip_flop_posedge dff_min (.D(btn[1]), .E(clk_msec), .Q(debounced_btn[1]));
xor (inc_sec, clk_sec, debounced_btn[0]);
xor (inc_min, clk_min, debounced_btn[1]);
always @(negedge inc_sec) begin
if(sec[0] >= 9) begin
sec[0] = 0;
if (sec[1] >= 5)begin
sec[1] = 0;
end
else sec[1] = sec[1] + 1;
end
else sec[0] = sec[0] + 1;
end
always @(negedge inc_min) begin
if(min[0] >= 9) begin
min[0] = 0;
if (min[1] >= 5)begin
min[1] = 0;
end
else min[1] = min[1] + 1;
end
else min[0] = min[0] + 1;
end
FND_4digit_switcher FND_4digit (
.value_1(sec[0]),
.value_10(sec[1]),
.value_100(min[0]),
.value_1000(min[1]),
.clk(clk),
.com_an(com_an),
.seg(seg)
);
endmodule
watch 코드
D_flip_flop_posedge dff_sec (.D(btn[0]), .E(clk_msec), .Q(debounced_btn[0]));
부분에서 Q는 임피던스.
플립플롭은 초기화가 안돼서 0101으로 시작함.
`timescale 1ns / 1ps
module clock_usec(
input clk,
output reg clk_usec
);
reg [6:0] cnt_usec = 0;
wire clk_100MHz;
assign clk_100MHz = clk;
always @ (negedge clk_100MHz)begin //us
if(cnt_usec >= 99) cnt_usec = 0;
else cnt_usec = cnt_usec + 1;
if(cnt_usec <= 49) clk_usec = 0;
else clk_usec = 1;
end
endmodule
module clock_msec(
input clk_usec,
output reg clk_msec
);
reg [9:0] cnt_msec = 0;
always @ (negedge clk_usec)begin //us
if(cnt_msec >= 999) cnt_msec = 0;
else cnt_msec = cnt_msec + 1;
if(cnt_msec <= 499) clk_msec = 0;
else clk_msec = 1;
end
endmodule
module clock_sec(
input clk_msec,
output reg clk_sec
);
reg [5:0] cnt_sec = 0;
always @ (negedge clk_msec)begin //us
if(cnt_sec >= 999) cnt_sec = 0;
else cnt_sec = cnt_sec + 1;
if(cnt_sec <= 499) clk_sec = 0;
else clk_sec = 1;
end
endmodule
module clock_min(
input clk_sec,
output reg clk_min
);
reg [5:0] cnt_min = 0;
always @(negedge clk_sec) begin
if(cnt_min >= 59) cnt_min = 0;
else cnt_min = cnt_min + 1;
if (cnt_min <= 29) clk_min = 0;
else clk_min = 1;
end
endmodule
clock library
버튼으로 59초 넘기면 1분 오르게 하기
빨간 네모 칸을 inc_sec으로 바꿔줬다.
module stop_watch_top(
input clk,
input [2:0] btn,
output [3:0] com_an,
output [6:0] seg,
output reg [3:0] led
);
wire btn_start, btn_lap, btn_clear;
wire reset_n, start, clk_start;
reg [3:0] sec [1:0];
reg [3:0] min [1:0];
not (reset_n, btn_clear); //btn을 안누르면 1이기 때문에 반전시켜줘야함.
T_flip_flop_posedge tff_start (.T(1), .clk(btn_start), .reset_n(reset_n), .preset_n(1), .Q(start)); //preset은 0에서 작동하니 1을 준다.
and (clk_start, start, clk_msec); //clk_start가 1일때 start 가 1
always @(negedge clk_sec or negedge reset_n) begin //start가 1일 때 작동하고 아닐 때 멈춤
if(!reset_n)begin
sec[0] = 0;
sec[1] = 0;
end
else begin
if(sec[0] >= 9) begin
sec[0] = 0;
if (sec[1] >= 5)begin
sec[1] = 0;
end
else sec[1] = sec[1] + 1;
end
else
sec[0] = sec[0] + 1;
end
end
always @(negedge clk_min or negedge reset_n) begin
if(!reset_n) begin
min[0] = 0;
min[1] = 0;
end
else begin
if(min[0] >= 9) begin
min[0] = 0;
if (min[1] >= 5)begin
min[1] = 0;
end
else min[1] = min[1] + 1;
end
else min[0] = min[0] + 1;
end
end
D_flip_flop_posedge dff_start (.D(btn[0]), .E(clk_msec), .Q(btn_start)); //Q출력이 임피던스, 플립플롭은 초기화가 안돼서 0101 시작됨
D_flip_flop_posedge dff_lap (.D(btn[1]), .E(clk_msec), .Q(btn_lap));
D_flip_flop_posedge dff_clear (.D(btn[2]), .E(clk_msec), .Q(btn_clear));
clock_usec uCLK (.clk(clk), .clk_usec(clk_usec));
clock_msec mCLK (.clk_usec(clk_usec), .clk_msec(clk_msec));
clock_sec sCLK (.clk_msec(clk_start), .clk_sec(clk_sec));
clock_min MCLK (.clk_sec(clk_sec), .clk_min(clk_min));
FND_4digit_switcher FND_4digit (
.value_1(sec[0]),
.value_10(sec[1]),
.value_100(min[0]),
.value_1000(min[1]),
.clk(clk),
.com_an(com_an),
.seg(seg)
);
endmodule
clear 기능을 넣어 stopwatch를 만듦.
그러나 cnt_min을 초기화 해주지 않아(다른 코드에 있음) 5초에 clear를 누르면 55초에 1분으로 변함
cnt_min을 초기화 해주는 기능 추가.
다음과 같이 reset_n을 통해 cnt_min을 0으로 초기화 해준다.
cnt_min 뿐만 아니다. 모든 시간 관련 코드를 reset_n = 0으로 만들어야 한다.
always문을 따로 만들면 안되고 같이 해줘야 한다.
`timescale 1ns / 1ps
module clock_usec(
input clk,
input reset_n,
output reg clk_usec
);
reg [6:0] cnt_usec = 0;
wire clk_100MHz;
assign clk_100MHz = clk;
always @ (negedge clk_100MHz or negedge reset_n)begin //us
if(!reset_n)begin
cnt_usec = 0;
end
else begin
if(cnt_usec >= 99) cnt_usec = 0;
else cnt_usec = cnt_usec + 1;
if(cnt_usec <= 49) clk_usec = 0;
else clk_usec = 1;
end
end
endmodule
module clock_msec(
input clk_usec,
input reset_n,
output reg clk_msec
);
reg [9:0] cnt_msec = 0;
always @ (negedge clk_usec or negedge reset_n)begin //us
if(!reset_n)begin
cnt_msec = 0;
end
else begin
if(cnt_msec >= 999) cnt_msec = 0;
else cnt_msec = cnt_msec + 1;
if(cnt_msec <= 499) clk_msec = 0;
else clk_msec = 1;
end
end
endmodule
module clock_sec(
input clk_msec,
input reset_n,
output reg clk_sec
);
reg [9:0] cnt_sec = 0;
always @ (negedge clk_msec or negedge reset_n)begin //us
if(!reset_n)begin
cnt_sec = 0;
end
else begin
if(cnt_sec >= 999) cnt_sec = 0;
else cnt_sec = cnt_sec + 1;
if(cnt_sec <= 499) clk_sec = 0;
else clk_sec = 1;
end
end
endmodule
module clock_min(
input clk_sec,
input reset_n,
output reg clk_min
);
reg [5:0] cnt_min = 0;
always @(negedge clk_sec or negedge reset_n) begin //always문을 따로 만들면 안됨
if(!reset_n)begin
cnt_min = 0;
end
else begin
if(cnt_min >= 59) cnt_min = 0;
else cnt_min = cnt_min + 1;
if (cnt_min <= 29) clk_min = 0;
else clk_min = 1;
end
end
endmodule
clock_lib를 다시 만들어줬다.
reset_n을 모든 cnt에 추가하였다.
reset_n을 사용해 시간들을 0으로 초기화 안해주면 0으로 초기화가 안된 상태에서 작동하기 때문에
ex)0에서 55초까지 실행하고 clear 후 5초를 실행하면 1분으로 바뀐다.
이러한 현상을 막기위해 시간 관련 코드를 전부 0으로 초기화 해줘야 막을 수 있다.
`timescale 1ns / 1ps
module FND_4digit_switcher( // 1ms에 1번씩 com_an 단자 바꿔줌
input [3:0] value_1,
input [3:0] value_10,
input [3:0] value_100,
input [3:0] value_1000,
input clk,
output reg [3:0] com_an = 4'b1110,
output [6:0] seg
);
wire clk_usec, clk_msec;
reg [3:0] hex_value;
clock_usec UCLK (.clk(clk), .reset_n(1), .clk_usec(clk_usec));
clock_msec MCLK (.clk_usec(clk_usec), .reset_n(1), .clk_msec(clk_msec));
decoder_7_seg dec7seg(.hex_value(hex_value), .seg(seg));
always @(negedge clk_msec) begin
case (com_an)
4'b1110: begin
com_an = 4'b1101;
hex_value = value_10;
end
4'b1101: begin
com_an = 4'b1011;
hex_value = value_100;
end
4'b1011: begin
com_an = 4'b0111;
hex_value = value_1000;
end
4'b0111: begin
com_an = 4'b1110;
hex_value = value_1;
end
default : begin
com_an = 4'b1110;
hex_value = value_1;
end
endcase
end
endmodule
7세그먼트 또한 바꿔줌.
`timescale 1ns / 1ps
module stop_watch_top(
input clk,
input [2:0] btn,
output [3:0] com_an,
output [6:0] seg,
output reg [3:0] led
);
wire btn_start, btn_lap, btn_clear;
wire reset_n, start, clk_start;
reg [3:0] sec [1:0];
reg [3:0] min [1:0];
not (reset_n, btn[2]); //btn을 안누르면 1이기 때문에 반전시켜줘야함.
T_flip_flop_posedge tff_start (.T(1), .clk(btn_start), .reset_n(reset_n), .preset_n(1), .Q(start)); //preset은 0에서 작동하니 1을 준다.
and (clk_start, start, clk_msec); //clk_start가 1일때 start 가 1
always @(negedge clk_sec or negedge reset_n) begin //start가 1일 때 작동하고 아닐 때 멈춤
if(!reset_n)begin
sec[0] = 0;
sec[1] = 0;
end
else begin
if(sec[0] >= 9) begin
sec[0] = 0;
if (sec[1] >= 5)begin
sec[1] = 0;
end
else sec[1] = sec[1] + 1;
end
else
sec[0] = sec[0] + 1;
end
end
always @(negedge clk_min or negedge reset_n) begin
if(!reset_n) begin
min[0] = 0;
min[1] = 0;
end
else begin
if(min[0] >= 9) begin
min[0] = 0;
if (min[1] >= 5)begin
min[1] = 0;
end
else min[1] = min[1] + 1;
end
else min[0] = min[0] + 1;
end
end
D_flip_flop_posedge dff_start (.D(btn[0]), .E(clk_msec), .Q(btn_start)); //Q출력이 임피던스, 플립플롭은 초기화가 안돼서 0101 시작됨
D_flip_flop_posedge dff_lap (.D(btn[1]), .E(clk_msec), .Q(btn_lap));
clock_usec uCLK (.clk(clk), .reset_n(reset_n), .clk_usec(clk_usec));
clock_msec mCLK (.clk_usec(clk_usec), .reset_n(reset_n), .clk_msec(clk_msec));
clock_sec sCLK (.clk_msec(clk_start), .reset_n(reset_n), .clk_sec(clk_sec));
clock_min MCLK (.clk_sec(clk_sec), .reset_n(reset_n), .clk_min(clk_min));
FND_4digit_switcher FND_4digit (
.value_1(sec[0]),
.value_10(sec[1]),
.value_100(min[0]),
.value_1000(min[1]),
.clk(clk),
.com_an(com_an),
.seg(seg)
);
endmodule
시간 관련 함수를 0으로 초기화 해주고 진행하면 clear시 겹치지 않고 다시 시간이 start됨
30초가 넘으면 1분이 나오는 현상찾기
29초 이하일 때는
else begin
if(cnt_min >= 59) cnt_min = 0;
else cnt_min = cnt_min + 1;
if (cnt_min <= 29) clk_min = 0;
else clk_min = 1;
end
위 코드 두 번째 if문에서 하강에지가 발생하기 때문에
else min[0] = min[0] + 1;
stop_watch 코드에서 위와 같은 코드에 의해 1이 출력됨.
`timescale 1ns / 1ps
module clock_usec(
input clk,
input reset_n,
output reg clk_usec
);
reg [6:0] cnt_usec = 0;
wire clk_100MHz;
assign clk_100MHz = clk;
always @ (negedge clk_100MHz or negedge reset_n)begin //us
if(!reset_n)begin
cnt_usec = 0;
end
else begin
if(cnt_usec >= 99) cnt_usec = 0;
else cnt_usec = cnt_usec + 1;
if(cnt_usec < 99) clk_usec = 0;
else clk_usec = 1;
end
end
endmodule
module clock_msec(
input clk_usec,
input reset_n,
output reg clk_msec
);
reg [9:0] cnt_msec = 0;
always @ (negedge clk_usec or negedge reset_n)begin //us
if(!reset_n)begin
cnt_msec = 0;
end
else begin
if(cnt_msec >= 999) cnt_msec = 0;
else cnt_msec = cnt_msec + 1;
if(cnt_msec < 999) clk_msec = 0;
else clk_msec = 1;
end
end
endmodule
module clock_sec(
input clk_msec,
input reset_n,
output reg clk_sec
);
reg [9:0] cnt_sec = 0;
always @ (negedge clk_msec or negedge reset_n)begin //us
if(!reset_n)begin
cnt_sec = 0;
end
else begin
if(cnt_sec >= 999) cnt_sec = 0;
else cnt_sec = cnt_sec + 1;
if(cnt_sec < 999) clk_sec = 0;
else clk_sec = 1;
end
end
endmodule
module clock_min(
input clk_sec,
input reset_n,
output reg clk_min
);
reg [5:0] cnt_min = 0;
always @(negedge clk_sec or negedge reset_n) begin //always문을 따로 만들면 안됨
if(!reset_n)begin
cnt_min = 0;
end
else begin
if(cnt_min >= 59) cnt_min = 0;
else cnt_min = cnt_min + 1;
if (cnt_min < 59) clk_min = 0;
else clk_min = 1;
end
end
endmodule
오류를 막기위해 시간 관련 코드를 49 => 99, 499 => 999로 바꿔준다.
물론 완전히 오류가 잡히는 것은 아닌데 확률이 1/1000으로 줄어든다. (임시방편)
1ms는 미미해서 눈으로 보기 힘들다.
손으로 누르는 것이기 때문에 시간이 길어서 가능한 것.
만약 clk을 줘서 실행하는거면 다른 기능을 써야하는데 아직 못배움.
clock_lib에서 각 시간마다 clk_시간 쪽을 초기화 해줘도 된다.
어디까지나 임시방편일뿐, 배우지 못한 진도가 있어 자세하게 오류를 잡긴 못한다.
always @(negedge clk_sec or negedge reset_n) begin //always문을 따로 만들면 안됨
if(!reset_n)begin
cnt_min = 0;
clk_min = 0;
end
else begin
if(cnt_min >= 59) cnt_min = 0;
else cnt_min = cnt_min + 1;
if (cnt_min < 59) clk_min = 0;
else clk_min = 1;
end
end
코드 수정 후 30초가 지난 후에 clear 하고 start를 눌러도 잘 작동하는 모습
lap기능 추가하기
`timescale 1ns / 1ps
module stop_watch_top(
input clk,
input [2:0] btn,
output [3:0] com_an,
output [6:0] seg,
output reg [3:0] led
);
wire btn_start, btn_lap, btn_clear;
wire reset_n, start, clk_start;
reg [3:0] sec [1:0];
reg [3:0] min [1:0];
reg [3:0] lap [3:0]; //lap btn을 누르면 멈춘 시간을 보여주지만 시간은 가는 기능
reg [3:0] out_value [3:0];
always @ (lap_e)begin
if(lap_e)begin //mux : 조합회로
out_value[0] = lap[0];
out_value[1] = lap[1];
out_value[2] = lap[2];
out_value[3] = lap[3];
end
else begin
out_value[0] = sec[0];
out_value[1] = sec[1];
out_value[2] = min[2];
out_value[3] = min[3];
end
end
always @ (posedge btn_lap or negedge reset_n)begin //손으로 눌렀다 뗐을 때 현재의 값으로 바뀌니 posedge
if(!reset_n)begin
lap[0] = 0;
lap[1] = 0;
lap[2] = 0;
lap[3] = 0;
end
else begin
lap[0] = sec[0];
lap[1] = sec[1];
lap[2] = min[0];
lap[3] = min[1];
end
end
not (reset_n, btn[2]); //btn을 안누르면 1이기 때문에 반전시켜줘야함.
T_flip_flop_posedge tff_start (.T(1), .clk(btn_start), .reset_n(reset_n), .preset_n(1), .Q(start)); //preset은 0에서 작동하니 1을 준다.
T_flip_flop_posedge tff_lap (.T(1), .clk(btn_lap), .reset_n(reset_n), .preset_n(1), .Q(lap_e)); //preset은 0에서 작동하니 1을 준다.
//lap_enable
and (clk_start, start, clk_msec); //clk_start가 1일때 start 가 1
always @(negedge clk_sec or negedge reset_n) begin //start가 1일 때 작동하고 아닐 때 멈춤
if(!reset_n)begin
sec[0] = 0;
sec[1] = 0;
end
else begin
if(sec[0] >= 9) begin
sec[0] = 0;
if (sec[1] >= 5)begin
sec[1] = 0;
end
else sec[1] = sec[1] + 1;
end
else
sec[0] = sec[0] + 1;
end
end
always @(negedge clk_min or negedge reset_n) begin
if(!reset_n) begin
min[0] = 0;
min[1] = 0;
end
else begin
if(min[0] >= 9) begin
min[0] = 0;
if (min[1] >= 5)begin
min[1] = 0;
end
else min[1] = min[1] + 1;
end
else min[0] = min[0] + 1;
end
end
D_flip_flop_posedge dff_start (.D(btn[0]), .E(clk_msec), .Q(btn_start)); //Q출력이 임피던스, 플립플롭은 초기화가 안돼서 0101 시작됨
D_flip_flop_posedge dff_lap (.D(btn[1]), .E(clk_msec), .Q(btn_lap));
clock_usec uCLK (.clk(clk), .reset_n(reset_n), .clk_usec(clk_usec));
clock_msec mCLK (.clk_usec(clk_usec), .reset_n(reset_n), .clk_msec(clk_msec));
clock_sec sCLK (.clk_msec(clk_start), .reset_n(reset_n), .clk_sec(clk_sec));
clock_min MCLK (.clk_sec(clk_sec), .reset_n(reset_n), .clk_min(clk_min));
FND_4digit_switcher FND_4digit (
.value_1(out_value[0]),
.value_10(out_value[1]),
.value_100(out_value[2]),
.value_1000(out_value[3]),
.clk(clk),
.com_an(com_an),
.seg(seg)
);
endmodule
lap기능 추가 코드