Verilog 기초, Blocking과 Non-blocking에 대해서
Verilog의 세계: Blocking vs. Non-blocking Assignments, 그리고 그 너머
디지털 회로 설계의 세계에 오신 것을 환영합니다! Verilog HDL을 사용하여 하드웨어를 설계할 때, Blocking Assignment (=)와 Non-blocking Assignment (<=)는 마치 건반 위의 '도'와 '레'처럼 필수적이면서도 명확히 구분되는 역할을 합니다. 이 둘의 차이를 제대로 이해하는 것은 시뮬레이션 결과와 실제 구현될 하드웨어 간의 불일치를 막고, 원하는 기능을 정확하게 구현하는 데 핵심적입니다.
오늘은 이 두 가지 할당 방식의 차이점을 파헤치고, 이것이 어떻게 래치(Latch)와 플립-플롭(Flip-Flop)이라는 기본적인 메모리 요소와 연결되는지, 나아가 물리적인 수준까지 깊이 있게 탐구해보겠습니다.
1. Blocking Assignment (=)
"지금 당장 업데이트!" — Blocking assignment는 말 그대로 실행 순서를 막습니다. Verilog 코드 내에서 이 할당자를 만나면, 오른편(RHS)의 표현식이 계산되고 그 결과가 즉시 왼편(LHS) 변수에 할당됩니다. 이 할당이 완료된 후에야 다음 줄의 코드가 실행될 수 있습니다.
주요 특징:
- 순차적 실행: 코드에 작성된 순서대로 실행되며, 이전 할당이 완료될 때까지 다음 할당은 기다립니다.
- 즉각적인 값 반영: 할당된 새로운 값은 같은 프로시저 블록 내의 다음 문장에서 즉시 사용할 수 있습니다.
- 주요 용도: 주로 조합 논리(Combinational Logic)를 모델링할 때 사용됩니다.
always @(*)블록이나always_comb블록 안에서 변수나 신호에 값을 할당할 때 유용합니다.
예시:
always @(*) begin
// 'a'는 즉시 업데이트되고,
a = in1 & in2;
// 'b'는 업데이트된 'a'의 값을 사용하여 계산됩니다.
b = a | in3;
end
이 예시에서 a가 먼저 계산되어 즉시 업데이트되면, b는 그 새로운 a 값을 즉시 읽어와서 계산됩니다.
2. Non-blocking Assignment (<=)
"잠시 기다렸다가, 다 같이 업데이트!" — Non-blocking assignment는 하드웨어의 동시성을 모델링하기 위해 설계되었습니다. always @(posedge clk)와 같은 클럭 동기 블록에서 주로 사용되며, 모든 Non-blocking assignment는 현재 시뮬레이션 시간 단계의 끝에서 일괄적으로 업데이트됩니다.
주요 특징:
- 동시적 업데이트: 블록 내 모든 Non-blocking assignment의 RHS는 현재 시점의 변수 값(업데이트 전 값)으로 먼저 평가됩니다.
- 지연된 할당: 실제 LHS 변수 값의 업데이트는 해당 시뮬레이션 시간 단계가 끝날 때까지 연기됩니다.
- 주요 용도: 순차 논리(Sequential Logic), 즉 레지스터나 플립-플롭과 같은 메모리 요소를 모델링하는 데 필수적입니다. 클럭의 특정 엣지(상승/하강)에 맞춰 상태가 변하도록 설계할 때 사용됩니다.
- 레이스 컨디션 방지: 값의 동시 업데이트 덕분에, 여러 신호 간의 복잡한 상호 의존성이 있는 순차 논리에서 발생할 수 있는 레이스 컨디션(Race Condition)을 효과적으로 방지합니다.
예시:
always @(posedge clk) begin
q1 <= d_in; // d_in 값은 이 클럭 엣지에서 q1에 할당될 예정입니다.
q2 <= q1; // q1의 값은 이전 클럭 엣지의 값이 사용됩니다.
q3 <= q2; // q2의 값은 이전 클럭 엣지의 값이 사용됩니다.
end
이 코드는 전형적인 시프트 레지스터를 모델링합니다. 각 클럭 엣지마다 d_in의 값이 q1으로, 이전 q1 값이 q2로, 이전 q2 값이 q3로 전달됩니다. 만약 여기서 Blocking assignment (=)를 사용했다면, q1이 업데이트된 직후 q2가 그 새 q1 값을 받아버리는 등 예상치 못한 결과가 발생할 수 있습니다.
3. Latch vs. Flip-Flop: 물리적 연결고리
Blocking assignment와 Non-blocking assignment의 차이는 우리가 Verilog 코드로 무엇을 '묘사'하고, 그 묘사가 실제 물리적인 하드웨어로 어떻게 '구현'되는지에 대한 중요한 단서를 제공합니다.
래치 (Latch)
- 동작 방식: 래치는 레벨 민감(Level-Sensitive) 소자입니다. 즉, 'Enable' 신호가 특정 레벨(높거나 낮음)로 활성화되어 있는 동안에는 입력 값의 변화가 출력에 즉시 반영됩니다. Enable 신호가 비활성화되면, 래치는 마지막으로 가지고 있던 값을 계속 유지합니다.
- 물리적 구현: 기본적인 래치는 크로스 커플링된 NOR 또는 NAND 게이트 쌍으로 구성될 수 있습니다. Enable 신호에 따라 게이트의 출력이 고정되거나 입력 신호를 따라가도록 설계됩니다.
- Verilog에서의 추론:
- 주로
always @(*)블록에서 모든 가능한 입력 조건에 대해 항상 출력을 할당해주지 않으면 래치가 추론될 수 있습니다. 예를 들어if-else문에서 특정 조건에서만 출력이 할당되고, 나머지 조건에서는 할당되지 않는 경우, 합성 도구는 해당 출력이 이전 값을 '유지'해야 한다고 판단하여 래치를 삽입합니다. - 이는 의도치 않은 래치(unintended latch)를 만들 수 있으며, 이는 타이밍 문제나 예상치 못한 동작의 원인이 됩니다. 따라서 조합 논리에서는 모든 출력에 대해 항상 값을 할당하는 것이 중요합니다.
- SystemVerilog의
always_latch키워드를 사용하면 의도적으로 래치를 모델링할 수 있습니다.
- 주로
플립-플롭 (Flip-Flop)
- 동작 방식: 플립-플롭은 엣지 트리거(Edge-Triggered) 소자입니다. 즉, 클럭 신호의 특정 엣지(상승 엣지
posedge또는 하강 엣지negedge)에서만 입력 값을 받아들이고, 그 값을 저장했다가 다음 엣지까지 유지합니다. - 물리적 구현: 플립-플롭은 일반적으로 두 개의 래치를 직렬로 연결한 마스터-슬레이브(Master-Slave) 구조로 구현됩니다. 마스터 래치가 클럭의 한 엣지에서 값을 캡처하고, 슬레이브 래치가 클럭의 반대 엣지에서 마스터의 값을 출력으로 내보냅니다. 이 구조 덕분에 플립-플롭은 클럭 엣지 사이에는 투명하지 않고(non-transparent), 오직 엣지에서만 상태가 변합니다.
- Verilog에서의 추론:
always @(posedge clk)또는always @(negedge clk)와 같이 클럭 엣지를 명시하는always블록에 Non-blocking assignment (<=)를 사용하면 플립-플롭이 정확하게 모델링됩니다.- SystemVerilog의
always_ff키워드는 플립-플롭 모델링을 위한 명확한 방법으로, 합성 도구에 의해 플립-플롭으로 인식되도록 도와줍니다.
Blocking/Non-blocking과 Latch/Flip-Flop의 관계
- Blocking (
=) +always @(*)→ 조합 논리: Blocking assignment와always @(*)(모든 입력 변화에 반응)의 조합은 결과적으로 조합 논리 회로(AND, OR 게이트, 멀티플렉서 등)를 생성합니다. 여기서 지연은 순전히 게이트의 전달 지연(propagation delay)으로만 발생합니다. - Non-blocking (
<=) +always @(posedge clk)→ 플립-플롭: Non-blocking assignment와 클럭 엣지 민감 블록의 조합은 표준적인 플립-플롭 회로를 생성합니다. 값은 클럭 엣지에서 저장되며, 시간은 클럭 주기라는 명확한 단위로 나뉩니다. - 의도치 않은 래치:
always @(*)블록에서 일부 출력에 대한 할당을 누락하면, 래치가 생성되어 회로에 의도치 않은 메모리 요소가 삽입될 수 있습니다. 래치는 클럭 엣지가 아닌 레벨에 반응하기 때문에, 특히 동기 설계에서 예측하기 어려운 타이밍 문제를 일으킬 수 있습니다.
댓글
댓글 쓰기