1. 동기 리셋은 다른 입력처럼 에지에서 샘플링됩니다
레지스터는 시뮬레이션을 x로 시작하고, 무언가가 대입하기 전까지 x를 유지합니다. 리셋은
레지스터에 알려진 값을 주는 입력입니다. 이 과정의 리셋은 동기 리셋입니다. always @(posedge clock) 블록 안에서 검사하므로 리셋이 1인 상승 에지에서만 효과가 있고, 에지 사이에 리셋을
올려도 다음 에지까지는 아무 일도 일어나지 않습니다.
형식
always @(posedge clock) begin
if (reset) count <= 4'd0;
else count <= count + 4'd1;
endCounter.v가 바로 이 블록이며, 레지스터와
nonblocking 대입 장에서 추적을 한 줄씩 읽은 카운터입니다. 테스트벤치에서 reset은 처음
두 상승 에지 동안 1이고 시간 20의 하강 에지에서 내려갑니다.
// 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는 값을 유지
endnet·변수·조합
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이 무엇이든 리셋이 이깁니다.
reset | enable | 실행되는 분기 | 에지 뒤의 count |
|---|---|---|---|
| 1 | 무관 | if (reset) | 0 |
| 0 | 1 | else if (enable) | 현재 값 + 1 |
| 0 | 0 | 없음 | 현재 값 유지 |
확인 문제
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에지 직후 |
|---|---|---|---|
| 0 | 1 | 0 | |
| 1 | 0 | 1 | |
| 2 | 0 | 1 | |
| 3 | 0 | 0 | |
| 4 | 1 | 1 | |
| 5 | 0 | 1 |
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 |
|---|---|---|
| 1 | reset이 1 | enable과 count가 무엇이든 0 |
| 2 | enable이 1이고 count가 마지막 값 MAX | 0. 되돌아감 |
| 3 | enable이 1 | count + 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인 에지에서만 일어납니다.
// 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
endmoduleTb.v는 reset과 enable을 하강 에지에서만 바꾸고, 상승 에지마다 EDGE 줄을 하나씩
출력합니다. 한 줄에는 같은 레지스터의 값이 두 번 나옵니다.
prev는 에지 시점에 읽은count입니다.<=와 같은 시간 단계에서 갱신 전에 읽었으므로,if연쇄가 판단에 쓴 값입니다.count는$strobe가 출력합니다.$strobe는 시간 단계가 끝날 때까지, 곧<=가 레지스터를 갱신한 뒤까지 기다립니다. 그 에지가 만든 값이며, 다음 하강 에지에서 읽어도 같은 값이 보입니다.
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=5 | 1 | prev=x count=0. 첫 리셋 에지 전에는 레지스터에 대입한 적이 없습니다 |
t=15, t=25 | 3 | count는 prev + 1입니다 |
t=35 | 4 | prev=2 count=2. 유지입니다 |
t=45~t=65 | 3 | 3, 4, 5 |
t=75 | 2 | prev=5 count=0. 되돌아감입니다 |
t=95, t=105 | 1 | enable이 1일 때도 0일 때도 0입니다 |
t=115 | 3 | 0에서 다시 세어 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는 평소 동작의 명령 |