7/17 FND, 시계

정유석·2024년 7월 16일

교육 - 베릴로그

목록 보기
10/28

FND 쉬프트

module ring_counter_fnd(
    input clk, reset_p,
    output reg [3:0] com);
    
    reg [20:0] clk_div = 0; //레지스터에 0주는건 시뮬레이션에서만 가능, 회로에서는 레지스터에 0을 줄수없기때문, 리셋으로 0줄수있음
    always @(posedge clk)clk_div = clk_div + 1;
    
    wire clk_div_nedge; //와이어에 0주는건 접지시키는것, 거기에 1들어가면 쇼트
    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

대략 1ms의 주기를 위해 21개비트 크기의 레지스터중 17번째비트를 사용하요 분주(2^17*10ns=1.3ms), FND의 com에 0인곳이 출력된다
시뮬레이션 결과

com의 0이 차례로 쉬프트(1110->1101->1011->0111->1110), clk마다 clk_div가 카운트하고 17번째 마다 펄스디텍터가 펄스발생

FND 개별 조절

4개의 7세그먼트중 한번에 하나씩만 키면서 지나가서 스위치에 따른 정해진 수를 표현

module fnd_test_top(
    input clk, reset_p,
    input [15:0] switch,
    output [3:0] com,
    output [7:0] seg_7);
    
    ring_counter_fnd(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  

인스턴스화하여 저장

module fnd_test_top(    //보드에 연결되는 것들(xdc포함)
    input clk, reset_p,
    input [15:0] switch,
    output [3:0] com,
    output [7:0] seg_7);
    
    fnd_cntr(.clk(clk), .reset_p(reset_p), .value(switch), .com(com), .seg_7(seg_7));   //fnd_cntr에 연결
    
endmodule    
module fnd_cntr(
    input clk, reset_p,
    input [15:0] value,
    output [3:0] com,
    output [7:0] seg_7);
    
    ring_counter_fnd(clk, reset_p, com);
    
    reg [3:0] hex_value;
    always @(posedge clk)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];
        endcase
    end
    
    decoder_7seg(.hex_value(hex_value), .seg_7(seg_7));
    
endmodule  

시계

60분주기

//60분주기
module clock_div_60(
    input clk, reset_p,
    input clk_source,
    output clk_div_60,
    output clk_div_60_nedge);
    
    reg [5:0]/*integer*/ cnt_clksource;    //32비트, 남는건 상관없음
    
    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

bcd 60진 카운터

module counter_bcd_60(
    input clk, reset_p,
    input clk_time,
    output reg [3:0] bcd1, bcd10);
    
    wire clk_time_nedge;
    edge_detector_n ed_clk(
        .clk(clk), .reset_p(reset_p), .cp(clk_time),
        .n_edge(clk_time_nedge));
        
    always @(posedge clk or posedge reset_p)begin
        if(reset_p)begin
            bcd1 = 0;
            bcd10 = 0;
        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

FND에 시간 표현

module watch_top(
    input clk, reset_p,
    output [3:0] com,
    output [7:0] seg_7);
    
    wire clk_usec, clk_msec, clk_sec, clk_min; //분주기 인스턴스 사용
    clock_div_100 usec_clk(.clk(clk), .reset_p(reset_p), .clk_div_100(clk_usec)); //100분주기 1us로 만듬
    clock_div_1000 msec_clk(.clk(clk), .reset_p(reset_p), .clk_source(clk_usec), .clk_div_1000(clk_msec)); //1000분주기 1ms로 만듬
    clock_div_1000 sec_clk(.clk(clk), .reset_p(reset_p), .clk_source(clk_msec), .clk_div_1000(clk_sec)); //1000분주기 1s로 만듬
    clock_div_60 min_clk(.clk(clk), .reset_p(reset_p), .clk_source(clk_sec), .clk_div_60(clk_min)); //60분주기 1분 만듬
    
    wire [3:0] sec1, sec10, min1, min10; //BCD 60진 카운터 인스턴스 사용
    counter_bcd_60 counter_sec(.clk(clk), .reset_p(reset_p), .clk_time(clk_sec), .bcd1(sec1), .bcd10(sec10)); //1의자리 초, 10의자리 초
    counter_bcd_60 counter_min(.clk(clk), .reset_p(reset_p), .clk_time(clk_min), .bcd1(min1), .bcd10(min10)); //1의자리 분, 10의자리 분
    
    wire [15:0] value;
    assign value = {min10, min1, sec10, sec1}; //결합연산자 사용하여 FND에 표시
    fnd_cntr fnd (.clk(clk), .reset_p(reset_p), .value(value), .com(com), .seg_7(seg_7));

endmodule

시계의 모드 변경

버튼 추가
.clk_source(clk_sec)를 .clk_source(inc_sec)로 변경
모드버튼을 눌러 멈춰도 내부적으로 시간을 계속 카운터 되서 모드버튼을 다시 눌러 시간이 진행되면 60초가 되지 않았는데도 분 자리에 1분 올라감

assign inc_min 조건문에서 모드버튼을 누르기전이면서, 60분주 클락이 30초 이상일때 clk_min은1 btn_min은 0인상태 에서 모드 버튼을 누르면 조건문에 의해서 clk_min에서 btn_min으로 변경 이것은 1->0으로 변경은 하강엣지발생, 이신호가 counter_bcd_60 모듈로 전달해서 '분'자리의 값 증가, assign inc_sec에서도 같은 증상이 있었지만 짧은 순간에서만 발생해서 알아치리기 어려움, 하지만 둘다 수정=>(clock_div_1000 sec_clk)와 (clock_div_60 min_clk)의clk_div_1000->clk_div_1000_nedge, clk_div_60->clk_div_60_nedge로 변경

0.5초, 30초동안 활성화되는 클락을 펄스로 바꿔서 10ns동안만 활성화 이때 버튼이 같이 눌려 같은 오류가 생길확률 극히 낮음
(다음 장에서 클락으로 신호를 보내는 모드와, 버튼으로 신호를 보내는 모드 분리)

module watch_top(
    input clk, reset_p,
    input [2:0] btn,
    output [3:0] com,
    output [7:0] seg_7);
    
    wire btn_mode;    //버튼 입력 펄스를 알기위함
    wire btn_sec;    //버튼 입력 펄스를 알기위함
    wire btn_min;    //버튼 입력 펄스를 알기위함
    wire set_watch; //모드 변경 토글
    wire inc_sec, inc_min;
    wire clk_usec, clk_msec, clk_sec, clk_min; //분주기 인스턴스 사용
    wire [3:0] sec1, sec10, min1, min10; //BCD 60진 카운터 인스턴스 사용
    wire [15:0] value;
         
     edge_detector_n ed_btn0(
        .clk(clk), .reset_p(reset_p), .cp(btn[0]),
        .p_edge(btn_mode));
  
     edge_detector_n ed_btn1(
        .clk(clk), .reset_p(reset_p), .cp(btn[1]),
        .p_edge(btn_sec));
   
     edge_detector_n ed_btn2(
        .clk(clk), .reset_p(reset_p), .cp(btn[2]),
        .p_edge(btn_min));
        
      
    T_flip_flop_p t_mode(.clk(clk), .reset_p(reset_p), .t(btn_mode), .q(set_watch)); //T플립플롭의 파워 리셋으로 q=0  
    
   
    assign inc_sec = set_watch ? btn_sec : clk_sec; //mux사용
    assign inc_min = set_watch ? btn_min : clk_min;
    
    
    clock_div_100 usec_clk(.clk(clk), .reset_p(reset_p), .clk_div_100(clk_usec)); //100분주기 1us로 만듬
    clock_div_1000 msec_clk(.clk(clk), .reset_p(reset_p), .clk_source(clk_usec), .clk_div_1000(clk_msec)); //1000분주기 1ms로 만듬
    clock_div_1000 sec_clk(.clk(clk), .reset_p(reset_p), .clk_source(clk_msec), ./*clk_div_1000*/clk_div_1000_nedge(clk_sec)); //1000분주기 1s로 만듬
    clock_div_60 min_clk(.clk(clk), .reset_p(reset_p), .clk_source(inc_sec), ./*clk_div_60*/clk_div_60_nedge(clk_min)); //60분주기 1분 만듬
                                                                            //30초 이후, 0.5초이후 에 모드버튼을 눌러도 분, 초가 올라가는 현상 해결
    //clk_time 변경
    counter_bcd_60 counter_sec(.clk(clk), .reset_p(reset_p), .clk_time(inc_sec), .bcd1(sec1), .bcd10(sec10)); //1의자리 초, 10의자리 초
    counter_bcd_60 counter_min(.clk(clk), .reset_p(reset_p), .clk_time(inc_min), .bcd1(min1), .bcd10(min10)); //1의자리 분, 10의자리 분
    
    
    assign value = {min10, min1, sec10, sec1}; //결합연산자 사용하여 FND에 표시
    fnd_cntr fnd (.clk(clk), .reset_p(reset_p), .value(value), .com(com), .seg_7(seg_7));

endmodule

xdc변경

##Buttons
set_property -dict { PACKAGE_PIN U18   IOSTANDARD LVCMOS33 } [get_ports reset_p]
set_property -dict { PACKAGE_PIN T18   IOSTANDARD LVCMOS33 } [get_ports btn[0]]
set_property -dict { PACKAGE_PIN W19   IOSTANDARD LVCMOS33 } [get_ports btn[1]]
set_property -dict { PACKAGE_PIN T17   IOSTANDARD LVCMOS33 } [get_ports btn[2]]
#set_property -dict { PACKAGE_PIN U17   IOSTANDARD LVCMOS33 } [get_ports btnD]

채터링

채터링은 보통 1ms안에 끝난다, 1ms클럭의 D플립플롭을 쓰면 상승엣지에서 한번만 읽기 때문에 채터링 구간을 넘길 수 있다.




1ms클럭을 넣으면 기존 10ns클럭으로 사용하던 것들과 맞지않아 비동기, enable포트로 엣지검출기로 1ms마다 펄스를 넣어 enable시키면, 버튼이 눌려지면 1ms마다 버튼 읽음
버튼 컨트롤 모듈

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(clk), .reset_p(reset_p), .cp(clk_div[16]), .n_edge(clk_div_nedge));	//대략1.3ms주기를 주기위해 16번 비트 사용
    
    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

버튼 컨트롤 모듈 인스턴스

module watch_top(
    input clk, reset_p,
    input [2:0] btn,
    output [3:0] com,
    output [7:0] seg_7);
    
    wire btn_mode;    //버튼 입력 펄스를 알기위함
    wire btn_sec;    //버튼 입력 펄스를 알기위함
    wire btn_min;    //버튼 입력 펄스를 알기위함
    wire set_watch; //모드 변경 토글
    wire inc_sec, inc_min;
    wire clk_usec, clk_msec, clk_sec, clk_min; //분주기 인스턴스 사용
    wire [3:0] sec1, sec10, min1, min10; //BCD 60진 카운터 인스턴스 사용
    wire [15:0] value;
    
    //채터링 보완
    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));
         
     /* //채터링 생김
     edge_detector_n ed_btn0(
        .clk(clk), .reset_p(reset_p), .cp(btn[0]),
        .p_edge(btn_mode));
  
     edge_detector_n ed_btn1(
        .clk(clk), .reset_p(reset_p), .cp(btn[1]),
        .p_edge(btn_sec));
   
     edge_detector_n ed_btn2(
        .clk(clk), .reset_p(reset_p), .cp(btn[2]),
        .p_edge(btn_min));
      */
        
      
    T_flip_flop_p t_mode(.clk(clk), .reset_p(reset_p), .t(btn_mode), .q(set_watch)); //T플립플롭의 파워 리셋으로 q=0  
    
   
    assign inc_sec = set_watch ? btn_sec : clk_sec; //mux사용
    assign inc_min = set_watch ? btn_min : clk_min;
    
    
    clock_div_100 usec_clk(.clk(clk), .reset_p(reset_p), .clk_div_100(clk_usec)); //100분주기 1us로 만듬
    clock_div_1000 msec_clk(.clk(clk), .reset_p(reset_p), .clk_source(clk_usec), .clk_div_1000(clk_msec)); //1000분주기 1ms로 만듬
    clock_div_1000 sec_clk(.clk(clk), .reset_p(reset_p), .clk_source(clk_msec), ./*clk_div_1000*/clk_div_1000_nedge(clk_sec)); //1000분주기 1s로 만듬
    clock_div_60 min_clk(.clk(clk), .reset_p(reset_p), .clk_source(inc_sec), ./*clk_div_60*/clk_div_60_nedge(clk_min)); //60분주기 1분 만듬
                                                                            //30초 이후, 0.5초이후 에 모드버튼을 눌러도 분, 초가 올라가는 현상 해결
    //clk_time 변경
    counter_bcd_60 counter_sec(.clk(clk), .reset_p(reset_p), .clk_time(inc_sec), .bcd1(sec1), .bcd10(sec10)); //1의자리 초, 10의자리 초
    counter_bcd_60 counter_min(.clk(clk), .reset_p(reset_p), .clk_time(inc_min), .bcd1(min1), .bcd10(min10)); //1의자리 분, 10의자리 분
    
    
    assign value = {min10, min1, sec10, sec1}; //결합연산자 사용하여 FND에 표시
    fnd_cntr fnd (.clk(clk), .reset_p(reset_p), .value(value), .com(com), .seg_7(seg_7));

endmodule

대략적인 그림

클락 리셋선은 그리면 그림이 복잡해지므로 생략, 있다는 표시만 함


  • 주의 : inc_min mux의 신호입력에도 set_watch선이 입력되어야함

그림 그리기 사이트

app.diagrams.net

profile
개인 기록공간

0개의 댓글