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

[Harman 세미콘 아카데미] 12일차 - Review, 디지털 시계 만들기(교육 과정 외)

by Graffitio 2023. 7. 4.
[Harman 세미콘 아카데미] 12일차 - Review, 디지털 시계 만들기(교육 과정 외)
728x90
반응형
[Review]

 

※ FPGA는 LUT과 DFF들의 배열

     (LUT 뒤에 DFF가 붙어 있음)

     - 메모리 만들 때 DFF를 그냥 가져다 쓰면, LUT이 낭비됨.

       ∴ 메모리 회로가 따로 있다.

 

※ 회로 합성을 어떻게 하느냐에 따라 결과가 달라진다.

 

    1) ASIC 용으로 합성

         : CMOS 회로로 합성됨(Gate들이 그대로 표현)

    2) FPGA 용으로 합성

         : LUT 회로로 합성됨(모든 gate들이 LUT으로 표현)

 

가독성 & 커뮤니케이션 능력

     : 기업에서 다루는 회로는 크기가 방대하므로 part를 나눠서 팀으로 진행한다.

        ∴ 코딩의 가독성 & 팀원간의 커뮤니케이션 능력이 대두됨.

 

※ Verilog 문법

 

1. 변수

 

    1) 자료형

        ▶ Net : 소자간의 물리적인 연결을 추상화(H/W적 특성)

             - wire : 함축된 논리적 동작이나 기능을 갖지 않는 단순한 연결을 위한 net

                         assign문의 좌변

                         default 자료형

                         ex) wire [3:0] w1, w2;

        ▶ Variable : 절차형 할당문 사이의 값을 임시 저장(S/W적 특성)

             - reg : 절차형 할당문의 값을 저장하는 객체의 자료형

                        always문의 출력 부분

                        ex) reg [2:0] r1,r2;

             - integer :  정수형 값을 취급함 → [31:0]

                         ex) integer i (for 문의 i를 선언할 때 사용)

자료형 Gate-primitive 연속 할당문 절차형 할당문 assign - disassign
PCA
force - release
PCA
Net O O X X O
Variable Comb : X
Seq : O
X O O O

             cf) 연속적 할당문

                   : assign 문법을 사용하여 Combinational logic을 선언하는 것

                  절차적 할당문

                   : always, initial 등의 문법을 사용하여 logic을 선언하는 것

 

    2) 논리값

논리값 의미
0 Logic Zero, False condition
1 Logic One, True condition
x - 입력 : don't care(0이든 1이든 상관없다.)
- 출력 : unknown(0 또는 1의 값을 가진다.)
z High- Impedance(연결이 끊긴 상태)

2. 연산자

 

연산자 의미 연산자 의미
{}, {{}} 결합, 반복 ^ 비트 XOR
+, -, *, /, ** 산술 ^~, ~^ 비트 XNOR(비트 등가)
% 나머지 & 축약 AND
>, >=, <, <= 관계 ~& 축약 NAND
! 논리 부정 | 축약 OR
&& 논리 AND ~| 축약 NOR
|| 논리 OR ^ 축약 XOR
== 논리 등가 ^~, ~^ 축약 XNOR
!= 논리 부등 << 논리 왼쪽 시프트
=== case 등가 >> 논리 오른쪽 시프트
!== case 부등 <<< 산술 왼쪽 시프트
~ 비트 부정 >>> 산술 오른쪽 시프트
& 비트 AND ____? ____ : ____ ; 조건 연산자
| 비트 OR or Event or
(sens' list 내부에 사용 (= ,))

    1) 연산자 우선 순위

        < 단항 → 산술 → 시프트 → 비교 → 축약 → 논리 → 조건 >

     

    2) 조건연산자

          Q = 조건식 ? A : B;

          i) 조건식이 참(1)일 경우,

              Q = A

          ii) 조건식이 거짓(0)일 경우,

              Q = B


3. 제어문

  

     3-1. 조건문

             if/else, switch, case

             → 흐름 제어(C언어에서만)가 아닌, MUX를 만들 때 사용한다.

module ifelse( /// if/else 예시
	input clk, rst, d,
    output reg q
);
	always @(posedge clk, negedge rst) begin
    	if (rst) d = d;
        else     d = q;
    end
endmodule


module case( // case 예시
	input clk, rst,
    input [3:0] d,
    output reg [7:0] q
);
	always @ (posedge clk, negedge rst) begin
    	case(d)
        	4'0001 : q = 8'b0000_0001;
        	4'0010 : q = 8'b0000_0010;
        	4'0100 : q = 8'b0000_0100;
        	4'1000 : q = 8'b0000_1000;
        endcase
    end
endmodule

 

     3-2. 반복문

 

            1) initial

                   - 모듈이 동작하는 처음에 한 번 실행한다.

 

            2) always @(sensitivity list)

                    - sensitive list 의 조건이 만족될때마다, always block을 반복한다.

                    - 종류 → always @ (a, b) : a 또는 b가 변할 때, 반복

                                   always @ (*) : *는 assign에 영향을 미치는 모든 signal

                                   always @ (posedge clk) : rising clk일 때, 반복

                                   always @ (negedge rstn) : falling rstn일 때, 반복

 

             3) for

                  for(초기식 : 조건식 : 할당문);

                        실행문;                             

                     : 초기식 1회 실행 후, 조건식이 참인 동안 실행문을 반복한다.


4. 배열

 

   : 1비트 또는 n비트를 가지는 여러 원소를 의미

   - reg, integer, time, 벡터 레지스터 데이터 타입의 배열을 선언할 수 있다.
     <data type> [msb:lsb] <배열 이름> [배열 갯수 -1 : 0]
   - 보통 depth라고 한다.

      cf) 2차원 배열 : < 배열 명 > [행 범위] [열 범위]

///// 예   시 /////

integer memory [0:3]; // 4개의 memory 변수의 배열
reg memory [15:0]; // 16개의 memory 레지스터 변수의 배열
reg [5:0] mem [0:9]; // 10개의 mem 변수의 배열, 각 변수는 6비트 폭을 가짐
integer mem [3:0][3:0]; // 4x4 2D array

위와 같이 2차원 사각형을 생각하면 쉽게 이해할 수 있다.

∴ mem[0] <= data : 0번째 배열에 data를 넣어주겠다는 의미


[시계 만들기] - 교육 과정 외

 

1. 모듈

    watch : 카운터를 활용해 hour, minute을 표현

    bin to dec : watch 에서 출력한 binary 값을 decimal로 변환

    decoder 7_seg : decimal 값을 7_seg 신호로 변환

    ring counter : 다이나믹 구동을 이용하여 출력 포트 절약

    FND controller : com 값에 따라, 디코더로 보내는 hex_value 제어

    FND Top module : watch, bin to dec, controller 모듈을 통합

    Clock divider : ring counter & watch 에 활용

 


2. 코딩

module watch(
    input clk, rst,
    output reg [3:0] hour,  /// 0~12까지니까 4bit
    output reg [5:0] minute /// 0~59까지니까 6bit
);

    reg [26:0] clk_div;
    always @ (posedge clk) clk_div = clk_div + 1; /// 125MHz -> 1Hz
    
    always @ (posedge clk_div[24], negedge rst) begin /// 8ns * 2^27 = 약 1sec
        if(rst) minute <= 0;
        else begin
            if(minute >= 59) minute <= 0;
            else             minute <= minute + 1;
        end
    end
    
    always @ (posedge clk_div[24], negedge rst) begin
        if(rst) hour <= 0;
        else begin
            if(hour >= 11 && minute >= 59) hour <= 0;
            else if(minute >= 59) hour <= hour + 1;
        end
    end
endmodule

//////////////////////////////////////////////////////////////////////////////////

module bin_to_dec(
    input [7:0] bin,
    output reg [7:0] bcd
);
    reg [3:0] i;
    
    always @(bin) begin
        bcd = 0;
        for (i=0 ; i< 8 ; i = i+1) begin // 12 ->8
            bcd = {bcd[6:0], bin[7-i]}; /// 14 -> 6, 11 -> 7
            if(i<7 && bcd[3:0] >4) bcd[3:0] = bcd[3:0] + 3; // 11 -> 7
            if(i<7 && bcd[7:4] >4) bcd[7:4] = bcd[7:4] + 3; // 11 -> 7
//            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 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; /// 0 
            4'b0001 : seg_7 = 8'b1001_1111; /// 1
            4'b0010 : seg_7 = 8'b0010_0101; /// 2 
            4'b0011 : seg_7 = 8'b0000_1101; /// 3 
            4'b0100 : seg_7 = 8'b1001_1001; /// 4
            4'b0101 : seg_7 = 8'b0100_1001; /// 5
            4'b0110 : seg_7 = 8'b0100_0001; /// 6
            4'b0111 : seg_7 = 8'b0001_1111; /// 7
            4'b1000 : seg_7 = 8'b0000_0001; /// 8
            4'b1001 : seg_7 = 8'b0000_1001; /// 9
            
            4'b1010 : seg_7 = 8'b0001_0001; /// a 10
            4'b1011 : seg_7 = 8'b1100_0001; /// b 11
            4'b1100 : seg_7 = 8'b0110_0011; /// c 12
            4'b1101 : seg_7 = 8'b1000_0101; /// d 13
            4'b1110 : seg_7 = 8'b0110_0001; /// e 14
            4'b1111 : seg_7 = 8'b0111_0001; /// f 15
        endcase
    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_watch_cntr( //// ring counter top module
    input clk, rst,
    input [7:0] value1, value2,
    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; /// common anode이므로 0을 줘야 켜진다.
    
    always @(posedge clk) begin
        case(com)
            4'b1110 : hex_value = value1[7:4]; //// CT4
            4'b1101 : hex_value = value1[3:0];
            4'b1011 : hex_value = value2[7:4];
            4'b0111 : hex_value = value2[3:0];
        endcase 
    end
endmodule

//////////////////////////////////////////////////////////////////////////////////

module watch_fnd_top(
    input clk, rst,
    output [7:0] seg_7,
    output [3:0] com
);

    wire [3:0] hour;
    wire [5:0] minute;
    watch uu_watch(.clk(clk), .rst(rst), .hour(hour), .minute(minute));
    
    wire [15:0] dec_value1, dec_value2;
    bin_to_dec bin2dec1(.bin({4'b0000, hour}), .bcd(dec_value1));
    bin_to_dec bin2dec2(.bin({2'b00, minute}), .bcd(dec_value2));
    
    FND_watch_cntr cntr0(.clk(clk), .rst(rst), .value1(dec_value1), .value2(dec_value2), .com(com), .seg_7(seg_7));
        
endmodule

 

3. 오류 및 디버깅

    1) hour과 minute의 분할 문제

         이전에 만들었던 counter는 count 하나의 값만 출력했기에,

         decilmal 변환과 decoder로 넘겨줄 때 문제가 발생하지 않았다.

         하지만 비트 수가 다른 hour와 minute은 조정이 필요했고

         다음과 같이 코드를 수정하였다.

           i) bin_to_dec module : 16bit → 8bit  로 수정

           ii) bin_to_dec module 2개를 instance하여 hour와 minute에 각각 연결

              일치하지 않는 bit 범위는 결합연산자를 활용하여 0으로 채워 줌.

           iii) wire 및 관련 입출력 변수 수정

 

    2) bin_to_dec 시, bcd overflow 문제

         overflow된 값은 삭제되고 hour, minute 연산 시 문제가 되지 않기에 overflow된 상태로 유지

         만약 문제가 발생했다면, bcd의 비트 범위를 조정해주면 해결이 가능하다.

 

4. 결과

빠른 결과 도출을 위해, count clock을 빠르게 변경하였음

 

5. 고찰

버튼 입력으로 동작하는 기능이 없어서 edge_detector, debouncing을 활용하지 못한 부분이 아쉬웠지만, 이전 게시글 작성 시 한 모듈에서 동기화 오류와 디바운싱을 해결하는 코드를 만들어냈기에 한 편으로는 만족스러운 프로젝트였다.

비전공자 맨땅 무지랭이로 시작했는데, 2주만에 혼자서 무언가를 만들어낼 수 있다는 것이 참 신기했고 좀 더 자신감을 얻을 수 있는 계기가 되었다.

최종 보스, One-chip project까지 열심히 달려보자!

 

 

 

728x90
반응형