7-2. FSM 설계 예제: 시퀀스 감지기

1. 시퀀스 감지기 개요

유한 상태 기계 (Finite State Machine, FSM)는 다양한 디지털 시스템의 동작을 모델링하고 구현하는 데 매우 유용한 도구입니다. FSM은 상태(State), 입력(Input), 출력(Output)전이(Transition)로 구성됩니다. FSM은 입력에 따라 상태를 변경하고, 현재 상태에 따라 출력을 생성합니다. 이러한 특성 덕분에 FSM은 제어 시스템, 통신 프로토콜, 그리고 특히 시퀀스 감지와 같은 복잡한 동작을 설계하는 데 널리 사용됩니다.

시퀀스 감지기는 특정 입력 시퀀스를 감지하는 FSM의 한 유형입니다. 예를 들어, 디지털 통신 시스템에서 특정 비트 시퀀스를 감지하거나, 보안 시스템에서 비밀번호를 입력하는 것을 감지하는 데 사용될 수 있습니다. 본 글에서는 시퀀스 감지기의 개념과 설계 방법을 자세히 살펴보고, 이를 Verilog를 사용하여 구현하는 방법을 예제를 통해 알아보겠습니다.

2. 시퀀스 감지기 동작 원리

시퀀스 감지기는 입력 데이터 스트림에서 미리 정의된 시퀀스를 감지하는 회로입니다. 이는 입력 비트 스트림을 분석하여 감지하려는 시퀀스가 나타나는지 여부를 결정합니다. 감지된 경우, 특정 신호를 활성화하여 감지를 나타냅니다. 시퀀스 감지기는 일반적으로 두 가지 유형의 출력을 제공합니다.

  • 감지 신호: 시퀀스가 감지되었음을 나타내는 신호.
  • 리셋 신호 (선택 사항): 감지된 시퀀스 이후 감지기를 초기 상태로 되돌리는 신호.

시퀀스 감지기의 핵심은 상태전이입니다. 상태는 감지기가 현재 인식하고 있는 시퀀스의 상태를 나타냅니다. 전이는 입력에 따라 상태를 변경하는 규칙을 정의합니다. 이러한 상태와 전이의 조합을 통해 감지기는 입력 시퀀스를 추적하고, 원하는 시퀀스가 나타났을 때 감지 신호를 활성화할 수 있습니다.

1) 예시: "101" 시퀀스 감지

"101" 시퀀스를 감지하는 간단한 시퀀스 감지기를 생각해 봅시다. 이 감지기는 다음과 같은 상태를 가질 수 있습니다.

  • S0: 초기 상태. 어떤 비트도 감지되지 않음.
  • S1: "1"을 감지함.
  • S2: "10"을 감지함.
  • S3: "101"을 감지함 (감지 완료).

입력 비트에 따른 상태 전이는 다음과 같이 정의됩니다.

  • S0: 입력이 "1"이면 S1으로, "0"이면 S0으로 유지.
  • S1: 입력이 "0"이면 S2로, "1"이면 S1으로 유지.
  • S2: 입력이 "1"이면 S3로, "0"이면 S0으로.
  • S3: 감지 완료 (감지 신호 활성화). 다음 입력을 받으면 S1으로 이동하여 새로운 시퀀스 감지 시작.

3. 상태 다이어그램 및 Verilog 코드

시퀀스 감지기 설계를 위해서는 상태 다이어그램을 먼저 설계하는 것이 일반적입니다. 상태 다이어그램은 FSM의 동작을 시각적으로 표현하여 설계 프로세스를 용이하게 합니다. 상태 다이어그램을 바탕으로 Verilog 코드를 작성하여 하드웨어로 구현할 수 있습니다.

1) 상태 다이어그램

위에서 언급한 "101" 시퀀스 감지기의 상태 다이어그램은 다음과 같습니다.

상태 다이어그램 예시 뒤

위 다이어그램에서, 원은 상태를 나타내고, 화살표는 상태 전이를 나타냅니다. 각 화살표는 입력 비트(0 또는 1)에 따라 어떤 상태로 이동하는지를 나타냅니다. S3 상태에 도달하면 시퀀스가 감지되었음을 의미하며, 감지 신호 (예: detect)를 활성화합니다.

2) Verilog 코드

위의 상태 다이어그램을 바탕으로 Verilog 코드를 작성할 수 있습니다. 다음은 "101" 시퀀스 감지기의 Verilog 코드 예시입니다.

module sequence_detector (
  input  logic clk,
  input  logic rst,
  input  logic data_in,
  output logic detect
);

  // 상태 정의
  typedef enum logic [1:0] {
    S0, // 초기 상태
    S1, // "1" 감지
    S2, // "10" 감지
    S3  // "101" 감지 (시퀀스 감지)
  } state_type;

  state_type current_state, next_state;

  // 출력 초기화
  always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
      current_state <= S0;
      detect <= 1'b0;
    end else begin
      current_state <= next_state;
    end
  end

  // 상태 전이 로직
  always_comb begin
    next_state = current_state; // 기본값: 현재 상태 유지
    detect = 1'b0;            // 기본값: 감지 안 됨

    case (current_state)
      S0: begin
        if (data_in) next_state = S1;
      end
      S1: begin
        if (data_in) next_state = S1;
        else next_state = S2;
      end
      S2: begin
        if (data_in) begin
          next_state = S3;
        end else begin
          next_state = S0;
        end
      end
      S3: begin
        detect = 1'b1;
        next_state = S1; // 다음 시퀀스를 위해 S1으로
      end
    endcase
  end

endmodule

위 코드에서 current_state는 현재 상태를 저장하는 레지스터이며, next_state는 다음 상태를 계산하는 조합 논리입니다. always_ff 블록은 클럭의 상승 에지 또는 리셋 신호에 반응하여 상태를 업데이트합니다. always_comb 블록은 상태 전이 로직을 구현합니다. case 문을 사용하여 현재 상태와 입력에 따라 다음 상태를 결정하고, 감지 신호를 적절하게 설정합니다.

4. 코드 설명 및 동작 원리

1) 모듈 선언 및 포트 정의

module sequence_detector(...) 문은 모듈의 시작을 정의합니다. 모듈은 독립적인 하드웨어 블록으로, 입출력 포트를 통해 다른 모듈과 연결됩니다. 위 코드에서는 다음과 같은 입출력 포트를 정의합니다.

  • input logic clk: 클럭 입력. 모든 순차 로직을 동기화합니다.
  • input logic rst: 리셋 입력. 시스템을 초기 상태로 만듭니다.
  • input logic data_in: 데이터 입력. 감지할 비트 시퀀스를 입력받습니다.
  • output logic detect: 감지 출력. 시퀀스가 감지되면 활성화됩니다.

2) 상태 정의

typedef enum logic [1:0] {
    S0, // 초기 상태
    S1, // "1" 감지
    S2, // "10" 감지
    S3  // "101" 감지 (시퀀스 감지)
} state_type;

typedef enum 문은 FSM의 상태를 정의합니다. enum은 각 상태에 고유한 이름을 할당하여 코드를 더 읽기 쉽게 만듭니다. logic [1:0]은 각 상태를 2비트로 표현함을 의미합니다.

3) 상태 변수 및 출력 변수 선언

state_type current_state, next_state;는 현재 상태와 다음 상태를 저장하는 데 사용되는 변수를 선언합니다. detect는 감지 신호를 나타내는 출력 변수입니다.

4) 순차 로직 (상태 업데이트)

always_ff @(posedge clk or posedge rst) begin
    if (rst) begin
      current_state <= S0;
      detect <= 1'b0;
    end else begin
      current_state <= next_state;
    end
end

always_ff 블록은 플립플롭을 모델링합니다. 이 블록은 클럭의 상승 에지 (posedge clk) 또는 리셋 (posedge rst) 신호에 의해 트리거됩니다.

  • rst가 활성화되면 (rst <mark class="highlight"> 1), current_state는 초기 상태인 S0으로 초기화되고, detect는 0으로 초기화됩니다.
  • rst가 비활성화되면 (rst </mark> 0), current_statenext_state의 값으로 업데이트됩니다.

5) 조합 로직 (상태 전이 및 출력 결정)

always_comb begin
    next_state = current_state; // 기본값: 현재 상태 유지
    detect = 1'b0;            // 기본값: 감지 안 됨

    case (current_state)
      S0: begin
        if (data_in) next_state = S1;
      end
      S1: begin
        if (data_in) next_state = S1;
        else next_state = S2;
      end
      S2: begin
        if (data_in) begin
          next_state = S3;
        end else begin
          next_state = S0;
        end
      end
      S3: begin
        detect = 1'b1;
        next_state = S1; // 다음 시퀀스를 위해 S1으로
      end
    endcase
end

always_comb 블록은 조합 논리를 모델링합니다. 이 블록은 입력 (data_in, current_state)의 변화에 따라 즉시 실행됩니다.

  • next_statedetect는 초기값으로 각각 current_state와 0으로 설정됩니다. 이는 기본적으로 상태가 변경되지 않고 감지되지 않음을 의미합니다.
  • case 문은 현재 상태에 따라 다음 상태와 출력을 결정합니다. 예를 들어, 현재 상태가 S0이고 data_in이 1이면, next_stateS1이 됩니다. S3 상태에서는 detect가 1로 설정되어 시퀀스가 감지되었음을 나타냅니다. 또한, S3에서 다음 시퀀스 감지를 위해 next_stateS1으로 설정됩니다.

5. 시뮬레이션 및 테스트

Verilog 코드를 작성한 후에는 시뮬레이션을 통해 동작을 검증해야 합니다. 시뮬레이션은 코드의 정확성을 확인하고, 하드웨어에서 예상대로 작동하는지 확인하는 중요한 단계입니다.

1) 테스트벤치 작성

테스트벤치는 시뮬레이션을 위한 환경을 제공합니다. 여기에는 클럭 신호 생성, 리셋 신호 설정, 입력 데이터 제공, 그리고 출력 신호 확인 등이 포함됩니다. 다음은 "101" 시퀀스 감지기를 위한 간단한 테스트벤치 예시입니다.

module sequence_detector_tb;

  // 시뮬레이션 파라미터
  parameter CLK_PERIOD = 10; // 클럭 주기 (10ns)

  // 신호 선언
  logic clk;
  logic rst;
  logic data_in;
  logic detect;

  // DUT (Device Under Test) 인스턴스
  sequence_detector uut (
    .clk(clk),
    .rst(rst),
    .data_in(data_in),
    .detect(detect)
  );

  // 클럭 생성
  always # (CLK_PERIOD / 2) clk = ~clk;

  // 초기화 및 리셋
  initial begin
    clk = 0;
    rst = 1;
    # (CLK_PERIOD * 2) // 리셋 기간
    rst = 0;
  end

  // 입력 데이터 시퀀스 및 검증
  initial begin
    // 테스트 케이스 1: "101" 감지
    data_in = 1'b0; # CLK_PERIOD;
    data_in = 1'b1; # CLK_PERIOD;
    data_in = 1'b0; # CLK_PERIOD;
    data_in <mark class="highlight"><strong><u> 1'b1; # CLK_PERIOD; // detect </u></strong></mark> 1 (시퀀스 감지)
    $display("Test Case 1: detect = %b", detect);

    // 테스트 케이스 2: "1101" 감지
    data_in = 1'b1; # CLK_PERIOD;
    data_in = 1'b1; # CLK_PERIOD;
    data_in = 1'b0; # CLK_PERIOD;
    data_in <mark class="highlight"><strong><u> 1'b1; # CLK_PERIOD; // detect </u></strong></mark> 1 (시퀀스 감지)
    $display("Test Case 2: detect = %b", detect);

    // 테스트 케이스 3: "0000" (시퀀스 미감지)
    data_in = 1'b0; # CLK_PERIOD;
    data_in = 1'b0; # CLK_PERIOD;
    data_in = 1'b0; # CLK_PERIOD;
    data_in <mark class="highlight"><strong><u> 1'b0; # CLK_PERIOD; // detect </u></strong></mark> 0 (시퀀스 미감지)
    $display("Test Case 3: detect = %b", detect);

    $finish;
  end

endmodule

위 테스트벤치는 다음과 같은 기능을 수행합니다.

  • 클럭 신호 생성: always 블록을 사용하여 클럭 신호를 생성합니다.
  • 리셋 신호: initial 블록을 사용하여 리셋 신호를 설정합니다.
  • 입력 데이터 설정: initial 블록 내에서 data_in 신호를 설정하여 다양한 입력 시퀀스를 시뮬레이션합니다.
  • 출력 검증: $display를 사용하여 detect 신호의 값을 확인합니다.

2) 시뮬레이션 결과

테스트벤치를 실행하면 시뮬레이터는 입력 데이터에 따라 detect 신호의 변화를 보여줍니다. "101" 시퀀스가 입력되면 detect가 1로 설정되고, 그렇지 않은 경우에는 0으로 유지됩니다. 시뮬레이션 결과를 통해 코드의 정확성을 확인할 수 있습니다.

6. 고급 설계 고려 사항

1) 중첩 시퀀스 감지

더 복잡한 시퀀스 감지기를 설계할 수 있습니다. 예를 들어, "10110"과 같은 더 긴 시퀀스를 감지하거나, 중첩된 시퀀스를 감지할 수 있습니다. 중첩 시퀀스 감지의 경우, "101101"과 같은 시퀀스를 감지할 때, "101" 시퀀스가 감지된 후 "1101" 시퀀스를 감지하는 것과 같은 방식으로 설계할 수 있습니다. 이러한 경우, 더 많은 상태가 필요하고 상태 다이어그램이 복잡해집니다.

2) 오버랩 시퀀스 감지

오버랩 시퀀스 감지는 시퀀스가 겹치는 경우에도 감지하는 것을 의미합니다. 예를 들어, "101101" 시퀀스를 감지할 때, "101"과 "101"이 겹쳐서 감지될 수 있습니다. 이러한 경우, FSM은 현재 감지된 시퀀스를 추적하고, 새로운 시퀀스가 시작될 때 올바르게 전이하도록 설계되어야 합니다.

3) 효율적인 상태 코딩

상태 코딩은 FSM의 상태를 이진수로 표현하는 방법입니다. 상태 코딩 방법에는 이진 코딩, 그레이 코드, 원-핫 코딩 등 다양한 방법이 있습니다. 원-핫 코딩은 각 상태를 하나의 비트로 표현하는 방식이며, 상태 전이 로직을 간단하게 만들 수 있지만, 더 많은 플립플롭을 필요로 합니다. 상태 코딩 방법은 하드웨어 리소스 사용량과 성능에 영향을 미치므로, 설계 요구 사항에 따라 적절한 방법을 선택해야 합니다.

7. 결론

시퀀스 감지기는 FSM 설계를 통해 구현할 수 있는 강력한 도구입니다. 본 글에서는 시퀀스 감지기의 기본적인 개념, 설계 방법, 그리고 Verilog를 이용한 구현 예시를 살펴보았습니다. 상태 다이어그램을 사용하여 FSM의 동작을 시각적으로 표현하고, 이를 바탕으로 Verilog 코드를 작성하는 방법을 익혔습니다. 또한, 시뮬레이션을 통해 코드의 정확성을 검증하는 방법을 배웠습니다.

FSM 설계는 디지털 시스템 설계를 위한 핵심 기술이며, 시퀀스 감지기는 FSM의 기본적인 응용 중 하나입니다. 이러한 예제를 통해 FSM 설계 능력을 향상시키고, 더 복잡한 디지털 시스템 설계를 위한 기반을 다질 수 있습니다. 더 나아가, 중첩 시퀀스 감지, 오버랩 시퀀스 감지, 효율적인 상태 코딩과 같은 고급 설계 기법을 학습하여 FSM 설계 능력을 더욱 발전시킬 수 있습니다.

비슷한 글 추천

Comments (0)

No comments yet. Be the first to comment!