6-3. 순차 논리 회로 모델링 - 카운터

1. 카운터의 개념과 중요성

순차 논리 회로 설계에서 카운터 (Counter)는 디지털 시스템의 기본적인 구성 요소 중 하나입니다. 카운터는 특정 클럭 펄스 또는 외부 이벤트에 따라 미리 정의된 시퀀스대로 상태를 변화시키는 회로를 말합니다. 쉽게 비유하자면, 카운터는 "숫자를 세는 기계"와 같습니다. 매 클럭마다 숫자가 증가하거나 감소하며, 특정 값에 도달하면 다시 처음부터 시작하거나 다른 동작을 수행합니다. 이러한 단순한 기능은 디지털 시계, 주파수 분할기, 메모리 주소 생성기, 이벤트 횟수 측정 등 다양한 분야에서 핵심적인 역할을 수행합니다.

카운터는 단순히 숫자를 세는 것 이상의 의미를 가집니다. 카운터의 출력은 시스템의 상태를 나타내며, 이를 통해 시스템의 제어, 타이밍, 데이터 처리 등 여러 기능을 구현할 수 있습니다. 예를 들어, 디지털 시계에서 초, 분, 시를 표현하는 것은 카운터의 전형적인 활용 사례입니다. 카운터는 또한 특정 주파수를 가진 클럭 신호를 분할하여 원하는 주파수를 생성하는 데 사용될 수 있습니다. 이러한 주파수 분할 (Frequency Division) 기능은 통신 시스템, 데이터 전송 등에서 매우 중요합니다.

2. 카운터의 종류

카운터는 동작 방식, 카운팅 방향, 모듈러스 등에 따라 여러 종류로 분류됩니다. 주요 카운터 유형을 살펴보고, 각 유형의 특징을 이해해 보겠습니다.

1) 업 카운터 (Up Counter)

업 카운터는 클럭 펄스가 들어올 때마다 미리 정해진 시퀀스에 따라 값을 증가시키는 카운터입니다. 가장 기본적인 형태의 카운터이며, 다음과 같은 특징을 가집니다.

  • 카운팅 방향: 증가 (0, 1, 2, 3, ...)
  • 모듈러스: 카운터가 순환하는 값의 개수 (예: 0~7까지 세는 카운터는 모듈러스 8)
  • 응용 분야: 주파수 분할, 타이밍 생성, 디지털 시계 등

2) 다운 카운터 (Down Counter)

다운 카운터는 클럭 펄스에 따라 값을 감소시키는 카운터입니다. 업 카운터와 반대되는 동작을 수행하며, 다음과 같은 특징을 가집니다.

  • 카운팅 방향: 감소 (7, 6, 5, 4, ...)
  • 모듈러스: 카운터가 순환하는 값의 개수
  • 응용 분야: 타이머, 이벤트 카운트다운, 디지털 시스템 제어 등

3) 모드-n 카운터 (Modulo-n Counter)

모드-n 카운터는 0부터 n-1까지의 값을 순환하며 카운트하는 카운터입니다. 여기서 n은 카운터의 모듈러스 (Modulus)라고 부르며, 카운터의 최대값을 결정합니다. 모드-n 카운터는 원하는 모듈러스를 갖도록 설계될 수 있으며, 다양한 시스템에 유연하게 적용될 수 있습니다.

  • 카운팅 방향: 업 또는 다운
  • 모듈러스: n (0 ~ n-1)
  • 응용 분야: 주파수 분할, 시스템 타이밍, 메모리 주소 생성 등

4) 링 카운터 (Ring Counter)

링 카운터는 시프트 레지스터 (Shift Register)를 사용하여 구현되는 카운터입니다. 각 플립플롭의 출력은 다음 플립플롭의 입력으로 연결되어 링 형태를 형성합니다. 특정 비트가 1로 설정된 상태에서 클럭 펄스마다 오른쪽으로 시프트 되면서 순환하는 형태를 보입니다.

  • 카운팅 방향: 단방향 순환
  • 모듈러스: 플립플롭의 개수와 같음
  • 응용 분야: 간단한 상태 머신, 디코딩, 타이밍 제어 등

5) 존슨 카운터 (Johnson Counter)

존슨 카운터는 링 카운터의 변형으로, 마지막 플립플롭의 반전된 출력값을 첫 번째 플립플롭의 입력으로 연결합니다. 이는 링 카운터보다 더 많은 상태를 생성할 수 있으며, 디코딩 회로를 단순화하는 데 도움이 됩니다.

  • 카운팅 방향: 단방향 순환
  • 모듈러스: 2n (n은 플립플롭의 개수)
  • 응용 분야: 디코딩, 상태 머신, 간단한 제어 시스템 등

3. Verilog를 이용한 카운터 모델링

Verilog는 하드웨어 기술 언어로서, 다양한 종류의 카운터를 모델링하고 시뮬레이션할 수 있는 강력한 기능을 제공합니다. Verilog를 사용하여 카운터를 모델링하는 방법을 자세히 살펴보겠습니다.

1) 업 카운터 모델링

가장 기본적인 형태의 업 카운터를 모델링하는 방법을 살펴보겠습니다. 4비트 업 카운터를 예시로 들어 설명합니다.

module up_counter (
  input  logic clk,
  input  logic rst,
  output logic [3:0] count
);

  always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
      count <= 4'b0000;  // Reset 시 초기화
    end else begin
      count <= count + 1; // 클럭 엣지마다 카운트 증가
    end
  end

endmodule

위 코드에서 always_ff 블록은 플립플롭을 모델링합니다. posedge clk or posedge rst는 클럭의 상승 에지 또는 리셋 신호의 상승 에지에서 블록이 실행됨을 의미합니다. rst 신호가 활성화되면 count는 0으로 초기화되고, 그렇지 않으면 클럭 펄스마다 1씩 증가합니다. 4비트이므로 count는 0부터 15까지 세며, 15에서 다음 클럭 펄스에 0으로 다시 돌아갑니다 (오버플로우).

2) 다운 카운터 모델링

다운 카운터는 업 카운터와 유사하지만, 값을 감소시키는 방식으로 동작합니다.

module down_counter (
  input  logic clk,
  input  logic rst,
  output logic [3:0] count
);

  always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
      count <= 4'b1111;  // Reset 시 초기화 (최대값)
    end else begin
      count <= count - 1; // 클럭 엣지마다 카운트 감소
    end
  end

endmodule

다운 카운터에서는 리셋 신호가 활성화될 때 count를 초기 최대값 (15)으로 설정하고, 클럭 펄스마다 1씩 감소시킵니다. 언더플로우가 발생하면 최대값 (15)으로 다시 돌아갑니다.

3) 모드-n 카운터 모델링

모드-n 카운터는 특정 값에 도달하면 다시 0으로 돌아가도록 설계됩니다. 모듈러스 값을 설정하여 원하는 카운팅 범위를 정의할 수 있습니다.

module mod_n_counter (
  input  logic clk,
  input  logic rst,
  input  logic [3:0] modulus, // 모듈러스 값 입력
  output logic [3:0] count
);

  always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
      count <= 4'b0000;
    end else begin
      if (count == modulus - 1) begin
        count <= 4'b0000; // 모듈러스 값에 도달하면 0으로 리셋
      end else begin
        count <= count + 1;
      end
    end
  end

endmodule

위 예제에서는 modulus라는 입력 포트를 통해 모듈러스 값을 설정합니다. count 값이 modulus - 1과 같으면 count를 0으로 리셋하여 순환 카운팅을 구현합니다.

모드-n 카운터 설명 뒤

4) 링 카운터 모델링

링 카운터는 시프트 레지스터를 사용하여 구현됩니다.

module ring_counter (
  input  logic clk,
  input  logic rst,
  output logic [3:0] q
);

  always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
      q <= 4'b0001; // 초기값 설정
    end else begin
      q <= {q[0], q[3:1]}; // 오른쪽으로 시프트
    end
  end

endmodule

링 카운터는 q를 초기값 (예: 4'b0001)으로 설정하고, 클럭 펄스마다 오른쪽으로 시프트합니다. 마지막 비트(q[0])는 첫 번째 비트로 다시 연결되어 순환합니다.

5) 존슨 카운터 모델링

존슨 카운터는 링 카운터와 유사하지만, 마지막 플립플롭의 반전된 출력을 첫 번째 플립플롭의 입력으로 사용합니다.

module johnson_counter (
  input  logic clk,
  input  logic rst,
  output logic [3:0] q
);

  logic q_temp;

  always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
      q <= 4'b0000;
    end else begin
      q_temp <= ~q[3]; // 마지막 비트 반전
      q <= {q_temp, q[3:1]}; // 시프트
    end
  end

endmodule

존슨 카운터는 마지막 비트(q[3])를 반전한 후 시프트합니다. 이러한 동작은 링 카운터보다 더 많은 상태를 생성하게 합니다.

4. 카운터 설계 시 고려 사항

카운터를 설계할 때는 여러 가지 사항을 고려해야 합니다.

1) 클럭 도메인

카운터는 클럭 신호에 의존하므로, 클럭 도메인에 대한 고려가 필요합니다. 특히, 여러 클럭 도메인 간의 인터페이스를 설계할 때는 클럭 도메인 교차 (Clock Domain Crossing) 문제를 해결해야 합니다.

2) 리셋 신호

카운터는 리셋 신호를 통해 초기화될 수 있어야 합니다. 리셋 신호의 동기화 여부, 리셋 방식 (동기, 비동기) 등을 신중하게 결정해야 합니다. 비동기 리셋은 간단하지만, 글리치(glitch)가 발생할 수 있으므로, 동기 리셋을 사용하는 것이 더 안전합니다.

3) 타이밍

카운터의 동작은 타이밍에 의해 결정됩니다. 플립플롭의 설정 시간(setup time)과 홀드 시간(hold time)을 만족하도록 설계를 해야 하며, 시뮬레이션을 통해 타이밍 문제를 확인해야 합니다.

4) 글리치 (Glitch)

카운터의 출력에서 일시적인 펄스 또는 글리치가 발생할 수 있습니다. 특히, 디코딩 로직을 사용하는 경우 글리치가 발생할 가능성이 높습니다. 이러한 글리치는 시스템의 오동작을 유발할 수 있으므로, 필터링이나 다른 기술을 사용하여 제거해야 합니다.

5) 전력 소비

카운터의 전력 소비는 설계의 효율성을 평가하는 중요한 요소입니다. 클럭 게이팅(Clock Gating)과 같은 기술을 사용하여 불필요한 클럭 신호의 스위칭을 줄여 전력 소비를 최적화할 수 있습니다.

5. 실용적인 카운터 설계 예제

실제 시스템에서 사용되는 몇 가지 카운터 설계 예제를 통해 카운터의 활용 방법을 살펴보겠습니다.

1) 1초 타이머

1초 타이머는 1초 간격을 측정하는 카운터입니다.

module one_second_timer (
  input  logic clk,          // 클럭 입력 (예: 50MHz)
  input  logic rst,          // 리셋 입력
  output logic tick           // 1초마다 발생하는 펄스
);

  parameter CLK_FREQ = 50_000_000; // 클럭 주파수 (Hz)
  parameter COUNT_MAX = CLK_FREQ - 1; // 카운트 최대값

  logic [31:0] counter;

  always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
      counter <= 32'd0;
      tick <= 1'b0;
    end else begin
      if (counter == COUNT_MAX) begin
        counter <= 32'd0; // 카운터 리셋
        tick <= 1'b1;     // 1초 틱 발생
      end else begin
        counter <= counter + 1;
        tick <= 1'b0;
      end
    end
  end

endmodule

이 예제에서는 입력 클럭 주파수를 사용하여 카운터의 최대값을 계산합니다. 카운터가 최대값에 도달하면 tick 신호를 1로 설정하여 1초 간격을 나타냅니다.

2) 주파수 분할기

주파수 분할기는 입력 클럭의 주파수를 분할하여 다른 주파수를 생성하는 회로입니다.

module frequency_divider (
  input  logic clk_in,    // 입력 클럭
  input  logic rst,        // 리셋
  input  logic [3:0] divisor, // 분주비 (1 ~ 16)
  output logic clk_out       // 분주된 클럭
);

  logic [3:0] count;
  logic clk_temp;

  always_ff @(posedge clk_in or posedge rst) begin
    if (rst) begin
      count <= 4'b0000;
      clk_temp <= 1'b0;
    end else begin
      if (count == divisor - 1) begin
        count <= 4'b0000;
        clk_temp <= ~clk_temp; // 토글
      end else begin
        count <= count + 1;
      end
    end
  end

  assign clk_out = clk_temp;

endmodule

이 예제에서는 divisor 입력값을 사용하여 분주비를 설정합니다. 카운터가 분주비에 도달할 때마다 clk_temp 신호를 토글하여 분주된 클럭을 생성합니다.

주파수 분할기 설명 뒤

6. 결론

카운터는 디지털 시스템의 기본 빌딩 블록이며, 다양한 응용 분야에서 핵심적인 역할을 합니다. 본 문서에서는 카운터의 종류, Verilog를 사용한 모델링 방법, 설계 시 고려 사항, 그리고 실용적인 예제를 살펴보았습니다. 카운터에 대한 이해는 디지털 시스템 설계 능력을 향상시키는 데 필수적이며, 다양한 프로젝트에서 유연하게 활용할 수 있습니다. 카운터 설계는 복잡한 디지털 시스템을 구축하기 위한 중요한 첫걸음이며, 숙련된 엔지니어는 요구 사항에 맞는 카운터를 설계하고 구현할 수 있어야 합니다. 이러한 지식을 바탕으로, 독자 여러분은 실제 설계 과제에 카운터를 효과적으로 적용하고, 더 나아가 복잡한 디지털 시스템을 구축하는 데 필요한 기반을 다질 수 있을 것입니다.

비슷한 글 추천

Comments (0)

No comments yet. Be the first to comment!