본문 바로가기
# Semiconductor/- Semicon Academy

[Harman 세미콘 아카데미] 9일차 - Counter 활용(parameter, bin_to_dec, TFF, edge_detector, debouncing)

by Graffitio 2023. 6. 29.
[Harman 세미콘 아카데미] 9일차 - Counter 활용(parameter, bin_to_dec, TFF, edge_detector, debouncing)
728x90
반응형
[Schematic]

 

 

[parameter]

 

이전에 4bit, 12bit counter를 만들었는데,

parameter를 활용하면 굳이 bit마다 모듈을 만들어줄 필요가 없다.

 

4bit짜리 counter -> 이제는 필요없음.
instance할 때, N값만 지정해주면(안 해주면, N=4) 된다.

 

[Bin_to_Dec]

 

아무래도 알파벳으로 카운팅되면, 가독성이 떨어질 수 밖에 없다.

 ∴ 0123456789abcdef → 0123456789

 

이전에 만들었던, bin_to_dec 모듈을 counter에 인스턴스하면 된다.

bin_to_dec
bin_to_dec instance

 

[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 회로를 추가해주면 된다.

 

DFF 두 개를 이어붙인 모양이며, 각 출력 사이의 갭동안 pulse를 발생시키고 그 pulse를 사용함으로써 동기화 오류를 해결한다.

 

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

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 사용했을 때, cp_in이 동시에 들어가니 잘못된 회로

※ 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을 사용하는 것이 안전하다.

 

t입력에 따라 TFF 가 반응하도록 수정

위와 같이 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_count로 시작

 

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

 

p_edge 대신 n_edge 사용하면 된다.

 

728x90
반응형