Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- Edge Detector
- Linked List
- atmega 128a
- Algorithm
- ring counter
- DHT11
- java
- Recursion
- BASYS3
- pwm
- ATMEGA128A
- hc-sr04
- LED
- test bench
- soc 설계
- stop watch
- verilog
- behavioral modeling
- half adder
- D Flip Flop
- KEYPAD
- structural modeling
- gpio
- Pspice
- vivado
- prescaling
- i2c 통신
- FND
- uart 통신
- dataflow modeling
Archives
- Today
- Total
거북이처럼 천천히
Verilog RTL 설계(7월 18일 - 1, Advanced Clock Mode - 4) 본문
RTL Design/Verilog RTL 설계
Verilog RTL 설계(7월 18일 - 1, Advanced Clock Mode - 4)
유로 청년 2024. 7. 21. 14:581. 초 값이 30초 이상일 때, btn_set 버튼을 누를 때마다 분 값이 1씩 증가한다. (또 다른 해결책)
- 해당 문제에 대해서 이미 이전 게시글을 통해서 다루어 보았다.
Verilog RTL 설계(7월 17일 - 5, Advanced Clock Mode - 2) (tistory.com)
해당 문제의 원인은 아래 코드가 원인이 되었다.
assign inc_min = (set_watch)? btn_min : clk_min;
- clk_min은 주기가 1min이며, Duty Cycle이 50%인 Pulse wave이다.
- 따라서 0 ~ 29초 동안에는 Low 전압 레벨을 갖지만, 30 ~ 59초동안에는 High 전압 레벨을 갖는다.
- 만약 30초 이상인 상태에서는 clk_min 값은 1 값을 가질 것이며, minute button은 아직 누르지 않은 상태라면 0 값을 가질 것이다.
- 이 상태에서 Set mode button을 눌러 set_watch 모드를 변경한다면 inc_min 변수의 값은 1 → 0으로 변화가 발생 할 것이며, 이는 Negative edge를 발생시킬 것이다.
bcd_60_counter counter_min(.clk(clk), .reset_p(reset_p), .clk_source(inc_min), .bcd1(min_1), .bcd10(min_10));
- 이로 인해 BCD 60진 카운터에서는 inc_min 펄스파를 기준으로 카운팅하기 때문에 inc_min 펄스파가 활성화되어 분 값이 1씩 증가할 것이다.
1.2. 문제의 해결책
- 문제는 clk_min 값이 1인 경우에 발생했기 때문에 clk_min이 활성화된 시간을 최소화함으로서 해결하고자 했다.
- 따라서 Duty cycle이 50%인 Pulse wave가 아닌 10ns 동안에만 활성화된 One Cycle Pulse를 활용하여 문제를 해결 하였다.
clk_div_n clk_div_min(.clk(clk), .reset_p(reset_p), .clk_source(inc_sec), .prescale(60), .clk_div_n_nedge(clk_min));
1.3. 또 다른 해결책
- 하지만, 10ns동안 활성화된 One Cycle Pulse를 이용한다 하더라도 정말 낮은 확률로 10ns동안 활성화된 시점에 Watch mode에서 Set mode로 변경한다면 동일한 문제가 이어질 것이다.
- 따라서 이를 해결하기 위해 다른 방법을 사용하고자 한다.
- 또 다른 해결 방법은 Watch 모드의 BCD 60진 Counter와 Set 모드의 BCD 60진 Counter로 분리하여 서로 각각 Counting하는 것이다.
1.3.1. BCD 60진 Counter를 분리한다는 의미는 무엇인가?
- BCD 60진 Counter를 분리한 논리 회로는 다음과 같다.
- 이전에서는 Watch 모드와 Set 모드는 하나의 BCD 60진 Counter를 사용했다.
- Watch 모드와 Set 모드의 BCD 60진 Counter를 분리하여 구현함으로서 Watch 모드에서는 Watch모드의 60진 BCD 카운터로 분과 초를 counting하며, Set 모드에서는 Set모드의 60진 BCD 카운터로 분과 초를 counting한다.
- 이렇게 계산된 분과 초의 데이터를 결합 연산자를 통해 16bit 데이터로 결합한 뒤, DEMUX를 통해 현재 모드(set_watch)에 따라 해당 모드의 맞는 분과 초를 출력한다.
- 이렇게 Set mode와 Watch 모드의 BCD 60진 카운터를 분리함으로서 모드 변함으로 인한 분과 초의 Counting 방법 변환 및 분과 초의 데이터과 독립적으로 동작하기 때문에 초 값이 30초 이상일 때, btn_set 버튼을 누를 때마다 분 값이 1씩 증가하는 문제점을 해결할 수 있다.
1.3.2 이를 어떻게 코드로 구현하는가?
- 이에 대해서는 코드와 함께 설명하도록 하겠다.
// Loadalbe BCD 60 Counter.
module loadable_bcd_60_counter (
input clk, reset_p,
input clk_time,
input load_enable,
input [3:0] load_bcd1, load_bcd10,
output reg [3:0] bcd1, bcd10 );
// Get One Cycle Pulse of clk_time
wire clk_time_nedge;
edge_detector_n edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_time), .n_edge(clk_time_nedge));
// Counting
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
bcd1 = 0;
bcd10 = 0;
end
else if(load_enable) begin
bcd1 = load_bcd1;
bcd10 = load_bcd10;
end
else if(clk_time_nedge) begin
if(bcd1 >= 9) begin
bcd1 = 0;
if(bcd10 >= 5) bcd10 = 0;
else bcd10 = bcd10 + 1;
end
else bcd1 = bcd1 + 1;
end
end
endmodule
- 기본 형태는 이전에 구현했던 bcd_60_counter 모듈과 유사하다.
- 다만, Watch 모드와 Set 모드의 Counter가 분리되었기 때문에 서로의 Counter가 어디까지 Counting했는지, 서로의 Counter는 현재 몇 분, 몇 초에 있는지 모르는 상태이다.
- 따라서 모드가 변했을 때, 해당 모드의 Counter는 이전 모드의 Counter로 부터 몇분 몇초까지 Counting했는지 여부를 알 필요가 있다. 그래야 이전 모드의 Counter가 Counting했던 분과 초를 시작으로 계속해서 Counting을 이어 나갈 수 있다.
- 이를 위해서 load_enable 변수와 load_bcd1, load_bcd10 변수를 입력 값으로 받는다.
- load_enable : 모드가 변화하여 이전 모드의 Counter로 부터 이전까지 Counting한 분과 초를 받을지 여부를 결정하는 변수
- load_bcd1 : 이전 모드의 Counter가 Counting했던 분 / 초의 일의 자리
- load_bcd10 : 이전 모드의 Counter가 Counting했던 분 / 초의 십의 자리 - 위 변수 값을 통해 모드가 변화하였을 때, 현재 모드의 Counter는 이전 모드의 Counter로부터 분과 초를 받을 지 여부를 결정하고, 이전 모드의 Counter로부터 데이터를 이어 받아 계속해서 Counting하게 된다.
1.3.3. 그럼 언제 각각의 Counter들은 이전 모드의 Counter로 부터 데이터를 이어 받는가?
- 각각의 Counter들은 이전 모드의 Counter로 부터 데이터를 이어 받는 시점은 set_watch 변수 값이 변화하였을 시점에 이어 받는다.
- set_watch 값이 0 → 1로 변화하게 되면
- watch 모드에서 set 모드로 변화
- set_watch 변수에서 Positive edge 발생
- 즉, set_watch 변수에서 Positive edge가 발생하면 set 모드의 Counter는 watch 모드의 Counter로 부터 데이터를 이어받는다. - set_watch 값이 1 → 0로 변화하게 되면
- set 모드에서 watch 모드로 변화
- set_watch 변수에서 Negative edge 발생
- 즉, set_watch 변수에서 Negative edge 가 발생하면 watch 모드의 Counter는 set 모드의 Counter로 부터 데이터를 이어받는다. - 즉, set_watch 변수에서 Positive edge 및 Negative edge가 발생했는지 여부에 따라 각각의 Counter들은 다른 Counter로 부터 데이터를 이전 받아 계속해서 Counting을 이어간다.
// Edge detector of set_watch.
wire watch_load_en, set_load_en;
edge_detector_n get_watch_load_en (.clk(clk), .reset_p(reset_p), .cp(set_watch), .n_edge(watch_load_en));
edge_detector_n get_set_load_en (.clk(clk), .reset_p(reset_p), .cp(set_watch), .p_edge(set_load_en));
1.3.4. 서로 다른 모드들의 BCD 60진 counter 정의
- Watch 모드와 Set 모드의 BCD 60진 counter가 독립적이다.
- 따라서 Watch 모드와 Set 모드의 BCD 60진 counter를 각각 분과 초에 대해서 정의할 필요가 있다.
- 또한 각각의 Counter들이 이전 모드의 Counter로 부터 데이터를 이전 받아 Counting하는 것은 set_watch 변수에서 Positive edge 및 Negative edge가 발생했는지 여부에 따라 동작하게 된다.
// BCD 60 counter of Watch mode
loadable_bcd_60_counter sec_watch(.clk(clk), .reset_p(reset_p), .clk_time(inc_sec), .load_enable(watch_load_en), .load_bcd1(set_sec1), .load_bcd10(set_sec10), .bcd1(watch_sec1), .bcd10(watch_sec10));
loadable_bcd_60_counter min_watch(.clk(clk), .reset_p(reset_p), .clk_time(inc_min), .load_enable(watch_load_en), .load_bcd1(set_min1), .load_bcd10(set_min10), .bcd1(watch_min1), .bcd10(watch_min10));
// BCD 60 counter of Set mode
loadable_bcd_60_counter sec_set(.clk(clk), .reset_p(reset_p), .clk_time(inc_sec), .load_enable(set_load_en), .load_bcd1(watch_sec1), .load_bcd10(watch_sec10), .bcd1(set_sec1), .bcd10(set_sec10));
loadable_bcd_60_counter min_set(.clk(clk), .reset_p(reset_p), .clk_time(inc_min), .load_enable(set_load_en), .load_bcd1(watch_min1), .load_bcd10(watch_min10), .bcd1(set_min1), .bcd10(set_min10));
1.3.5. 모드에 따라 두 개의 데이터 중 현재 모드에 따라 결정하여 출력한다.
- Watch 모드의 counter로 부터 얻은 데이터와 Set 모드의 counter로 부터 얻은 데이터를 결합 연산자를 이용하여 각각 결합한다.
- 그리고, 현재 모드(set_watch) 값에 따라 결정하여 해당 값들을 출력하게 된다.
// Combine data of second, minute.
wire [15:0] watch_value, set_value, hex_value;
assign watch_value = {watch_min10, watch_min1, watch_sec10, watch_sec1};
assign set_value = {set_min10, set_min1, set_sec10, set_sec1};
// Choose data of minute, second by using set_watch.
assign hex_value = (set_watch)? set_value : watch_value;
// Print the FND
fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value(hex_value), .com(com), .seg_7(seg_7));
1.3.6. 구현 영상
1.3.7. 전체 소스 코드
// Root of module
module Set_Clock_Mode(
input clk, reset_p,
input [3:0] btn,
output [3:0] com,
output [7:0] seg_7 );
// Get pulse wave of sec, min
wire clk_usec, clk_sec, clk_min;
clk_div_100 clk_div_usec (.clk(clk), .reset_p(reset_p), .clk_div_100(clk_usec));
clk_div_n clk_div_sec(.clk(clk), .reset_p(reset_p), .clk_source(clk_usec), .prescale(1000000), .clk_div_n_nedge(clk_sec));
clk_div_n clk_div_min(.clk(clk), .reset_p(reset_p), .clk_source(inc_sec), .prescale(60), .clk_div_n_nedge(clk_min));
// Get One Cycle Pulse of button.
wire btn_set, btn_sec, btn_min;
button_cntr edge_btn_set (.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_nedge(btn_set));
button_cntr edge_btn_sec (.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_nedge(btn_sec));
button_cntr edge_btn_min (.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_nedge(btn_min));
// select mode.
wire set_watch;
t_flip_flop t_ff (.clk(clk), .reset_p(reset_p), .t(btn_set), .q(set_watch));
// Select standard pulse of bcd 60 counter.
// when current state is watch mode, standard pulse is clk_sec, clk_min
// when current state is set mode, standard pulse is btn_sec, btn_min
wire inc_sec, inc_min;
assign inc_sec = (set_watch)? btn_sec : clk_sec;
assign inc_min = (set_watch)? btn_min : clk_min;
// Variable of Watch mode, Set mode
wire [3:0] watch_sec10, watch_sec1, watch_min10, watch_min1;
wire [3:0] set_sec10, set_sec1, set_min10, set_min1;
// Edge detector of set_watch.
wire watch_load_en, set_load_en;
edge_detector_n get_watch_load_en (.clk(clk), .reset_p(reset_p), .cp(set_watch), .n_edge(watch_load_en));
edge_detector_n get_set_load_en (.clk(clk), .reset_p(reset_p), .cp(set_watch), .p_edge(set_load_en));
// BCD 60 counter of Watch mode
loadable_bcd_60_counter sec_watch(.clk(clk), .reset_p(reset_p), .clk_time(inc_sec), .load_enable(watch_load_en), .load_bcd1(set_sec1), .load_bcd10(set_sec10), .bcd1(watch_sec1), .bcd10(watch_sec10));
loadable_bcd_60_counter min_watch(.clk(clk), .reset_p(reset_p), .clk_time(inc_min), .load_enable(watch_load_en), .load_bcd1(set_min1), .load_bcd10(set_min10), .bcd1(watch_min1), .bcd10(watch_min10));
// BCD 60 counter of Set mode
loadable_bcd_60_counter sec_set(.clk(clk), .reset_p(reset_p), .clk_time(inc_sec), .load_enable(set_load_en), .load_bcd1(watch_sec1), .load_bcd10(watch_sec10), .bcd1(set_sec1), .bcd10(set_sec10));
loadable_bcd_60_counter min_set(.clk(clk), .reset_p(reset_p), .clk_time(inc_min), .load_enable(set_load_en), .load_bcd1(watch_min1), .load_bcd10(watch_min10), .bcd1(set_min1), .bcd10(set_min10));
// Combine data of second, minute.
wire [15:0] watch_value, set_value, hex_value;
assign watch_value = {watch_min10, watch_min1, watch_sec10, watch_sec1};
assign set_value = {set_min10, set_min1, set_sec10, set_sec1};
// Choose data of minute, second by using set_watch.
assign hex_value = (set_watch)? set_value : watch_value;
// Print the FND
fnd_cntr fnd(.clk(clk), .reset_p(reset_p), .value(hex_value), .com(com), .seg_7(seg_7));
endmodule
// Control Button.
module button_cntr (
input clk, reset_p,
input btn,
output btn_pedge, btn_nedge);
// Get 1ms Pulse wave, One Cycle Pulse
reg [16:0] counter;
always @(posedge clk or posedge reset_p) begin
if(reset_p) counter = 0 ;
else begin
if(counter >= 99999) counter = 0;
else counter = counter + 1;
end
end
wire clk_1ms, clk_1ms_nedge;
assign clk_1ms = (counter < 50000)? 0 : 1;
edge_detector_n edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_1ms), .n_edge(clk_1ms_nedge));
// Get positive edge of button, Negative edge of button.
reg debounced_btn;
always @(posedge clk or posedge reset_p) begin
if(reset_p) debounced_btn = 0;
else if(clk_1ms_nedge) debounced_btn = btn;
end
edge_detector_n edge_detector_1 (.clk(clk), .reset_p(reset_p), .cp(debounced_btn), .p_edge(btn_pedge), .n_edge(btn_nedge));
endmodule
// T Flo Flop
module t_flip_flop (
input clk, reset_p,
input t,
output reg q );
always @(posedge clk or posedge reset_p) begin
if(reset_p) q = 0;
else if(t) q = ~q;
else q = q;
end
endmodule
// Loadalbe BCD 60 Counter.
module loadable_bcd_60_counter (
input clk, reset_p,
input clk_time,
input load_enable,
input [3:0] load_bcd1, load_bcd10,
output reg [3:0] bcd1, bcd10 );
// Get One Cycle Pulse of clk_time
wire clk_time_nedge;
edge_detector_n edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_time), .n_edge(clk_time_nedge));
// Counting
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
bcd1 = 0;
bcd10 = 0;
end
else if(load_enable) begin
bcd1 = load_bcd1;
bcd10 = load_bcd10;
end
else if(clk_time_nedge) begin
if(bcd1 >= 9) begin
bcd1 = 0;
if(bcd10 >= 5) bcd10 = 0;
else bcd10 = bcd10 + 1;
end
else bcd1 = bcd1 + 1;
end
end
endmodule
// BCD 60 Counter
module bcd_60_counter (
input clk, reset_p,
input clk_source,
output reg [3:0] bcd1, bcd10 );
wire clk_sourc_nedge;
edge_detector_n edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_source), .n_edge(clk_sourc_nedge));
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
bcd1 = 0;
bcd10 = 0;
end else if(clk_sourc_nedge) begin
if(bcd1 >= 9) begin
bcd1 = 0;
if(bcd10 >= 5) bcd10 = 0;
else bcd10 = bcd10 + 1;
end else bcd1 = bcd1 + 1;
end
end
endmodule
// Prescaler N
module clk_div_n (
input clk, reset_p,
input clk_source,
input [31:0] prescale,
output clk_div_n, clk_div_n_nedge );
// Get one cycle pulse of clk_source
wire clk_source_nedge;
edge_detector_n edge_detector_0 (.clk(clk), .reset_p(reset_p), .cp(clk_source), .n_edge(clk_source_nedge));
// Counting
integer counter;
always @(posedge clk or posedge reset_p) begin
if(reset_p) counter = 0;
else if(clk_source_nedge) begin
if(counter >= prescale-1) counter = 0;
else counter = counter + 1;
end
end
// Get clk_div_n wave, One cycle pulse of clk_div_n.
assign clk_div_n = (counter < prescale / 2)? 0 : 1;
edge_detector_n edge_detector_1 (.clk(clk), .reset_p(reset_p), .cp(clk_div_n), .n_edge(clk_div_n_nedge));
endmodule
// prescale 100.
module clk_div_100 (
input clk, reset_p,
output clk_div_100,
output clk_div_100_nedge );
reg [6:0] counter;
always @(posedge clk or posedge reset_p) begin
if(reset_p) counter = 0;
else begin
if(counter >= 99) counter = 0;
else counter = counter + 1;
end
end
assign clk_div_100 = (counter < 50)? 0 : 1;
edge_detector_n edge_detector (.clk(clk), .reset_p(reset_p), .cp(clk_div_100), .n_edge(clk_div_100_nedge));
endmodule
// Control FND
module fnd_cntr (
input clk, reset_p,
input [15:0] value,
output reg [3:0] com,
output [7:0] seg_7);
// Shifting the com value every 1ms.
// prescale 100000
reg [16:0] counter;
always @(posedge clk or posedge reset_p) begin
if(reset_p) counter = 0;
else begin
if(counter >= 99999) counter = 0;
else counter = counter + 1;
end
end
// period 1ms Pulse wave
wire clk_div_100000, clk_div_100000_nedge;
assign clk_div_100000 = (counter < 50000) ? 0 : 1;
// period 1ms One Cycle Pulse
edge_detector_n edge_detector_0(.clk(clk), .reset_p(reset_p), .cp(clk_div_100000), .n_edge(clk_div_100000_nedge));
// Shifting com value
always @(posedge clk or posedge reset_p) begin
if(reset_p) com = 4'b1110;
else if(clk_div_100000_nedge) begin
if(com == 4'b0111) com = 4'b1110;
else com = {com[2:0], 1'b1};
end
end
reg[3:0] hex_value;
always @(com) begin
case(com)
4'b1110 : hex_value = value[3:0];
4'b1101 : hex_value = value[7:4];
4'b1011 : hex_value = value[11:8];
4'b0111 : hex_value = value[15:12];
default : hex_value = hex_value;
endcase
end
// Get fnd data
decoder_seg_7 fnd (.hex_value(hex_value), .seg_7(seg_7));
endmodule
// Decoder of 7-seg
module decoder_seg_7 (
input [3:0] hex_value,
output reg [7:0] seg_7 );
always @(hex_value) begin
case(hex_value)
0 : seg_7 = 8'b0000_0011; // 0
1 : seg_7 = 8'b1001_1111; // 1
2 : seg_7 = 8'b0010_0101; // 2
3 : seg_7 = 8'b0000_1101; // 3
4 : seg_7 = 8'b1001_1001; // 4
5 : seg_7 = 8'b0100_1001; // 5
6 : seg_7 = 8'b0100_0001; // 6
7 : seg_7 = 8'b0001_1111; // 7
8 : seg_7 = 8'b0000_0001; // 8
9 : seg_7 = 8'b0000_1001; // 9
10 : seg_7 = 8'b0000_0101; // A
11 : seg_7 = 8'b1100_0001; // b
12 : seg_7 = 8'b0110_0011; // C
13 : seg_7 = 8'b1000_0101; // d
14 : seg_7 = 8'b0110_0001; // E
15 : seg_7 = 8'b0111_0001; // F
endcase
end
endmodule
// Edge detector
module edge_detector_n (
input clk, reset_p,
input cp,
output p_edge, n_edge );
reg flip_flop_current, flip_flop_old;
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
flip_flop_current <= 0;
flip_flop_old <= 0;
end begin
flip_flop_current <= cp;
flip_flop_old <= flip_flop_current;
end
end
assign p_edge = ({flip_flop_current, flip_flop_old} == 2'b10) ? 1 : 0;
assign n_edge = ({flip_flop_current, flip_flop_old} == 2'b01) ? 1 : 0;
endmodule
'RTL Design > Verilog RTL 설계' 카테고리의 다른 글
Verilog RTL 설계(7월 18일 - 3, Stop Watch - 2) (0) | 2024.07.21 |
---|---|
Verilog RTL 설계(7월 18일 - 2, Stop Watch - 1) (0) | 2024.07.21 |
Verilog RTL 설계(7월 17일 - 7, Advanced Clock Mode) (0) | 2024.07.21 |
Verilog RTL 설계(7월 17일 - 6, Advanced Clock Mode - 3) (0) | 2024.07.21 |
Verilog RTL 설계(7월 17일 - 5, Advanced Clock Mode - 2) (1) | 2024.07.20 |