본문으로 건너뛰기
code0.HDL

리셋·enable·우선순위

레지스터에 동기 리셋과 enable을 주고, 리셋이 이기도록 if/else 연쇄의 순서를 정하고, 폭이 다하는 지점이 아니라 요구사항이 정한 값에서 카운터가 되돌아가게 합니다.

이 장에서 배울 내용

  • 클록 블록 안에 동기 리셋을 쓰고 어느 에지에서 효과가 나는지 말할 수 있습니다
  • enable이 0일 때 아무것도 대입하지 않는 분기로 enable을 추가하고, 빠진 분기가 여기서는 유지를, 조합 블록에서는 래치를 뜻하는 이유를 설명할 수 있습니다
  • 리셋이 enable을 이기도록 if/else 연쇄의 순서를 정하고, 리셋을 조용히 무시하는 중첩한 판을 추적할 수 있습니다
  • 요구사항이 정한 값에서 카운터가 되돌아가게 하고, 비교값이 하나 어긋났을 때의 추적을 예측할 수 있습니다
  • 카운터를 리셋·되돌림·증가·유지의 전이 표 하나로 적고, 실행으로 행마다 확인하고, 에지에서 읽은 값과 $strobe가 에지 뒤에 출력하는 값이 다른 이유를 설명할 수 있습니다
이 장에서 배우는 개념
  • 리셋·enable 우선순위
먼저 알아야 할 개념

1. 동기 리셋은 다른 입력처럼 에지에서 샘플링됩니다

레지스터는 시뮬레이션을 x로 시작하고, 무언가가 대입하기 전까지 x를 유지합니다. 리셋은 레지스터에 알려진 값을 주는 입력입니다. 이 과정의 리셋은 동기 리셋입니다. always @(posedge clock) 블록 안에서 검사하므로 리셋이 1인 상승 에지에서만 효과가 있고, 에지 사이에 리셋을 올려도 다음 에지까지는 아무 일도 일어나지 않습니다.

형식

always @(posedge clock) begin
  if (reset) count <= 4'd0;
  else       count <= count + 4'd1;
end

Counter.v가 바로 이 블록이며, 레지스터와 nonblocking 대입 장에서 추적을 한 줄씩 읽은 카운터입니다. 테스트벤치에서 reset은 처음 두 상승 에지 동안 1이고 시간 20의 하강 에지에서 내려갑니다.

Counter.v
// active-high 동기 리셋을 가진 4비트 카운터.
module counter (
  input            clock,
  input            reset,
  output reg [3:0] count
);
  always @(posedge clock) begin
    if (reset) count <= 4'd0;
    else       count <= count + 4'd1;
  end
endmodule

이 개념 실습하기

다음 단계로 짧은 Verilog 문제에 이 개념을 적용해 보세요.

확인 문제

reset은 시간 20의 하강 에지에서 내려갑니다. 어느 상승 에지 뒤에 count가 처음으로 1이 됩니까?

감지 목록에 적는 비동기 리셋은 에지를 기다리지 않고 동작합니다. 이 과정의 예제도 여기의 실습 문제도 비동기 리셋을 쓰지 않습니다.

2. enable은 갱신과 유지를 가릅니다

enable 입력은 이 에지에서 레지스터가 새 값을 받을지 현재 값을 유지할지 정합니다. 클록 블록에서 유지는 어떤 문장도 레지스터에 대입하지 않을 때 일어나는 일이므로, 마지막 else가 없는 else if (enable)이 곧 enable입니다.

always @(posedge clock) begin
  if (reset)       count <= 4'd0;
  else if (enable) count <= count + 4'd1;
  // else 없음: count는 값을 유지
end

net·변수·조합 always 장에서는 조합 블록의 빠진 분기가 결함이었습니다. 저장이 없어야 할 회로에 저장을 강요했기 때문입니다. 클록 블록에서는 레지스터 자체가 저장이므로, 아무것도 대입하지 않는 경로는 "유지"를 뜻합니다. 아래 모델을 조작해 보세요. clear와 enable은 에지마다 샘플링되고 q는 16을 주기로 셉니다.

개념 실험 · counter4

컴파일한 설계가 아니라 브라우저 안의 회로 모델입니다. 실제 도구 체인은 예제에서 실행됩니다.

입력

출력

q
0

아직 에지가 없습니다. 입력을 정한 뒤 클록 에지를 인가하세요.

reset과 clear는 다른 입력입니다. 위 모델의 입력 이름은 clear이고, 요구사항에서 설계하기 장의 실습 문제 clear-enable-counter도 같은 이름을 씁니다. 코드만 보면 동기식 clear는 이 장의 reset과 똑같이 연쇄의 첫 분기에서 0을 저장합니다. 다른 것은 역할입니다. reset은 설계를 시작 상태로 돌리는 입력으로, 시스템이 켜지거나 다시 시작할 때 올리며 보통 모든 레지스터가 함께 씁니다. clear는 enable처럼 한 블록이 평소에 받는 명령입니다. 카운터를 쓰는 쪽이 처음부터 다시 세고 싶을 때마다 올립니다. 둘을 모두 갖는 설계라면 연쇄의 첫 자리는 reset, 두 번째 자리는 clear입니다. 요구사항이 입력 이름을 정해 두었다면 이 장의 이름이 아니라 요구사항의 이름과 우선순위를 따르세요.

3. 우선순위는 if/else 연쇄의 순서입니다

같은 에지에 두 입력이 모두 1이면 연쇄가 어느 쪽이 이기는지 정합니다. 참인 첫 조건이 그 에지를 차지하고, 뒤의 조건은 검사되지도 않습니다. 리셋을 맨 앞에 두면 enable이 무엇이든 리셋이 이깁니다.

resetenable실행되는 분기에지 뒤의 count
1무관if (reset)0
01else if (enable)현재 값 + 1
00없음현재 값 유지

확인 문제

count가 4입니다. 다음 상승 에지에서 reset이 1, enable이 0입니다. 에지 뒤에 count는 무엇입니까?

답

중첩한 판은 이 규칙을 깨면서도 깨끗하게 컴파일됩니다.

if (enable) begin
  if (reset) count <= 4'd0;
  else       count <= count + 4'd1;
end

확인 문제

중첩한 판에서 어느 에지에 reset이 1, enable이 0입니다. 무슨 일이 일어납니까?

답

올바른 우선순위를 가진 카운터의 표를 채워 보세요. 두 입력은 각 사이클을 끝내는 에지에서 샘플링된 값이고, q는 그 에지 뒤의 값을 적습니다.

예측한 뒤 비교 · counter4

비교하기 전에 모든 칸을 채우세요. 빈칸도 불일치로 처리합니다.

상태표
사이클clear에지에서 샘플링한 입력enable에지에서 샘플링한 입력q에지 직후
010
101
201
300
411
501

4. 폭이 다하는 곳이 아니라 요구사항이 정한 곳에서 되돌아갑니다

4비트 레지스터는 15에서 0으로 저절로 되돌아갑니다. 15 + 1은 16이고 16의 하위 네 비트는 0000입니다. 10진 카운터는 9 다음에 되돌아가야 하고, 설계는 현재 값이 9인 에지에서 그 결정을 내려야 합니다. 결정은 분기 하나이며, 리셋 뒤 증가 앞에 놓습니다.

if (reset)              count <= 4'd0;
else if (count == 4'd9) count <= 4'd0;
else                    count <= count + 4'd1;

피하려는 첫 값이 아니라 보려는 마지막 값과 비교하세요.

이제 카운터 전체를 상태 전이 계약 하나로 적을 수 있습니다. 상승 에지마다 조건이 참인 첫 행이 다음 값을 정합니다.

우선순위상승 에지에서 샘플링한 조건에지 뒤의 count
1reset이 1enable과 count가 무엇이든 0
2enable이 1이고 count가 마지막 값 MAX0. 되돌아감
3enable이 1count + 1
4위의 어느 것도 아님count 유지

10진 카운터는 MAX가 9이고 enable이 항상 1인 경우입니다. if/else 연쇄는 이 표를 위에서 아래로 옮겨 적은 것입니다.

확인 문제

되돌림 분기를 else if (count == 4'd10) count <= 0;으로 썼습니다. 추적에는 무엇이 보입니까?

답

5. 계약의 모든 행을 한 번의 실행으로 확인하기

WrapCounter.v는 3비트 레지스터에서 MAX를 5로 둔 계약을 그대로 따르므로, 0부터 5까지 세고 0으로 돌아갑니다. 되돌림은 enable 분기 안쪽의 if이므로 enable이 1인 에지에서만 일어납니다.

WrapCounter.v
// 0부터 5까지 세고 0으로 돌아간다. 우선순위는 리셋, enable, 유지 순이다.
module wrap_counter (
  input            clock,
  input            reset,
  input            enable,
  output reg [2:0] count
);
  always @(posedge clock) begin
    if (reset)
      count <= 3'd0;
    else if (enable) begin
      if (count == 3'd5) count <= 3'd0;
      else               count <= count + 3'd1;
    end
    // 마지막 else가 없다. enable이 0이면 레지스터는 값을 유지한다.
  end
endmodule

Tb.v는 reset과 enable을 하강 에지에서만 바꾸고, 상승 에지마다 EDGE 줄을 하나씩 출력합니다. 한 줄에는 같은 레지스터의 값이 두 번 나옵니다.

  • prev는 에지 시점에 읽은 count입니다. <=와 같은 시간 단계에서 갱신 전에 읽었으므로, if 연쇄가 판단에 쓴 값입니다.
  • count는 $strobe가 출력합니다. $strobe는 시간 단계가 끝날 때까지, 곧 <=가 레지스터를 갱신한 뒤까지 기다립니다. 그 에지가 만든 값이며, 다음 하강 에지에서 읽어도 같은 값이 보입니다.
Tb.v
module tb;
  reg        clock = 0;
  reg        reset = 1, enable = 0;
  reg  [2:0] prev;
  wire [2:0] count;

  wrap_counter dut (.clock(clock), .reset(reset), .enable(enable), .count(count));

  always #5 clock = ~clock;

  // 상승 에지마다 한 줄. 에지에서 읽은 값과 에지가 지난 뒤의 값을 함께 출력한다.
  always @(posedge clock) begin
    prev = count;
    $strobe("EDGE t=%0t reset=%b enable=%b prev=%0d count=%0d", $time, reset, enable, prev, count);
  end

  // 두 입력은 샘플링되기 반 주기 전인 하강 에지에서 바꾼다.
  task apply(input r, input e);
    begin
      @(negedge clock);
      reset = r; enable = e;
    end
  endtask

  initial begin
    // 두 번 세고, 한 번 유지하고, 5까지 센다.
    apply(0, 1); apply(0, 1);
    apply(0, 0);
    apply(0, 1); apply(0, 1); apply(0, 1);
    // 0으로 돌아가 한 번 더 센 뒤, enable 중에 리셋하고 한 번 더 리셋한다.
    apply(0, 1); apply(0, 1);
    apply(1, 1);
    apply(1, 0);
    apply(0, 1);
    @(posedge clock) #1 $finish;
  end
endmodule

에지에 도착하는 입력은 다음 순서입니다. reset만, enable 두 번, 둘 다 0, enable 다섯 번, enable과 함께 reset, 다시 reset만, enable. 계약 표로 한 행씩 시험하는 줄의 count를 예측해 보세요.

확인 문제

EDGE t=35 reset=0 enable=0 prev=2 count=?

확인 문제

EDGE t=75 reset=0 enable=1 prev=5 count=?

확인 문제

EDGE t=95 reset=1 enable=1 prev=1 count=?

확인 문제

reset은 95와 105의 에지 동안 1입니다. 115에서는 reset이 0, enable이 1입니다. 115의 에지 뒤에 count는 무엇입니까?

결과 확인. 실행을 누르고 열두 줄을 계약 표와 비교하세요.

줄계약의 행확인할 내용
t=51prev=x count=0. 첫 리셋 에지 전에는 레지스터에 대입한 적이 없습니다
t=15, t=253count는 prev + 1입니다
t=354prev=2 count=2. 유지입니다
t=45~t=6533, 4, 5
t=752prev=5 count=0. 되돌아감입니다
t=95, t=1051enable이 1일 때도 0일 때도 0입니다
t=11530에서 다시 세어 1입니다

증가나 되돌림이 일어난 줄에서는 prev와 count가 다릅니다. 값을 유지하거나, 이미 0인 레지스터를 다시 0으로 리셋한 줄에서는 같습니다. 연쇄는 이전 값을 읽고, 새 값은 그 시간 단계의 nonblocking 갱신이 끝난 뒤에야 생깁니다.

하나만 바꿔 보세요. Tb.v의 $strobe를 $display로 바꾸고 다시 실행합니다. 먼저 예측해 보세요.

확인 문제

$strobe 대신 $display를 쓰면 EDGE 줄마다 count 열에는 무엇이 나옵니까?

답

이 동작의 바탕인 한 시간 단계 안의 active 영역과 nonblocking 갱신 영역은 레지스터와 nonblocking 대입 장에 있습니다. 다음으로 넘어가기 전에 $strobe를 되돌려 두세요.

6. 실행한 뒤 카운터를 고치세요

위의 Counter.v에서 실행을 누르고 앞 장의 규칙으로 추적을 읽으세요. EDGE 줄은 에지 이전 값, AFTER 줄은 에지 뒤의 값입니다. reset이 1인 에지가 몇 번인지, 세기가 어디서 시작되는지 헤아려 보세요.

점검 과제는 실습 문제 decade-counter입니다. 위의 평범한 카운터는 리셋은 올바르지만 9 다음에 10, 11, 12로 계속 셉니다. 동기 리셋을 유지하면서 리셋과 증가 사이에 되돌림 분기를 넣으세요. 테스트벤치는 상승 에지에서 출력하므로, 줄을 예측과 비교할 때 각 줄이 그 에지 이전 값을 보인다는 점을 기억하세요.

3절의 중첩한 if는 테스트벤치 기법 장의 실습 문제 reset-priority로 다시 나옵니다. 그 카운터는 enable이 1인 동안은 올바르게 세지만, enable이 0인 동안 올린 리셋이 값을 바꾸지 못합니다. 그 문제에 이르면 중첩한 if를 reset=1 enable=0으로 추적하고, 리셋이 맨 앞에 오는 하나의 연쇄로 다시 쓰세요. 같은 장의 누산기는 clear 입력으로 같은 종류의 우선순위를 정하고 모델과 비교해 검사합니다.

7. 정리

요소뜻
always @(posedge clock) 안의 if (reset)동기 리셋. 다음 상승 에지에서 동작
마지막 else가 없는 else if (enable) …enable이면 갱신, 아니면 유지
연쇄의 순서참인 첫 조건이 에지를 차지
중첩한 if (enable) if (reset)enable이 0인 동안 리셋 무시
else if (count == 4'd9)요구사항이 정한 값에서 되돌림
계약 표리셋, 되돌림, 증가, 유지 순. 참인 첫 행이 각 에지를 결정
에지에서 읽기와 $strobe에지 전의 값과 에지 뒤의 값
reset과 clear코드 모양은 같음. reset은 시스템을 시작 상태로, clear는 평소 동작의 명령

확인 지점

실습 문제를 제출해 채점을 받습니다

채점은 서버의 비공개 테스트로 진행되며, 결과는 이 브라우저에만 기록됩니다.