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

[Harman 세미콘 아카데미] 51일차 - Verilog(Stop Watch - 오류 검증, Prescaler 간소화, Mission)

by Graffitio 2023. 9. 6.
[Harman 세미콘 아카데미] 51일차 - Verilog(Stop Watch - 오류 검증, Prescaler 간소화, Mission)
728x90
반응형
[오류 발생한 Part 체크]

 

📌 Simulation을 이용한 방법

 

테스트벤치를 만들어 오류가 발생한 part를 체크하고 수정할 수 있다.

 

Testbench 관련 내용

 

[Harman 세미콘 아카데미] 2일차 - 보수체계, Testbench, Delay

[개요] 1. 논리 회로 - Combinational Logic (조합 논리 회로) : input이 주어지면, output이 고정된다. output이 다음 output에 영향을 주지 못 함. - Sequential Logic (순차 논리 회로) : 이전 output이 다음 output에 영

rangvest.tistory.com

 


 

📌 LED를 이용한 방법

 

가장 쉽고 간단한 방법이다.

시뮬레이션을 활용하려면, 테스트벤치를 만들고 레지스터 등록하는 등 해줘야할 것이 많다.

하지만 LED를 각 모듈의 출력과 연결시켜서 테스트하면,

LED가 들어오지 않는 곳이 오류가 발생한 부분이므로 오류를 쉽게 도출해낼 수 있다.

 

// LED를 활용한 오류 도출 방법
assign LED_bar[0] = lap_input; // 버튼을 누르면 LED_bar가 켜짐 -> 버튼 회로 문제 검출 과정
assign LED_bar[1] = lap_btn; // Edge Detector는 system clock 1만큼만 on되어서 눈으로 보기 어렵다.
assign LED_bar[2] = lap;

 


 

[Prescaler 간소화]

 

msec 모듈과 sec 모듈은 둘 다 1000분주하여 상위 단계의 clock을 출력해낸다.

따라서 1000분주 모듈을 만들어 인스턴스하여 사용하는 것으로 모듈을 간소화할 수  있다.

 

// 1000분주 clock
module clock_div_1000(
    input clk, clk_source, reset_p,
    output clk_div_1000
    );

    reg [8:0] cnt_clk_source;
    reg cp_div_1000; // cp : clock pulse
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) cnt_clk_source = 0; // reset이 들어오면 cnt = 0
        else if(clk_source) begin
            if (cnt_clk_source >= 499) begin
                cnt_clk_source = 0; // 0부터 499번까지 카운트하면 다시 0으로
                cp_div_1000 = ~cp_div_1000;
            end
            else cnt_clk_source = cnt_clk_source + 1;
        end
    end // 이렇게 카운트가 짝수일 경우, 위와 같이 코딩하여 1bit 줄일 수 있다.
    
    // 비정규 clock이므로 동기화가 필요하다.
    edge_detector_n edg_div_1000(.clk(clk), .cp_in(cp_div_1000), .rst(reset_p), .n_edge(clk_div_1000)); /// 안 쓰는 출력은 빼버리면 된다.
endmodule

 

    // Prescaler Instance
    wire clk_usec, clk_msec, clk_sec;
    clock_usec_stopwatch usec_clk(.clk(clk), .reset_p(reset_p), .clk_usec(clk_usec));
    clock_div_1000 msec_clk(.clk(clk), .clk_source(clk_usec), .reset_p(reset_p), .clk_div_1000(clk_msec));
    clock_div_1000 sec_clk(.clk(clk), .clk_source(clk_msec), .reset_p(reset_p), .clk_div_1000(clk_sec));
    clock_min min_clk(.clk(clk), .clk_sec(clk_sec), .reset_p(reset_p), .clk_min(clk_min));

 


 

[Mission]

 

📌 Mission

 

Q1.

    : sec와 msec가 출력되는 Stopwatch의 Block Diagram을 그리고 해당 기능을 구현하여라.

 

Q2 - Advanced

    : LED bar를 활용하여 min 단위를 출력하여라.

 


📌 Block Diagram

 

 


 

📌 Result(Counter 활용)

 

Clock Library
// Cora board sys clock : 125MHz
// 1 clock 당, 8ns

// Micro sec clock
module clock_usec_stopwatch(
    input clk, reset_p,
    input enable,
    output clk_usec
    );
    
    // 125개 카운트하면 1us
    reg [6:0] cnt_8nsec;
    wire cp_usec; // cp : clock pulse
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) cnt_8nsec = 0; // reset이 들어오면 cnt = 0
            else if(cnt_8nsec >= 124) cnt_8nsec = 0; // 0부터 124번까지 카운트하면 다시 0으로
            else cnt_8nsec = cnt_8nsec + 1;
     end
     
    assign cp_usec = cnt_8nsec < 63 ? 0 : 1; // 0~62 : 0, 63~124 : 1 
    
    // 비정규 clock이므로 동기화가 필요하다.
    edge_detector_n edg(.clk(clk), .cp_in(cp_usec), .rst(reset_p), .n_edge(clk_usec)); /// 안 쓰는 출력은 빼버리면 된다.
endmodule


// min clock
module clock_min(
    input clk, clk_sec, reset_p,
    output clk_min
    );

    // 60개 카운트하면 1s
    reg [5:0] cnt_sec;
    reg cp_min; // cp : clock pulse
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) cnt_sec = 0; // reset이 들어오면 cnt = 0
        else if(clk_sec) begin
            if (cnt_sec >= 29) begin
                cnt_sec = 0; // 0부터 번까지 카운트하면 다시 0으로
                cp_min = ~cp_min;
            end
            else cnt_sec = cnt_sec + 1;
        end
    end // 이렇게 카운트가 짝수일 경우, 위와 같이 코딩하여 1bit 줄일 수 있다.
    
    // 비정규 clock이므로 동기화가 필요하다.
    edge_detector_n edg_min(.clk(clk), .cp_in(cp_min), .rst(reset_p), .n_edge(clk_min)); /// 안 쓰는 출력은 빼버리면 된다.
    
endmodule


// 1000분주 clock
module clock_div_1000(
    input clk, clk_source, reset_p,
    output clk_div_1000
    );

    reg [8:0] cnt_clk_source;
    reg cp_div_1000; // cp : clock pulse
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) cnt_clk_source = 0; // reset이 들어오면 cnt = 0
        else if(clk_source) begin
            if (cnt_clk_source >= 499) begin
                cnt_clk_source = 0; // 0부터 499번까지 카운트하면 다시 0으로
                cp_div_1000 = ~cp_div_1000;
            end
            else cnt_clk_source = cnt_clk_source + 1;
        end
    end // 이렇게 카운트가 짝수일 경우, 위와 같이 코딩하여 1bit 줄일 수 있다.
    
    // 비정규 clock이므로 동기화가 필요하다.
    edge_detector_n edg_div_1000(.clk(clk), .cp_in(cp_div_1000), .rst(reset_p), .n_edge(clk_div_1000)); /// 안 쓰는 출력은 빼버리면 된다.
endmodule

 

Source_Counter
// 100bit_Counter
module counter_dec_100_stopwatch(
    input clk, reset_p,
    input clk_time,
    output reg [3:0] dec1, dec10, dec100
    );
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) begin
            dec1 = 0;
            dec10 = 0;
            dec100 = 0;
        end
        else if(clk_time) begin
            if(dec1 >= 9) begin 
                dec1 <= 0;
                if(dec10 >= 9) begin
                    dec10 = 0;
                    if(dec100 >= 9)
                        dec100 = 0;
                    else dec100 <= dec100 + 1;
                end
                else dec10 <= dec10 + 1;
            end
            else dec1 <= dec1 + 1;
        end
    end
endmodule


// 60bit_Counter
module counter_dec_60_stopwatch(
    input clk, reset_p,
    input clk_time,
    output reg [3:0] dec1, dec10
    );
    
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) begin
            dec1 = 0;
            dec10 = 0;
        end
        else if(clk_time) begin
            if(dec1 >= 9) begin 
                dec1 <= 0;
                if(dec10 >= 5) dec10 = 0;
                else dec10 <= dec10 + 1;
            end
            else dec1 <= dec1 + 1;
        end
    end
endmodule

 

StopWatch_Top
module Mission_Stop_Watch(
    input clk,
    input reset_p,
    input [1:0] btn, // 버튼 2개 쓸 것이므로
    output [3:0] com, 
    output [7:0] seg_7,
    output reg [7:0] LED_bar
    );
    
    wire start_stop_input, start_stop, start_stop_btn;
    
    // DFF를 위한 분주기
    reg [16:0] clk_div;
    always @(posedge clk) clk_div = clk_div + 1;
    
    // Start_stop 기능 구현 - usec과 연결
    D_flip_flop_n debnc_60(.d(btn[0]), .clk(clk_div[16]), .rst(reset_p), .q(start_stop_input)); // 바운싱 제거용 DFF
    edge_detector_n edg_start_stop(.clk(clk), .cp_in(start_stop_input), .rst(reset_p), .n_edge(start_stop_btn)); // Edga Detector
    t_flip_flop_p tff_start_stop(.clk(clk), .rst(reset_p), .t(start_stop_btn), .q(start_stop)); // start_stop 버튼 토글 기능 
    
    // Lap 기능 구현
    wire lap_input, lap_btn, lap;
    D_flip_flop_n debnc_lap(.d(btn[1]), .clk(clk_div[16]), .rst(reset_p), .q(lap_input)); // 바운싱 제거용 DFF
    edge_detector_n edg_lap(.clk(clk), .cp_in(lap_input), .rst(reset_p), .n_edge(lap_btn)); // Edga Detector
    t_flip_flop_p tff_lap(.clk(clk), .rst(reset_p), .t(lap_btn), .q(lap)); // 토글을 위한 TFF
    
//    // 시뮬레이션보다 좀 더 간편한 방법.
//    assign LED_bar[0] = lap_input; // 버튼을 누르면 LED_bar가 켜짐 -> 버튼 회로 문제 검출 과정
//    assign LED_bar[1] = lap_btn; // Edge Detector는 system clock 1만큼만 on되어서 눈으로 보기 어렵다.
//    assign LED_bar[2] = lap;
    
    // 분주기 인스턴스
    wire clk_usec, clk_msec, clk_sec;
    wire clk_start;
    assign clk_start = start_stop ? 0 : clk_usec;
    clock_usec_stopwatch usec_clk(.clk(clk), .reset_p(reset_p), .clk_usec(clk_usec));
    clock_div_1000 msec_clk(.clk(clk), .clk_source(clk_start), .reset_p(reset_p), .clk_div_1000(clk_msec));
    clock_div_1000 sec_clk(.clk(clk), .clk_source(clk_msec), .reset_p(reset_p), .clk_div_1000(clk_sec));
    clock_min min_clk(.clk(clk), .clk_sec(clk_sec), .reset_p(reset_p), .clk_min(clk_min)); 
    
    // 카운터 인스턴스
    wire [3:0] msec1, msec10, msec100;
    counter_dec_100_stopwatch cnt_100_msec(.clk(clk), .reset_p(reset_p), .clk_time(clk_msec), .dec1(msec1), .dec10(msec10), .dec100(msec100));
    wire [3:0] sec1, sec10; // sec1 : 1의자리, sec10 : 10의 자리
    counter_dec_60_stopwatch cnt_60_sec(.clk(clk), .reset_p(reset_p), .clk_time(clk_sec), .dec1(sec1), .dec10(sec10)); // sec count
    wire [3:0] min1, min10; // min1 : 1의자리, min10 : 10의 자리
    counter_dec_60_stopwatch cnt_60_min(.clk(clk), .reset_p(reset_p), .clk_time(clk_min), .dec1(min1), .dec10(min10)); // min count
    
    // Lap Value / value Select
    reg [15:0] lap_value;
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) lap_value = 0;
        else if(lap_btn) // lap_btn 나올 때 저장
            lap_value = {sec10, sec1, msec100, msec10};

    end
    
    // lap 버튼에 따른 FND 출력
    wire [15:0] value;
    assign value = lap ? lap_value : {sec10, sec1, msec100, msec10};
    assign value = lap ? lap_value : {sec10, sec1, msec_b2d[11:4]};
    FND_4digit_cntr fnd_cntr(.clk(clk), .rst(reset_p), .value(value), .com(com), .seg_7(seg_7));  /// 컨트롤러가 분주기 가지고 있으니 그냥 clk준다.
    
    // min을 LED_bar를 통하여 출력
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) LED_bar = 0;
        for (integer i = 0; i < 8; i = i + 1) begin
            LED_bar[i] = (min1 >= i + 1) ? 1 : 0;
        end
    end
endmodule

Start/Stop, Lap, LED_bar 정상적으로 동작

 


 

📌 Result(Bin_to_dec 활용)

 

Clock Library

    위와 동일

 

Bin_to_dec
module bin_to_dec_stopwatch( //// 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

 

StopWatch_Top
module Mission_Stop_Watch(
    input clk,
    input reset_p,
    input [1:0] btn, // 버튼 2개 쓸 것이므로
    output [3:0] com, 
    output [7:0] seg_7,
    output reg [7:0] LED_bar
    );
    
    wire start_stop_input, start_stop, start_stop_btn;
    
    // DFF를 위한 분주기
    reg [16:0] clk_div;
    always @(posedge clk) clk_div = clk_div + 1;
    
    // Start_stop 기능 구현 - usec과 연결
    D_flip_flop_n debnc_60(.d(btn[0]), .clk(clk_div[16]), .rst(reset_p), .q(start_stop_input)); // 바운싱 제거용 DFF
    edge_detector_n edg_start_stop(.clk(clk), .cp_in(start_stop_input), .rst(reset_p), .n_edge(start_stop_btn)); // Edga Detector
    t_flip_flop_p tff_start_stop(.clk(clk), .rst(reset_p), .t(start_stop_btn), .q(start_stop)); // start_stop 버튼 토글 기능 
    
    // Lap 기능 구현
    wire lap_input, lap_btn, lap;
    D_flip_flop_n debnc_lap(.d(btn[1]), .clk(clk_div[16]), .rst(reset_p), .q(lap_input)); // 바운싱 제거용 DFF
    edge_detector_n edg_lap(.clk(clk), .cp_in(lap_input), .rst(reset_p), .n_edge(lap_btn)); // Edga Detector
    t_flip_flop_p tff_lap(.clk(clk), .rst(reset_p), .t(lap_btn), .q(lap)); // 토글을 위한 TFF
    
    // 분주기 인스턴스
    wire clk_usec, clk_msec, clk_sec;
    wire clk_start;
    assign clk_start = start_stop ? 0 : clk_usec;
    clock_usec_stopwatch usec_clk(.clk(clk), .reset_p(reset_p), .clk_usec(clk_usec));
    clock_div_1000 msec_clk(.clk(clk), .clk_source(clk_start), .reset_p(reset_p), .clk_div_1000(clk_msec));
    clock_div_1000 sec_clk(.clk(clk), .clk_source(clk_msec), .reset_p(reset_p), .clk_div_1000(clk_sec));
    clock_min min_clk(.clk(clk), .clk_sec(clk_sec), .reset_p(reset_p), .clk_min(clk_min)); 
    
    reg [9:0] cnt_clk_msec;
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) cnt_clk_msec = 0; // reset이 들어오면 cnt = 0
        else if(clk_msec) begin
            if (cnt_clk_msec >= 999) begin
                cnt_clk_msec = 0;
            end
            else cnt_clk_msec = cnt_clk_msec + 1;
        end
    end
    
    wire [15:0] msec_b2d;
    bin_to_dec_stopwatch msec_clk_b2d(.bin({2'b00, cnt_clk_msec}), .bcd(msec_b2d));
    
    // 카운터 인스턴스
    wire [3:0] sec1, sec10; // sec1 : 1의자리, sec10 : 10의 자리
    counter_dec_60_stopwatch cnt_60_sec(.clk(clk), .reset_p(reset_p), .clk_time(clk_sec), .dec1(sec1), .dec10(sec10)); // sec count
    wire [3:0] min1, min10; // min1 : 1의자리, min10 : 10의 자리
    counter_dec_60_stopwatch cnt_60_min(.clk(clk), .reset_p(reset_p), .clk_time(clk_min), .dec1(min1), .dec10(min10)); // min count
    
    // Lap Value / value Select
    reg [15:0] lap_value;
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) lap_value = 0;
        else if(lap_btn) // lap_btn 나올 때 저장
            lap_value = {sec10, sec1, msec_b2d[11:4]};
    end
    
    // lap 버튼에 따른 FND 출력
    wire [15:0] value;
    assign value = lap ? lap_value : {sec10, sec1, msec_b2d[11:4]};
    FND_4digit_cntr fnd_cntr(.clk(clk), .rst(reset_p), .value(value), .com(com), .seg_7(seg_7));  /// 컨트롤러가 분주기 가지고 있으니 그냥 clk준다.
    
    // min을 LED_bar를 통하여 출력
    always @(posedge clk, posedge reset_p) begin
        if(reset_p) LED_bar = 0;
        for (integer i = 0; i < 8; i = i + 1) begin
            LED_bar[i] = (min1 >= i + 1) ? 1 : 0;
        end
    end
endmodule

 

Bin_to_Dec이 궁금하다면?

아래 링크 참조

 

 

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

[Schematic] [parameter] 이전에 4bit, 12bit counter를 만들었는데, parameter를 활용하면 굳이 bit마다 모듈을 만들어줄 필요가 없다. [Bin_to_Dec] 아무래도 알파벳으로 카운팅되면, 가독성이 떨어질 수 밖에 없

rangvest.tistory.com

 


 

 

728x90
반응형