□ 본 게시글은 수업 내용을 바탕으로 주관적으로 작성한 것이기 때문에, 틀리거나 오류가 있을 수 있습니다!!!
FND Control
FND 출력을 링카운터를 활용해 시프트 동작을 시켜 1110 -> 1101 -> 1011 -> 0111과 같은 동작을 반복하려고 한다.
따라서 다음과 같은 코드를 작성하였다.
코드를 살펴보면, reset이 발생하였을 때 1110으로 초기화된 후, clk_div_nedge가 발생할 때마다 1을 1비트씩 옆으로 이동하도록 하였다. 옆으로 이동하는 동작은 "else com = {com [2:0], 1'b1};" 문장에서 실행이 된다.
module ring_counter_fnd2(
input clk, reset_p,
output reg [3:0] com);
reg [20:0] clk_div = 0;
always @(posedge clk)clk_div = clk_div + 1;
wire clk_div_nedge;
edge_detector_p ed(.clk(clk), .reset_p(reset_p), .cp(clk_div[16]), .n_edge(clk_div_nedge));
always @(posedge clk or posedge reset_p)begin
if(reset_p)com = 4'b1110;
else if(clk_div_nedge)begin
if(com == 4'b0111) com = 4'b1110;
else com = {com[2:0], 1'b1};
end
end
endmodule
0이 왼쪽으로 시프트 되는 것을 확인할 수 있다.
위 코드를 Basys3로 직접 확인하기 위하여 아래와 같은 코드를 작성하였다.
위에서 만든 ring_counter_fnd2를 불러온 후, case문을 활용하여 작성하였다.
아래에 언급된 decoder_7 seg는 7-Segment 숫자를 표현하기 위해서 언급되었다.
case문을 살펴보면, switch [3:0] 은 첫 번째 비트의 숫자를 표현하는 데에, switch [7:4]는 두 번째, 그 뒤로는 세 번째, 네 번째 숫자를 표현하기 위해서 작성한 것을 확인할 수 있다.
작성된 코드는 아래의 코드와 같다.
module fnd_test_top(
input clk, reset_p,
input [15:0] switch,
output [3:0] com,
output [7:0] seg_7);
ring_counter_fnd2(clk, reset_p,com);
reg [3:0] hex_value;
always @(posedge clk)begin
case(com)
4'b1110: hex_value = switch[3:0];
4'b1101: hex_value = switch[7:4];
4'b1011: hex_value = switch[11:8];
4'b0111: hex_value = switch[15:12];
endcase
end
decoder_7seg(.hex_value(hex_value), .seg_7(seg_7));
endmodule
코드를 실행시키면 다음과 같이 출력되는 것을 확인할 수 있다.
디지털시계
이번에는 분과 초를 나타내는 디지털시계를 구현해보려 한다.
분과 초를 나타내는 시계를 구현하기 위해서는 기본적으로 분과 초를 구성하는 모듈을 생성해야 한다.
1ns를 1us로, 1us를 1ms로, 최종적으로는 1ms를 1 sec로 만들기 위해 분주를 사용한다.
시간제어
우선 1ns를 1us로 만들어보기 위해 아래와 같은 코드를 작성하였다.
코드를 살펴보면, 100 분주 동작을 실행시키기 위한 코드로 system clock이 99 이상일 때 0으로 초기화를 하며, 다시 clock을 증가시키는 동작을 계속 반복한다. 만일 clock이 50 이상일 때에는 1을 주게 되어 결국 주기는 100으로 된다. 즉, 10ns에 100을 곱하게 되면 1us가 되는 원리이다.
"cnt_sysclk"을 7bit로 준 이유는, 10진수 100을 2진수로 변환하면 최고자리의 1의 위치가 7번째이기 때문에 7bit로 준 것이다.
module clock_div_100(
input clk, reset_p,
output clk_div_100,
output clk_div_100_nedge);
reg [6:0] cnt_sysclk;
always @(negedge clk or posedge reset_p)begin
if(reset_p)cnt_sysclk = 0;
else begin
if(cnt_sysclk >= 99) cnt_sysclk = 0;
else cnt_sysclk = cnt_sysclk + 1;
end
end
assign clk_div_100 = (cnt_sysclk < 50) ? 0 : 1;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(clk_div_100),
.n_edge(clk_div_100_nedge));
endmodule
다음은 1us를 1ms로 만들기 위해 1000 분주를 하는 코드를 구현해 보았다.
"cnt_clksource"에 10bit를 주는 이유는, 1000을 2진수로 변환하였을 때 "0011 1110 1000"이 나오기 때문에 1이 끝나는 부분이 10번째이기 때문에 10bit를 준 것이다.
1 sec 또한 밑의 코드를 사용하여 1ms로 1 sec을 구현하였다.
module clock_div_1000(
input clk, reset_p,
input clk_source,
output clk_div_1000,
output clk_div_1000_nedge);
reg [9:0] cnt_clksource;
wire clk_source_nedge;
edge_detector_n ed_source(
.clk(clk), .reset_p(reset_p), .cp(clk_source),
.n_edge(clk_source_nedge));
always @(negedge clk or posedge reset_p)begin
if(reset_p)cnt_clksource = 0;
else if(clk_source_nedge)begin
if(cnt_clksource >= 999) cnt_clksource = 0;
else cnt_clksource = cnt_clksource + 1;
end
end
assign clk_div_1000 = (cnt_clksource < 500) ? 0 : 1;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(clk_div_1000),
.n_edge(clk_div_1000_nedge));
endmodule
이제 1 sec를 1 min로 만들어보자. 1 min을 만들기 위해서는 60 sec이 필요하므로 아래와 같은 코드로 구현하였다.
위의 과정과 동일하게 10진수 60을 2진수로 변환하면 "0011 1100"이기 때문에 6bit로 선언하였다.
module clock_div_60(
input clk, reset_p,
input clk_source,
output clk_div_60,
output clk_div_60_nedge);
reg [5:0] cnt_clksource;
wire clk_source_nedge;
edge_detector_n ed_source(
.clk(clk), .reset_p(reset_p), .cp(clk_source),
.n_edge(clk_source_nedge));
always @(negedge clk or posedge reset_p)begin
if(reset_p)cnt_clksource = 0;
else if(clk_source_nedge)begin
if(cnt_clksource >= 59) cnt_clksource = 0;
else cnt_clksource = cnt_clksource + 1;
end
end
assign clk_div_60 = (cnt_clksource < 30) ? 0 : 1;
edge_detector_n ed(
.clk(clk), .reset_p(reset_p), .cp(clk_div_60),
.n_edge(clk_div_60_nedge));
endmodule
버튼제어
이제 시간 관련 작업은 끝났으므로, 시간을 제어할 버튼에 관하여 알아볼 것이다.
버튼은 총 3개를 사용할 것이며, 각각의 역할은 mode, sec, min을 제어하는 것이다.
mode를 선택할 시, 시간이 흐르는 동작이 멈추게 되며, min과 sec은 분과 초를 1씩 올릴 수 있도록 설정하였다.
버튼 제어를 구현할 코드는 아래와 같다.
우선, negative edge에 반응할 수 있도록 " edge detector" 모듈을 선언하여 각 핀들을 제어하였다.
module button_cntr(
input clk, reset_p,
input btn,
output btn_pedge, btn_nedge);
reg [20:0] clk_div = 0;
always @(posedge clk)clk_div = clk_div + 1;
wire clk_div_nedge;
edge_detector_p ed(.clk(clk), .reset_p(reset_p), .cp(clk_div[16]), .n_edge(clk_div_nedge));
reg debounced_btn;
always @(posedge clk or posedge reset_p)begin
if(reset_p)debounced_btn = 0;
else if(clk_div_nedge)debounced_btn = btn;
end
edge_detector_p ed_btn(.clk(clk), .reset_p(reset_p), .cp(debounced_btn), .n_edge(btn_nedge), .p_edge(btn_pedge));
endmodule
설정 제어
이번에 구현할 시계에서는 설정 모드와 일반적인 동작모드가 존재한다. 이러한 설정모드를 제어하기 위해서 T F/F 모듈을 선언하였다. T F/F 모듈의 코드는 아래와 같다.
T F/F의 용도는 Toggle이 주 목적이기 때문에 T가 입력될 때마다 플립플롭의 값이 토글 된다.
module T_flip_flop_p(
input clk, reset_p,
input t,
output reg q);
always @(posedge clk or posedge reset_p)begin
if(reset_p)q = 0;
else begin
if(t) q = ~q ;
else q = q;
end
end
endmodule
아래의 코드는 시계를 구현할 코드에서의 T F/F의 역할을 나타낸다.
플립플롭에 입력이 있을 때마다 "set_watch"는 활성화되며, "set_watch"의 값에 따라 코드에 나온 바와 같이 sec과 min이 제어된다. "inc(increment)_sec/min"은 "set_watch"이 1이 될 때마다 sec/min 에 clock을 인가하여 1씩 증가하게 한다.
T_flip_flop_p t_mode(.clk(clk), .reset_p(reset_p), .t(btn_mode), .q(set_watch));
assign inc_sec = set_watch ? btn_sec : clk_sec;
assign inc_min = set_watch ? btn_min : clk_min;
디지털시계 구현 코드
이제 위에서 정리된 코드들을 종합하여 코드를 구현하면 아래와 같이 구현이 되는 것을 확인할 수 있다.
module watch_top(
input clk, reset_p,
input [2:0] btn,
output [3:0] com,
output [7:0] seg_7);
// wire
wire btn_mode;
wire btn_sec;
wire btn_min;
wire set_watch;
wire inc_sec, inc_min; // inc = increment
wire clk_usec, clk_msec, clk_sec, clk_min;
wire [3:0] sec1, sec10, min1, min10;
wire [15:0] value;
// button Mode
button_cntr btn0(.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_mode));
button_cntr btn1(.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_sec));
button_cntr btn2(.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_min));
// button control
T_flip_flop_p t_mode(.clk(clk), .reset_p(reset_p), .t(btn_mode), .q(set_watch));
assign inc_sec = set_watch ? btn_sec : clk_sec;
assign inc_min = set_watch ? btn_min : clk_min;
// Time Setting
clock_div_100 usec_clk(.clk(clk), .reset_p(reset_p), .clk_div_100(clk_usec));
clock_div_1000 msec_clk(.clk(clk), .reset_p(reset_p), .clk_source(clk_usec), .clk_div_1000(clk_msec));
clock_div_1000 sec_clk(.clk(clk), .reset_p(reset_p), .clk_source(clk_msec), .clk_div_1000_nedge(clk_sec));
clock_div_60 min_clk(.clk(clk), .reset_p(reset_p), .clk_source(inc_sec), .clk_div_60_nedge(clk_min));
counter_bcd_60 counter_sec(.clk(clk), .reset_p(reset_p), .clk_time(inc_sec), .bcd1(sec1), .bcd10(sec10));
counter_bcd_60 counter_min(.clk(clk), .reset_p(reset_p), .clk_time(inc_min), .bcd1(min1), .bcd10(min10));
assign value = {min10, min1, sec10, sec1};
fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value(value), .com(com), .seg_7(seg_7));
endmodule
코드를 바탕으로 회로도를 그려보면 아래와 같은 회로도를 그릴 수 있다.
동작 영상
코드를 실행시키면 다음과 같이 동작되는 것을 확인할 수 있다.
'Language > Verilog' 카테고리의 다른 글
Vivado : 초음파 센서 (0) | 2024.07.25 |
---|---|
Vivado : Basys3 Stop watch (0) | 2024.07.18 |
Vivado : Basys3 7segment (0) | 2024.07.16 |
Vivado : Edge Detector, Shift Register(SISO, SIPO, PISO, PIPO) (2) | 2024.07.16 |
Vivado : 동기식 카운터 (0) | 2024.07.12 |