[Schematic]

[parameter]
이전에 4bit, 12bit counter를 만들었는데,
parameter를 활용하면 굳이 bit마다 모듈을 만들어줄 필요가 없다.



[Bin_to_Dec]
아무래도 알파벳으로 카운팅되면, 가독성이 떨어질 수 밖에 없다.
∴ 0123456789abcdef → 0123456789
이전에 만들었던, bin_to_dec 모듈을 counter에 인스턴스하면 된다.


[Toggle]
버튼 누를 때마다, up/down 토글되도록 만들어 보자.
→ TFF 활용.

※ 이렇게 하면 에러 발생
clk 입력에 버튼 입력을 주니까 에러가 생김
clk 입력에는 FPGA 보드의 정규 clk(125MHz)만 사용해야 한다.
다른 걸 넣는다는 것은 비동기로 쓴다는 의미
→ 동기화 문제가 발생한다.
ex) clk_div, clk_1ms 등 다 비정규 clk
→ clk_div 같은 정규 clk를 받아서 쓰는 것은 그나마 사용 가능하다.(pdt가 별로 안 생김)
버튼 입력같이 무관한 clk를 쓰는 경우에 문제 발생


[Edge detector]
Edge_detector는 비정규 clk로 들어온 입력(Async)을 받아
정규 클락을 기준으로 하는 Pulse를 만들어 그 것을 신호로 사용한다,
따라서 위와 같이 비정규 clk를 사용했을 때 발생하는 동기화 오류를 해결하기 위해서는
버튼 입력을 clk로 쓰는 대신, edge detector 회로를 추가해주면 된다.

✅ Pull-up, Pull-down 저항과의 연관성

① Pull-up 저항을 사용할 경우, 평소에는 High(1)의 입력이 들어오고 있다가
스위치가 동작하면 Low(0)가 입력된다.
해당 신호를 Edge-detector로 받을 경우, neg_edge 신호가 출력되므로
Pull-up 저항을 사용할 때는 Edg의 neg_edge 출력을 선택해서 사용해야 한다.
② Pull-down 저항을 사용할 경우, 평소에는 Low(0)가 입력되고 있다가
스위치가 동작하면 High(1)가 입력된다.
해당 신호를 Edge_detector로 받을 경우, pos_edge 신호가 출력되므로
Pull-down 저항을 사용할 때는 Edg의 pos_edge 출력을 선택하여 사용해야 한다.



※ blocking / non-blocking
현재 작업이 block(차단, 대기) 되느냐 아니냐에 따라 다른 작업을 수행할 수 있는지
다른 요청의 작업을 처리하기 위해 현재 작업을 block(차단, 대기) 하냐 안하냐의 유무
파일을 읽는 작업이 있을 때, 블로킹 방식으로 읽으면 파일을 다 읽을 때까지 대기하고, 논블로킹 방식으로 읽으면 파일을 다 읽지 않아도 다른 작업을 할 수 있다.

////Blocking//// always @ * begin cp_in = 1; cp_cur = cp_in; cp_old = cp_cur; /// line5에서 계산된 cp_cur이 적용됨. end //// cp_in = cp_cur = cp_old = 1 //// 이전 라인이 다음 라인에 각각 반영되면서 나감.
////Non-blocking//// always @* begin cp_in = 1; cp_cur <= cp_in; cp_old <= cp_cur; //// 초기 cp_cur 값이 0이라고 하면, //// line5 에서의 cp_cur이 반영되지 않았으니 //// cp_old = 0 end //// cp_in = 1, cp_cur = 1, cp_old = 0 //// 즉, 병렬적으로 각각 동작한다.
그냥 순차 논리 회로에서는 무조건 Non-blocking을 사용하는 것이 안전하다.


위와 같이 counter_fnd_top에 edge dector를 instance해주고 디텍터의 출력을 TFF에 넣어주면, 동기화 에러가 해결된다.
[debouncing]

보통 bouncing은 1ms 이내에 끝난다. → 그걸 기다려주면 됨.
1ms에 한 번씩 clk가 바뀌는 DFF에 연결해주면 바운싱 제거됨.
cf) 다른 방식의 동기화/디바운싱
module debounce_better_version(input pb_1,clk,output pb_out); wire slow_clk_en; wire Q1,Q2,Q2_bar,Q0; clock_enable u1(clk,slow_clk_en); my_dff_en d0(clk,slow_clk_en,pb_1,Q0); // -----> debouncing ////////////////////////////////////////////////// my_dff_en d1(clk,slow_clk_en,Q0,Q1); // my_dff_en d2(clk,slow_clk_en,Q1,Q2); // assign Q2_bar = ~Q2; // assign pb_out = Q1 & Q2_bar; // ---> edge detector ////////////////////////////////////////////////// endmodule //// edge detector는 어짜피 DFF 두 개로 만들어졌으니, //// 한 모듈로 만들 수 있다.
<Edge _detector 모듈 없는 코딩>
module decoder_7seg( //// 적은 bit의 데이터를 받아 많은 bit로 출력 input [3:0] hex_value, output reg [7:0] seg_7 ); always @ (hex_value) begin case(hex_value) 4'b0000 : seg_7 = 8'b0000_0011; 4'b0001 : seg_7 = 8'b1001_1111; 4'b0010 : seg_7 = 8'b0010_0101; 4'b0011 : seg_7 = 8'b0000_1101; 4'b0100 : seg_7 = 8'b1001_1001; 4'b0101 : seg_7 = 8'b0100_1001; 4'b0110 : seg_7 = 8'b0100_0001; 4'b0111 : seg_7 = 8'b0001_1111; 4'b1000 : seg_7 = 8'b0000_0001; 4'b1001 : seg_7 = 8'b0000_1001; 4'b1010 : seg_7 = 8'b0001_0001; 4'b1011 : seg_7 = 8'b1100_0001; 4'b1100 : seg_7 = 8'b0110_0011; 4'b1101 : seg_7 = 8'b1000_0101; 4'b1110 : seg_7 = 8'b0110_0001; 4'b1111 : seg_7 = 8'b0111_0001; endcase end endmodule module bin_to_dec( input [11:0] bin, output reg [15:0] bcd ); reg [3:0] i; always @(bin) begin bcd = 0; for (i=0 ; i< 12 ; i = i+1) begin bcd = {bcd[14:0], bin[11-i]}; if(i<11 && bcd[3:0] >4) bcd[3:0] = bcd[3:0] + 3; if(i<11 && bcd[7:4] >4) bcd[7:4] = bcd[7:4] + 3; if(i<11 && bcd[11:8] >4) bcd[11:8] = bcd[11:8] + 3; if(i<11 && bcd[15:12] >4) bcd[15:12] = bcd[15:12] + 3; /// 100보다 큰 것을 좌쉬프트하면 1010을 넘어가는데, /// BCD에서는 사용 불가하니까 건너뛰어야함. /// 쉬프트 전 3을 더해주는 것 = 쉬프트 후 6 더하는 것 end end endmodule module up_down_counter_Nbit #(parameter N=4)( input clk, rst, up_down, output reg [N-1:0] count ); always @(posedge clk, posedge rst) begin if(rst) count = 0; else begin if(up_down) count = count + 1; else count = count - 1; end end endmodule module ring_count_fnd( //// 0001 0010 0100 1000 순환하는 링카운터 input clk, output reg [3:0] com ); always @(posedge clk) begin if(com != 4'b1110 && com != 4'b1101 && com != 4'b1011 && com != 4'b0111) com = 4'b1110; else if (com == 4'b0111) com = 4'b1110; else com = {com[2:0], 1'b1}; end endmodule module FND_4digit_cntr( input clk, rst, input [15:0] count, output [3:0] com, output [7:0] seg_7 ); reg [16:0] clk_1ms; always @ (posedge clk) clk_1ms = clk_1ms + 1; /// ringcounter를 위한 분주기 ring_count_fnd ring(.clk(clk_1ms[16]), .com(com)); reg [3:0] hex_value; wire [7:0] seg_7_font; decoder_7seg dec(.hex_value(hex_value), .seg_7(seg_7_font)); assign seg_7 = ~seg_7_font; always @ (negedge clk) begin case(com) 4'b1110 : hex_value = count[15:12]; 4'b1101 : hex_value = count[11:8]; 4'b1011 : hex_value = count[7:4]; 4'b0111 : hex_value = count[3:0]; endcase end endmodule module counter_fnd_top( input clk, rst, btn1, output [7:0] seg_7, output [3:0] com ); reg [25:0] clk_div; always @ (posedge clk) clk_div = clk_div + 1; D_flip_flop_n debnc(.d(btn1), .clk(clk_div[14]), .rst(rst), .q(up_down)); /// ----------> debouncing //////////////////////////////////////////////////////////////////// wire cp_cur, cp_old, p_edge; D_flip_flop_n edge1(.d(up_down), .clk(clk), .rst(rst), .q(cp_cur)); D_flip_flop_n edge2(.d(cp_cur), .clk(clk), .rst(rst), .q(cp_old)); wire old_bar; assign old_bar = ~cp_old; assign p_edge = cp_cur & old_bar; // wire cur_bar; // assign cur_bar = ~cp_cur; // assign n_edge = cur_bar & cp_old; // n_edge 출력을 원할 때 사용 /////////////////////////////////////////////////////////////////// ///--------> edge detector wire up; t_flip_flop t_up(.clk(clk), .rst(rst), .t(p_edge), .q(up)); wire [11:0] count; up_down_counter_Nbit #(.N(12)) counter_fnd(.clk(clk_div[25]), .rst(rst), .up_down(~up), .count(count)); wire [15:0] dec_value; bin_to_dec bin2dec(.bin(count), .bcd(dec_value)); FND_4digit_cntr cntr(.clk(clk), .rst(rst), .count(dec_value), .com(com), .seg_7(seg_7)); endmodule module D_flip_flop_n( input d, clk, rst, output reg q ); always @(posedge clk) begin if (rst) q = 0; else q = d; end endmodule module t_flip_flop( input clk, rst, t, output reg q ); always @ (posedge clk, posedge rst) begin if(rst) q = 0; else if(t) q = ~q; else q = q; end endmodule
※ 새로 받은 Bread board로 이전하여 code 변경됨
module D_flip_flop_n( input d, input clk,rst, output reg q ); always @(negedge clk, posedge rst) begin /// edge trigering if(rst) q = 0; ///// FF는 리셋 기능이 꼭 있어야 한다. else q = d; end module t_flip_flop_p( input clk, rst, input t, /// t = cp_in으로 보면 됨. output reg q ); always@(posedge clk, posedge rst) begin if(rst) q=0; else if (t) q = ~q; else q = q; //// 사실 FF(순차논리회로)에서는 굳이 안써줘도 됨. //// 어짜피 값을 유지하기 때문에. //// 조합논리회로에서는 꼭 써줘야 한다. //// 안 해주면 LATCH가 만들어짐. end endmodule module bin_to_dec( //// 12bit binary를 받아서 16bit decimal로 변환 input [11:0] bin, output reg [15:0] bcd /// 올림수가 하나만 나와도 4자리 다 읽어야 해서 vector : 16 ); reg [3:0] i; always @(bin) begin //// FND 출력할 때 자주 써먹게 될 것임. bcd = 0; for (i=0 ; i<12 ; i=i+1) begin bcd = {bcd[14:0], bin[11-i]}; //// 쉬프트 연산을 결합연산자로 표현(회로가 더 간단해진다.) //// 좌로 1bit 쉬프트하고, 빈 자리에는 bin[11-i]를 넣어 준다. if ( i < 11 && bcd[3:0] > 4) bcd[3:0] = bcd[3:0] + 3; if ( i < 11 && bcd[7:4] > 4) bcd[7:4] = bcd[7:4] + 3; if ( i < 11 && bcd[11:8] > 4) bcd[11:8] = bcd[11:8] + 3; if ( i < 11 && bcd[15:12] > 4) bcd[15:12] = bcd[15:12] + 3; end end endmodule module ring_count_fnd( //// 라운드 로그인 방식 input clk, output [3:0] com ); reg [3:0] temp; //// 초기값을 정해줘야 하므로 temp 선언 always @ (posedge clk) begin if(temp != 4'b1110 && temp != 4'b1101 && temp != 4'b1011 && temp != 4'b0111) temp = 4'b1110; /// 초기값은 0001 /// 0줬을 때 켜지는 FND는 4'b1110 else if (temp == 4'b0111) temp = 4'b1110; //// 1000되면 0001로 돌아와라 else temp = {temp[2:0], 1'b1}; //// 결합연산자 이용하여 좌시프트 end //// 0001 0010 0100 1000 을 반복하는 링카운터 assign com = temp; //// temp를 다시 com이라는 출력에 연결시켜준다. endmodule module FND_4digit_cntr( //// ring counter top module 생성 ---- 16bit의 값을 받아서 1ms 간격으로 한 자리씩 켜준다. input clk, rst, input [15:0] value, //// 4자리니까 16bit output [3:0] com, output [7:0] seg_7 ); reg [16:0] clk_1ms; //// 8ns * 2^17 = 1,048,576ns = 1048㎲ = 1.048ms always @(posedge clk) clk_1ms = clk_1ms + 1 ; ring_count_fnd ring1(.clk(clk_1ms[16]), .com(com)); //// 1ms posedge마다 좌시프트 //// 1ms 마다 CT 순대로 LED가 점멸한다. wire [7:0] seg_7_font; reg [3:0] hex_value; decoder_7seg ring_seg_7(.hex_value(hex_value), .seg_7(seg_7_font)); assign seg_7 = ~seg_7_font; always @(negedge clk) begin //// 카운팅은 posedge에 했으니, 겹치지 않게 negedge case(com) 4'b1110 : hex_value = value[15:12]; //// CT1에 해당하는 hex_value는 value[15:12] 4'b1101 : hex_value = value[11:8]; 4'b1011 : hex_value = value[7:4]; 4'b0111 : hex_value = value[3:0]; endcase //// edge triggering은 default에서 현재 값을 유지해도 FF가 만들어지니 상관없다. end endmodule module up_down_counter_Nbit #(parameter N = 4)( //// Nbit 업다운 카운터 input clk, rst, up_down, output reg [N-1:0] count ); always @(posedge clk, posedge rst) begin if(rst) count = 0; else begin if(up_down) count = count + 1; else count = count - 1; end end endmodule module counter_fnd_top( //// count와 fnd만 묶어서 top module 생성 input clk, rst, btn1, output [7:0] seg_7, // top module을 통한 출력 output [3:0] com ); wire [11:0] count; /// 카운터의 출력은 wire로 디코더와 연결 // assign com = 4'b1111; //// 나타낼 부분은 1 reg [25:0] clk_div; always @(posedge clk) clk_div = clk_div + 1; ///// 분주기 만들어줌(시뮬레이션은 분주기가 필요없지만, ///// 직접 출력할 때는 분주기가 필요해) ///// 약 0.5초마다 count가 증가한다. ///// (보드의 주기 : 8ns) → 8ns x 2^26 = 0.56 sec D_flip_flop_n debnc1(.d(btn1), .clk(clk_div[14]), .rst(rst), .q(up_down)); //// 위의 clk divider의 16번쨰 bit를 써야 된다. wire up_down_p; edge_detector_n(.clk(clk), .cp_in(up_down), .rst(rst), .p_edge(up_down_p)); /// 안 쓰는 출력은 빼버리면 된다. wire up; t_flip_flop_p t_up(.clk(clk), .t(up_down_p), .rst(rst), .q(up)); //// 버튼 누를때마다 up/down 토글 up_down_counter_Nbit #(.N(12)) counter_fnd (.clk(clk_div[25]), .rst(rst), .up_down(~up), .count(count)); //// clk는 125MHz로 너무 빠르다. ---> 분주기 만들어줘야 됨. wire [15:0] dec_value; bin_to_dec bin2dec(.bin(count), .bcd(dec_value)); //// 12bit binary를 받아서 16bit decimal로 변환 FND_4digit_cntr f_dgt_cntr(.clk(clk), .rst(rst), .value({dec_value}), .com(com), .seg_7(seg_7)); /// 컨트롤러가 분주기 가지고 있으니 그냥 clk준다. endmodule module edge_detector_n( /// falling edge detector input clk, cp_in, rst, /// cp : clock pulse output p_edge, n_edge /// 아주 짧은 펄스 하나를 내보냄. ); reg cp_in_old, cp_in_cur; always @ (negedge clk, posedge rst) begin if(rst) begin cp_in_old = 0; cp_in_cur = 0; end else begin cp_in_old <= cp_in_cur; cp_in_cur <= cp_in; /// <= : 대입 연산자(=과 유사한 기능) /// non-blocking 값이 들어가는 순서가 있을 때는 무조건 non-blocking /// 순차논리회로에서는 non-blocking을 무조건 쓰는 것이 안전하다. /// 조합논리회로(level triggering)에서는 무조건 blocking /// 블록 내에 한 줄일때는 아무거나 써도 상관없어. end end assign p_edge = ~cp_in_old & cp_in_cur; //// cp_old는 아직 0이니까 반전 assign n_edge = cp_in_old & ~cp_in_cur; //// cp_cur는 먼저 0이 되었으므로 반전시켜줌 endmodule
<리셋값 조정 & up_count로 시작>


이전에는 리셋하면 0 _ _ _ 이렇게 출력되었음.
코드 추가 후 0000 출력

<버튼을 뗄 때 up/down 변환>

'# Semiconductor > [Semicon Academy]' 카테고리의 다른 글
[Harman 세미콘 아카데미] 11일차 - 반도체 개요, C언어 개요 및 문법 (0) | 2023.07.03 |
---|---|
[Harman 세미콘 아카데미] 10일차 - Register, Memory (0) | 2023.06.30 |
[Harman 세미콘 아카데미] 8일차 - review, async/sync counter (0) | 2023.06.28 |
[Harman 세미콘 아카데미] 6~7일차 - 취업 특강 (0) | 2023.06.26 |
[Harman 세미콘 아카데미] 5일차 - Sequential Logic(LATCH, FF) (0) | 2023.06.23 |