반도체 Study/디지털 설계

Verilog - 7-Segment Count Up (0~9999) / FPGA

잇(IT) 2024. 6. 2. 12:24
728x90

1. Introduction

2. Signal Description

3. Verilog Code

4. Trouble Shooting


1. Introduction

 

1.1. Overview

- 0~9999까지의 숫자를 Count하여 7-Segment를 통해 해당 Count한 숫자를 표현하는 기능을 구현

 

1.2. Features

Version 1

 

- Clock 신호를 분주하는 모듈

- 4-Digits에 필요 신호를 전달하는 모듈

- 7-Segment에 필요 신호를 전달하는 모듈

- 숫자를 Count하여 TOP 모듈에 신호를 전달하는 모듈

 

Version 2

 

- Clock 신호를 분주하는 모듈

- 4-Digits에 신호를 전달하는 모듈

- 숫자 Count 및 7-Segment에 신호를 전달하는 모듈

 

1.3. Block Diagram

 

Version 1

 

Version 2

 

합성 결과 실제 구성도


2. Signal Description

 

2.1. Overview

- 0~9999까지의 숫자를 7-Segment로 표현하기 위해서 Input을 통해 Clock 신호와 Reset 신호 2개를 전달하고 Output으로 4-Digits, 7-Segment로 출력을 전달한다.

 

2.2. Detailed Signal Descriptions

2.2.1. Clock

- 현재 사용중인 FPGA의 장비는 하나의 Clock 신호 pin이 존재하고, 50MHz의 주파수를 전달한다. 

 

2.2.2. Reset

- 7-Segment를 동작 시키는 신호로 1일 경우 Count가 시작되고, 0일 경우 초기화가 된다.

 


3. Verilog Code

Version 1

 

TOP.v

`timescale 1ns / 1ps

module TOP(
    input CLK_REF,
    input rst,
    output [3:0] digit,
    output [6:0] segment
    );
    
    assign CLK_50MHz = CLK_REF;
    
    wire CLK_5MHz_wire;
    wire CLK_1Hz_wire;
    
    wire [3:0] digit_wire;
   
    assign digit = digit_wire;
    
    clk_wiz_0 clk_wiz_0_M(
        .clk_in1(CLK_50MHz), 
        .clk_out1(CLK_5MHz_wire)
        );
        
    clk_1Hz clk_1Hz_M(
        .CLK_5MHz(CLK_5MHz_wire), 
        .CLK_1Hz(CLK_1Hz_wire)
        );

    digit_sel digit_sel_M(
        .CLK_5MHz(CLK_5MHz_wire), 
        .rst(rst), 
        .digit(digit_wire)
        );

    segment_timer segment_timer_M(
        .CLK_1Hz(CLK_1Hz_wire),
        .rst(rst), 
        .digit_wire(digit_wire),
        .segment(segment),
        .digit(digit)
        );  
endmodule

 

TOP 모듈의 경우 분주기를 위한 모듈 2개, digit을 선택하기 위한 모듈 1개, Counter를 위한 모듈 1개를 호출한다.

 

* clk_wiz_0의 경우 vivado의 clock wizard를 사용하여 분주기 모듈을 생성하였다.

 

 

clk_1Hz.v

`timescale 1ns / 1ps

module clk_1Hz(
    input CLK_5MHz,
    output CLK_1Hz
    );
    
    reg [23:0] cnt_1Hz = 0;
    reg clk_out_reg = 0;
    
    always @(posedge CLK_5MHz)begin
        if(cnt_1Hz == 24_999) begin
            cnt_1Hz <= 0;
            clk_out_reg = ~clk_out_reg;
        end
        else begin
            cnt_1Hz <= cnt_1Hz + 1;
        end
    end
        
    assign CLK_1Hz = clk_out_reg;
    
endmodule

 

clk_1Hz 모듈의 경우 5MHz의 clk을 전달 받아 1Hz로 분주기를 하는 모듈이다.

(cnt_1Hz를 통해 clk 주기를 조절한다. 단, 위 코드에서는 숫자의 빠른 변화를 직접 확인하기 위해 1Hz보다는 빠른 분주기를 설정하였다.)

 

digit_sel.v

`timescale 1ns / 1ps

module digit_sel(
    input CLK_5MHz,
    input rst,
    output reg [3:0] digit
    );
    
    integer i = 0;
    
    always @(posedge CLK_5MHz or negedge rst) begin
        if(!rst) begin
            digit <= 4'b0000;
            i <= 0; // Reset i to 0
        end
        else begin
            case(i)
                0 : digit <= 4'b0001;
                1 : digit <= 4'b0010;
                2 : digit <= 4'b0100;
                3 : digit <= 4'b1000;               
            endcase
            i <= (i + 1) & 3; // Increment i and ensure it wraps around
        end
    end
endmodule

 

digit_sel 모듈의 경우 5MHz 클럭을 전달받는다.

총 4개의 digit을 4bit에 할당하여 posedge마다 digit을 0~3까지 번갈아가면서 선택한다.

 

segment_timer.v

module segment_timer(
    input CLK_1Hz,
    input rst,
    input [3:0] digit_wire,
    output [6:0] segment,
    output [3:0] digit
);

    reg [14:0] timer = 0;

    reg [10:0] mod_1000 = 0, mod_100 = 0, mod_10 = 0;
    reg [3:0] digit_1 = 0, digit_10 = 0, digit_100 = 0, digit_1000 = 0;
    wire [6:0] segment_1000, segment_100, segment_10, segment_1;

    reg [6:0] segment_reg = 7'b0000000;

    assign segment = segment_reg;

    always @(posedge CLK_1Hz or negedge rst) 
    begin
        if(!rst) begin
            timer <= 0;
        end
        else begin
            if(timer == 9999) begin
                timer <= 0;
            end
            else begin
                timer <= timer + 1;
            end
            digit_1000 <= timer/1000;
            mod_1000 <= timer-(digit_1000*1000);

            digit_100 <= mod_1000/100; 
            mod_100 <= mod_1000-(digit_100*100);

            digit_10 <= mod_100/10;
            mod_10 <= mod_100-(digit_10*10);

            digit_1 <= mod_10;
        end
    end

    always @(digit_wire) begin
        case(digit_wire)
            4'b0001 : segment_reg = segment_1;
            4'b0010 : segment_reg = segment_10;
            4'b0100 : segment_reg = segment_100;
            4'b1000 : segment_reg = segment_1000;
        endcase
    end

    bcd7segment seg1000(.count(digit_1000), .segment(segment_1000));
    bcd7segment seg100(.count(digit_100), .segment(segment_100));
    bcd7segment seg10(.count(digit_10), .segment(segment_10));
    bcd7segment seg1(.count(digit_1), .segment(segment_1));
endmodule

 

segment_timer의 모듈의 경우 숫자를 Count하는 코드가 작성되어 있으며, bcd7segment 모듈을 통해 각 digit에 숫자에 신호를 전달한다.

digit_1, 10, 100, 1000은 각 자리수에 0~9까지 숫자를 할당하기 위한 숫자이고, mod는 각 자리수를 구할 때 나머지 값을 저장하기 위한 reg이다.

1Hz 클럭의 posedge마다 Count를 하며 해당 숫자의 각 자리수를 구한 다음 각 숫자를 bcd7segment를 모듈을 통해 값을 전달한다.

digit_wire를 통해 5MHz 클럭의 속도로 digit의 선택값을 전달하여 각 digit에 count한 값을 전달한다.

 

bcd7segment.v

`timescale 1ns / 1ps

module bcd7segment(
    input [3:0] count,
    output reg [6:0] segment
    );
    
    always @(count) begin
        case(count)
            4'd0 : segment = 7'b000_0001;
            4'd1 : segment = 7'b100_1111;
            4'd2 : segment = 7'b001_0010;
            4'd3 : segment = 7'b000_0110;
            4'd4 : segment = 7'b100_1100;
            4'd5 : segment = 7'b010_0100;
            4'd6 : segment = 7'b010_0000;
            4'd7 : segment = 7'b000_1111;
            4'd8 : segment = 7'b000_0000;
            4'd9 : segment = 7'b000_0100;
            default : segment = 7'b000_0000;
        endcase
    end
endmodule

 

bcd7segment 모듈의 경우 각 자리의 숫자를 전달 받아 해당 7-segment에 신호를 전달하여 LED에 불이 들어오도록 한다.


Version 2

 

TOP.v

`timescale 1ns / 1ps

module TOP( 
    input clk_50MHz,
    input reset,
    output [0:6] seg,
    output [3:0] digit
    );
    
    wire w_10Hz;
    wire [3:0] w_1s, w_10s, w_100s, w_1000s; // 
    
    tenHz_gen hz10(.clk_50MHz(clk_50MHz), .reset(reset), .clk_10Hz(w_10Hz));
    
    digits digs(.clk_10Hz(w_10Hz), .reset(reset), .ones(w_1s), .tens(w_10s), .hundreds(w_100s), .thousands(w_1000s));
    
    seg7_control seg7(.clk_50MHz(clk_50MHz), .reset(reset), .ones(w_1s), .tens(w_10s), .hundreds(w_100s), .thousands(w_1000s), .seg(seg), .digit(digit));
    
endmodule

 

tenHz_gen.v

`timescale 1ns / 1ps

module tenHz_gen(
    input clk_50MHz,
    input reset,
    output clk_10Hz
    );
    
    reg [22:0] ctr_reg = 0;
    reg clk_out_reg = 0;
    
    always @(posedge clk_50MHz or posedge reset)
        if(reset) begin
            ctr_reg <= 0;
            clk_out_reg <= 0;
        end
        else
            if(ctr_reg == 2_499_999) begin // 100MHz / 10Hz / 2 = 5,000,000
                ctr_reg <= 0;
                clk_out_reg <= ~clk_out_reg;
            end
            else
                ctr_reg <= ctr_reg + 1;
                
    assign clk_10Hz = clk_out_reg;
    
endmodule

 

digits.v

`timescale 1ns / 1ps

module digits(
    input clk_10Hz,
    input reset,
    output reg [3:0] ones,
    output reg [3:0] tens,
    output reg [3:0] hundreds,
    output reg [3:0] thousands
    );
    
    always @(posedge clk_10Hz or posedge reset)
        if(reset)
            ones <= 0;
        else
            if(ones == 9)
                ones <= 0;
            else
                ones <= ones + 1;
                
    always @(posedge clk_10Hz or posedge reset)
        if(reset)
            tens <= 0;
        else
            if(ones == 9)
                if(tens == 9)
                    tens <= 0;
                else
                    tens <= tens + 1;

    always @(posedge clk_10Hz or posedge reset)
        if(reset)
            hundreds <= 0;
        else
            if(tens == 9 && ones == 9)
                if(hundreds == 9)
                    hundreds <= 0;
                else
                    hundreds <= hundreds + 1;
                    
    always @(posedge clk_10Hz or posedge reset)
        if(reset)
            thousands <= 0;
        else
            if(hundreds == 9 && tens == 9 && ones == 9)
                if(thousands == 9)
                    thousands <= 0;
                else
                    thousands <= thousands + 1;                    
                    
endmodule

 

seg7_control.v

`timescale 1ns / 1ps

module digits(
    input clk_10Hz,
    input reset,
    output reg [3:0] ones,
    output reg [3:0] tens,
    output reg [3:0] hundreds,
    output reg [3:0] thousands
    );
    
    always @(posedge clk_10Hz or posedge reset)
        if(reset)
            ones <= 0;
        else
            if(ones == 9)
                ones <= 0;
            else
                ones <= ones + 1;
                
    always @(posedge clk_10Hz or posedge reset)
        if(reset)
            tens <= 0;
        else
            if(ones == 9)
                if(tens == 9)
                    tens <= 0;
                else
                    tens <= tens + 1;

    always @(posedge clk_10Hz or posedge reset)
        if(reset)
            hundreds <= 0;
        else
            if(tens == 9 && ones == 9)
                if(hundreds == 9)
                    hundreds <= 0;
                else
                    hundreds <= hundreds + 1;
                    
    always @(posedge clk_10Hz or posedge reset)
        if(reset)
            thousands <= 0;
        else
            if(hundreds == 9 && tens == 9 && ones == 9)
                if(thousands == 9)
                    thousands <= 0;
                else
                    thousands <= thousands + 1;                    
                    
endmodule

 

 

seg7_control.v

`timescale 1ns / 1ps

module seg7_control(
    input clk_50MHz,
    input reset,
    input [3:0] ones,
    input [3:0] tens,
    input [3:0] hundreds,
    input [3:0] thousands,
    output reg [0:6] seg,
    output reg [3:0] digit
    );
    
    parameter ZERO  = 7'b000_0001;
    parameter ONE   = 7'b100_1111;
    parameter TWO   = 7'b001_0010;
    parameter THREE = 7'b000_0110;
    parameter FOUR  = 7'b100_1100;
    parameter FIVE  = 7'b010_0100; 
    parameter SIX   = 7'b010_0000;
    parameter SEVEN = 7'b000_1111;
    parameter EIGHT = 7'b000_0000;
    parameter NINE  = 7'b000_0100;
    
    reg [1:0] digit_select;
    reg [16:0] digit_timer;

    always @(posedge clk_50MHz or posedge reset) begin
        if(reset) begin
            digit_select <= 0;
            digit_timer <= 0;
        end
        else
            if(digit_timer == 49_999) begin
                digit_timer <= 0;
                digit_select <= digit_select + 1;
            end
            else
                digit_timer <= digit_timer + 1;
    end
    
    always @(digit_select) begin
        case(digit_select)
            2'b00 : digit = 4'b0001;
            2'b01 : digit = 4'b0010;
            2'b10 : digit = 4'b0100;
            2'b11 : digit = 4'b1000;
        endcase
    end
    
    always @*
        case(digit_select)
            2'b00 : begin
                        case(ones)
                            4'b0000 : seg = ZERO;
                            4'b0001 : seg = ONE;
                            4'b0010 : seg = TWO;
                            4'b0011 : seg = THREE;
                            4'b0100 : seg = FOUR;
                            4'b0101 : seg = FIVE;
                            4'b0110 : seg = SIX;
                            4'b0111 : seg = SEVEN;
                            4'b1000 : seg = EIGHT;
                            4'b1001 : seg = NINE;
                        endcase
                    end
           2'b01 : begin
                        case(tens)
                            4'b0000 : seg = ZERO;
                            4'b0001 : seg = ONE;
                            4'b0010 : seg = TWO;
                            4'b0011 : seg = THREE;
                            4'b0100 : seg = FOUR;
                            4'b0101 : seg = FIVE;
                            4'b0110 : seg = SIX;
                            4'b0111 : seg = SEVEN;
                            4'b1000 : seg = EIGHT;
                            4'b1001 : seg = NINE;
                        endcase
                    end
           2'b10 : begin
                        case(hundreds)
                            4'b0000 : seg = ZERO;
                            4'b0001 : seg = ONE;
                            4'b0010 : seg = TWO;
                            4'b0011 : seg = THREE;
                            4'b0100 : seg = FOUR;
                            4'b0101 : seg = FIVE;
                            4'b0110 : seg = SIX;
                            4'b0111 : seg = SEVEN;
                            4'b1000 : seg = EIGHT;
                            4'b1001 : seg = NINE;
                        endcase
                    end
           2'b11 : begin
                        case(thousands)
                            4'b0000 : seg = ZERO;
                            4'b0001 : seg = ONE;
                            4'b0010 : seg = TWO;
                            4'b0011 : seg = THREE;
                            4'b0100 : seg = FOUR;
                            4'b0101 : seg = FIVE;
                            4'b0110 : seg = SIX;
                            4'b0111 : seg = SEVEN;
                            4'b1000 : seg = EIGHT;
                            4'b1001 : seg = NINE;
                        endcase
                    end
        endcase
endmodule

 


4. Trouble Shooting

 

1. 4_digit, 7_segment

- 0~9999까지 숫자를 Count하면서 각 자리의 숫자를 4개의 digit에 숫자를 부여하는 작업

기존의 0~9까지 숫자를 7_segment를 통해 표시 할 때는 하나의 digit에 1의 신호를 부여하여 숫자를 표시하였다.

하지만 0~9999의 숫자를 표시하기 위해선 4개의 digit을 사용해야 한다. 하드웨어 특성상 4개의 digit에 신호를 전달하기 위해선

1. 신호를 전달할 digit에만 1의 신호가 보내져야 하고, 나머지 3개의 digit에는 0의 신호를 보내야한다. 즉, 1의 신호가 전달된 digit에 신호를 전달하면 해당 digit의 7-segment에 불빛이 들어고 나머지 digit개에는 0의 신호가 전달되기 때문에 불이 꺼져있는 상태가 된다.

-> 하나의 digit에만 1의 신호를 전달해야 하며, 1의 신호를 받은 digit에만 불이 들어온다면 2자리 이상 숫자는 어떻게 해결해야 할까

 

=> 4개의 digit에 1의 신호를 매우 빠른 속도로 번갈아 가면서 전달하면 된다. 불이 켜졌다 꺼졌다하는 속도가 매우 빠를 경우 사람이 보기에는 전부 불빛이 들어온 것처럼 보인다.

분주기를 통해 digit에 주는 신호를 매우 빠른 신호로 전달할 수 있다.

+ 추가로 4 digit에 신호를 번갈아 주는 주기는 숫자가 1이 커지는 속도보다 빨라야 한다. digit에 주는 신호가 count가 1 커지는 속도보다 느리게 되면 4개의 digit에 신호를 보내는 도중에 표시해야 할 값이 변경될 수 있기 때문이다.

728x90