기존에 진행했던 cook timer와 stopwatch를 합친 다목적 시계 프로젝트를 진행하였다.
button 4번을 통해 mode변경을 할 수 있게 하였고, cook timer와 stopwatch를 번갈아가며 사용할 수 있다.
cook timer 에는 시작 / 멈춤, 분 증가, 리셋, 초 증가 기능이 있다.
stopwatch 에는 시작 / 멈춤, 기록, 리셋 기능이 있다.
각 버튼마다 기능을 설명해놨다.
파란 글씨는 cook timer, 검은 글씨는 stopwatch
mode는 mode change를 위해 사용한다.
노이즈 제거를 위해 clock_divider와 D flipflop을 사용하였고, button 4로 모드변경을 위해 T flipflop과 demux를 사용했다.
또한 결합연산자를 활용하여 mux를 제작하였다.
다목적 시계 top모듈
`timescale 1ns / 1ps
module multiple_watch(
input clk,
input [4:0] btn,
output [3:0] com_an,
output [6:0] seg
);
wire mode;
wire mode_sel;
wire [4:0] btn_s;
wire [4:0] btn_c;
wire [3:0] com_an_s, com_an_c;
wire [6:0] seg_s, seg_c;
wire clk_160ns,clk_2560ns,clk_40us, clk_655us;
clock_divider cd_16(.clk(clk), .sel(3), .clk_out(clk_160ns)); //160ns
clock_divider cd_256(.clk(clk_160ns), .sel(3), .clk_out(clk_2560ns)); //2560ns
clock_divider cd_4096(.clk(clk_2560ns), .sel(3), .clk_out(clk_40us)); //40us
clock_divider cd_8192(.clk(clk_40us), .sel(3), .clk_out(clk_655us)); //655us
stop_watch_top swt(.clk(clk), .btn(btn), .com_an(com_an_s), .seg(seg_s));
cook_timer_top ctt(.clk(clk), .btn(btn), .com_an(com_an_c), .seg(seg_c));
D_flip_flop_posedge dff(.D(btn[4]), .E(clk_655us), .Q(mode));
T_flip_flop_posedge btn_mode (.T(1), .clk(mode), .reset_n(1), .preset_n(1), .Q(mode_sel));
demux_1_2 dm0(.D(btn[0]), .S(mode_sel), .F({btn_s[0], btn_c[0]}));
demux_1_2 dm1(.D(btn[1]), .S(mode_sel), .F({btn_s[1], btn_c[1]}));
demux_1_2 dm2(.D(btn[2]), .S(mode_sel), .F({btn_s[2], btn_c[2]}));
demux_1_2 dm3(.D(btn[3]), .S(mode_sel), .F({btn_s[3], btn_c[3]}));
assign btn_s[3] = btn_c[3];
assign com_an = mode_sel ? com_an_s : com_an_c;
assign seg = mode_sel ? seg_s : seg_c;
endmodule
cook timer 코드
`timescale 1ns / 1ps
module cook_timer_top(
input clk,
input [3:0] btn,
output [3:0] com_an,
output [6:0] seg,
output [3:0] led
);
wire reset_n;
wire btn_down, btn_incsec, btn_incmin, btn_reset;
wire dec_clk;
wire clk_up_down_sec;
wire down;
wire [3:0] sec [1:0]; //4bit중 2bit를 할당한다는 뜻 같음
wire [3:0] min [1:0]; //module의 output으로 받는거니까 wire로 받아야함
wire led_on;
reg time_out;
//time_out = |{sec[0], sec[1], min[0], min[1]}; //16bit
assign reset_n = ~reset_edge; //btn은 눌러야하니까 반전시켜서 btn은 1이였다가 0으로 된다. 임피던스였다가 -> 0
//reset버튼을 누른것처럼 하려고 작성한것. 1010이 나오던걸 없애려면 reset버튼을 눌러야하는데 이 코드를 넣어서 없앤 것처럼 하는거임
//{ PACKAGE_PIN U18 IOSTANDARD LVCMOS33 } [get_ports {btn[2]}]
D_flip_flop_posedge dff_down (.D(btn[1]), .E(clk_msec), .Q(btn_down));
//{ PACKAGE_PIN T18 IOSTANDARD LVCMOS33 } [get_ports btn[1]]
D_flip_flop_posedge dff_incsec (.D(btn[3]), .E(clk_msec), .Q(btn_incsec));
//{ PACKAGE_PIN T17 IOSTANDARD LVCMOS33 } [get_ports {btn[3]}]
D_flip_flop_posedge dff_incmin (.D(btn[0]), .E(clk_msec), .Q(btn_incmin));
// { PACKAGE_PIN W19 IOSTANDARD LVCMOS33 } [get_ports {btn[0]}]
D_flip_flop_posedge dff_reset (.D(btn[2]), .E(clk_msec), .Q(btn_reset));
edge_detect ed_reset (.clk(clk), .cp_in(btn_reset), .reset_n(1), .n_edge(reset_edge));
clock_usec uCLK (.clk(clk), .reset_n(reset_n), .clk_usec(clk_usec));
clock_msec mCLK (.clk(clk), .clk_usec(clk_usec), .reset_n(reset_n), .clk_msec(clk_msec));
clock_sec sCLK (.clk(clk), .clk_msec(clk_msec), .reset_n(reset_n), .clk_sec(clk_sec));
clock_min MCLK (.clk(clk), .clk_sec(clk_sec), .reset_n(reset_n), .clk_min(clk_min));
always @ (posedge clk)begin
time_out = |{sec[0], sec[1], min[0], min[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)
);
T_flip_flop_posedge tff_down (.T(1), .clk(btn_down), .reset_n(time_out), .preset_n(1), .Q(down)); //preset은 0에서 작동하니 1을 준다.
//00분 00초가 되면 time_out = 리셋 되면서 up count로 바뀐다.
not (btn_down_n, btn_down); //btn_down_n = up,
not (btn_incsec_n, btn_incsec);
not (btn_incmin_n, btn_incmin);
and (any_btn, btn_down_n, btn_incsec_n, btn_incmin_n, reset_n);
T_flip_flop tff_led (.T(1), .clk(time_out), .reset_n(any_btn), .preset_n(1), .Q(led_on));
assign led[0] = led_on;
wire clk_up_down_sec;
mux_2_1 mux_sec(
.D({clk_sec, btn_incsec}),
.S(down),
.F(clk_up_down_sec)
);
// and (clk_down_sec, down, clk_sec); //down = 1일 때 1초 clk로 나옴
// and (clk_up_sec, ~down, btn_inc); //down이 0일 때, 즉, up일 때 btn_clk에 의해 clk_up_sec가 1출력
// or (clk_up_down_sec, clk_down_sec, clk_down_sec);
counter_up_down counter_sec (
.clk(clk_up_down_sec),
.down(down), //down이 1일 때는 down, down이 0일 때는 up카운터
.reset_n(reset_n),
.count_1(sec[0]),
.count_10(sec[1]),
.dec_clk(dec_clk)
);
wire dec_clk_negedge;
edge_detect ed_dec_clk(
.clk(clk),
.cp_in(dec_clk),
.reset_n(reset_n),
.n_edge(dec_clk_negedge));
mux_2_1 mux_min(
.D({dec_clk_negedge, btn_incmin}), //59될 때 나오는 dec_clk를 사용해야함
.S(down),
.F(clk_up_down_min)
);
counter_up_down counter_min (
.clk(clk_up_down_min),
.down(down), //down이 1일 때는 down, down이 0일 때는 up카운터
.reset_n(reset_n),
.count_1(min[0]),
.count_10(min[1])
);
endmodule
module counter_up_down(
input clk,
input down, //down이 1일 때는 down, down이 0일 때는 up카운터
input reset_n,
output reg [3:0] count_1 = 0,
output reg [3:0] count_10 = 0,
output reg dec_clk = 0
);
always @(negedge clk or negedge reset_n)begin
if(!reset_n)begin
count_1 = 0;
count_10 = 0;
dec_clk = 1;
end
else begin
if(down)begin
if(count_1 <= 0)begin
count_1 = 9;
if(count_10 <= 0)begin
count_10 = 5;
dec_clk = 0;
end
else begin
count_10 = count_10 - 1;
end
end
else begin
count_1 = count_1 - 1;
if(~|{count_1, count_10}) dec_clk = 1; //모든 값이 0이 되었을 때 dec_clk을 1만든다. 1에서 0되고 59될 때 edge발생
end // |(0001, 0101) 이면 0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 각 비트를 전부 or하는 것
//초 단위가 0 0으로 시작하면 dec_clk이 0인 상태로 하는거라 바로 down이 안됨
end
else begin
dec_clk = 0;
if(count_1 >= 9)begin
count_1 = 0;
if(count_10 >= 5)begin count_10 = 0;
dec_clk = 1;
end
else
count_10 = count_10 + 1;
end
else count_1 = count_1 + 1;
end
end
end
endmodule
stop watch 코드
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;
wire [3:0] w_sec [1:0];
wire [3:0] w_min [1:0];
wire [3:0] w_lap;
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];
assign w_sec[0] = sec[0];
assign w_sec[1] = sec[1];
assign w_min[0] = w_min[0];
assign w_min[1] = w_min[1];
assign w_lap[0] = w_lap[0];
assign w_lap[1] = w_lap[1];
assign w_lap[2] = w_lap[2];
assign w_lap[3] = w_lap[3];
always @(lap_e, w_sec[0], w_sec[1], w_min[0], w_min[1], w_lap[0], w_lap[1], w_lap[2], w_lap[3]);
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[0];
out_value[3] = min[1];
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출력이 임피던스,
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(clk), .clk_usec(clk_usec), .reset_n(reset_n), .clk_msec(clk_msec));
clock_sec sCLK (.clk(clk), .clk_msec(clk_start), .reset_n(reset_n), .clk_sec(clk_sec));
clock_min MCLK (.clk(clk), .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
동작이 정상적으로 되는걸 확인했다.
'Verilog > Verilog 실습' 카테고리의 다른 글
Verilog [CPU만들기] CPU 제작 (0) | 2023.05.18 |
---|---|
Verilog [CPU만들기] Control Block (0) | 2023.05.17 |
Verilog HC-SR04 초음파센서 제어 (0) | 2023.05.16 |
Verilog [CPU만들기] ACC (2) | 2023.05.16 |
Verilog [CPU 만들기] PC, ALU (0) | 2023.05.09 |