Menu

Verilog Blocking vs Non-Blocking Assignment: = 와 <= 언제 쓸까

초보 Verilog에서 가장 헷갈리는 주제. always 블록 안에서 =<=가 실제로 무엇을 의미하는지, 그리고 대부분의 race condition을 막아 주는 규칙.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

두 연산자

alwaysinitial 블록 안에서 Verilog에는 두 가지 대입 연산자가 있습니다.

  • =blocking assignment. 다음 문장으로 넘어가기 전에 LHS를 지금 갱신.
  • <=non-blocking assignment. RHS를 지금 평가하고, 현재 time step 끝에 LHS가 갱신되도록 스케줄.

procedural 블록 밖(assign)에서는 =만 존재합니다 - assign a <= b는 syntax error입니다. procedural 블록 안에서는 둘 다 합법이고, 올바른 쪽을 선택하는 것이 초보 Verilog에서 가장 중요한 결정입니다.

Blocking이 하는 일

Blocking은 소프트웨어 언어에서 기대하는 그대로입니다: 한 줄씩, 위에서 아래로. N번 문장이 N+1번 시작 전에 완료됩니다.

순수하게 순차적입니다. 각 문장이 그 위 문장들의 효과를 봅니다. 소프트웨어 직관과 일치합니다.

Non-Blocking이 하는 일

Non-blocking은 하드웨어 모양입니다. 모든 RHS가 time step 시작 시의 값 으로 평가됩니다. 모든 LHS가 time step 끝에 갱신됩니다. 소스의 문장 순서는 신호 간 의존을 바꾸지 않습니다.

이 스니펫은 한 단계에서 a → b → c → a의 3-way 회전을 구현합니다. blocking이라면 셋 중 하나를 덮어쓰지 않으려고 temp 변수가 필요했을 겁니다. non-blocking에서는 모든 RHS가 step 이전 값을 읽기 때문에 swap이 atomic하게 일어납니다.

이게 정확히 세 플립플롭이 클럭 edge에서 동작하는 방식입니다: 의존성과 상관없이 모두 같은 순간에 입력을 포착합니다.

여러분을 구해 줄 규칙

clocked 블록에서는 <=. 조합 블록에서는 =.

그게 전부입니다. 외우세요. 생각 없이 작성하세요. 대부분의 race condition, 시뮬레이션/synthesis 불일치, "내 환경에서는 되는데 synthesis에서는 안 됨" 버그가 이 규칙 위반에서 옵니다.

규칙에는 하드웨어적 이유가 있습니다: clocked 블록은 동시에 입력을 sampling하는 플립플롭을 모델링합니다. 조합 블록은 가능한 한 빨리 전파되는 로직을 모델링합니다. 대입 연산자 의미가 그 동작과 일치합니다.

<=는 source register의 현재 값을 읽어 destination register가 time step 끝에 그 값을 갖도록 스케줄합니다. 순 효과는 정확히 하드웨어 shift register가 하는 일입니다: 모든 플립플롭이 이웃의 값을 같은 클럭 edge에서 race 없이 포착.

이제 blocking assignment였다면 어떻게 됐을지 보세요.

// 잘못 - 이건 shift register가 아닙니다!
always @(posedge clk) begin
    out[3] = out[2];   // out[3]가 out[2]가 됨
    out[2] = out[1];   // out[2]가 out[1]이 됨, 위에서 방금 설정한 값
    out[1] = out[0];
    out[0] = in;
end

각 문장이 다음 문장이 읽기 전에 source를 덮어씁니다. 한 클럭 사이클에서 inout[3]까지 쭉 전파됩니다. 위 줄이 갓 쓴 값을 다음 줄이 보기 때문입니다. 실제 하드웨어(non-blocking 의미를 사용)의 동작은 시뮬레이터가 보여 준 것과 완전히 다를 겁니다.

조합 블록: blocking이 맞음

always @(*)에서는 blocking이 옳습니다. 플립플롭이 없고, 동시 포착 규칙을 강제할 일도 없으며, 중간 변수가 유용합니다.

sum이 먼저 계산되고, 그 다음 result가 방금 계산된 값을 사용합니다. 조합 논리는 단일 하드웨어로 평탄화됩니다: result = ~(a + b). 클럭이 없으므로 플립플롭이 나타나지 않습니다.

여기서 <=를 썼다면 시뮬레이터가 여전히 sumresult가 그것에 대해 평가되기 전에 갱신했을 것이지만(두 갱신 모두 step 끝에 일어나므로), 순서가 미묘하게 다르고 많은 synthesis 도구가 불평합니다. 섞지 마세요; 블록 타입에 맞는 연산자를 고르세요.

가장 아픈 실수

여기 있습니다: blocking assignment를 쓴 clocked 블록.

// BUG: 일어나길 기다리는 race condition
always @(posedge clk) begin
    a = b;
    b = c;
    c = a;
end

시뮬레이션에서는 시뮬레이터가 a를 먼저, 그다음 b, 그다음 c로 줘서 어떤 값 세트를 만들 수 있습니다. 하드웨어는 다른 값 세트를 만들 겁니다 - 실제 플립플롭은 동시에 포착하기 때문입니다. 둘은 조용히 갈라지고, 여러분은 하루를 버그 찾는 데 보내게 됩니다. clocked 블록에서는 <=를 쓰세요.

왜 두 연산자가 존재하나

Verilog 설계자들이 대입 의미 하나만 고를 수도 있었지만 그러지 않았습니다 - 언어가 두 가지 별개의 하드웨어 동작을 모델링해야 했기 때문입니다.

  • 조합 논리: 신호가 연속적으로 전파되고, 의존성이 의미를 가지며, "이 게이트가 무엇을 계산하는가"가 말이 됩니다.
  • 순차 논리: 클럭 edge가 일어나면 모든 플립플롭이 동시에 포착하고, 플립플롭 입력과 출력 사이의 의존성이 분리됩니다.

Blocking은 첫 번째용. Non-blocking은 두 번째용. 연산자가 의미를 고르고 나머지는 시뮬레이터가 합니다.

다음에 볼 내용

이제 procedural 블록을 올바르게 작성할 규칙이 생겼습니다. 다음 장은 개별 블록에서 한 단계 올라가 그 안에 들어가는 흐름 제어 구문을 다룹니다: if/else, case, for 루프. blocking vs non-blocking 규칙은 그 안에서도 그대로 적용됩니다.

자주 묻는 질문

Verilog의 blocking과 non-blocking assignment 차이는?

Blocking(=)은 다음 문장이 실행되기 전에 타겟을 즉시 갱신합니다. 순차적 실행을 모델링합니다. Non-blocking(<=)은 갱신을 현재 time step 끝으로 스케줄합니다 - 모든 non-blocking RHS가 모든 신호의 값으로 평가된 뒤, 모든 LHS가 한 번에 함께 갱신됩니다. Non-blocking이 플립플롭의 실제 동작입니다.

Verilog에서 = 와 <= 중 무엇을 써야 하나요?

규칙: clocked always @(posedge clk) 블록에서는 <=, 조합 always @(*) 블록에서는 =. 그 단일 규칙이 혼합 대입이 만들어 내는 race condition 전체 부류를 막아 줍니다. testbench initial 블록 안에서는 =가 평범한 선택이고 <=는 거의 등장하지 않습니다.

왜 Verilog에 non-blocking assignment가 필요한가요?

하드웨어 플립플롭은 모두 클럭 edge에서 동시에 입력을 포착하기 때문입니다. clocked 코드에서 =(blocking)을 쓰면 파일의 문장 순서가 어느 신호가 다른 신호의 새 값을 보게 될지를 바꿔 버려 - 시뮬레이션과 실제 하드웨어 사이의 race condition이 됩니다. <=는 모든 RHS를 먼저 평가하고 그 다음 모든 갱신을 함으로써 하드웨어 동작과 일치합니다.

같은 Verilog always 블록에서 = 와 <= 를 섞으면?

race condition을 얻게 됩니다. 혼합은 시뮬레이터 내부(이벤트 스케줄링 순서)에 따라 동작이 달라지는 하드웨어를 만들어 내며, 더 나쁘게는 시뮬레이션이 synthesis가 만드는 것과 일치하지 않을 수 있습니다. 대부분의 lint 도구가 이를 오류로 표시합니다. 해법은 블록의 역할에 따라 한쪽에 commit하는 것입니다 - clocked는 non-blocking, 조합은 blocking.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기