Menu

Verilog Always 블록: 조합 논리와 순차 논리

always 블록이 어떻게 동작하는지, 조합 always @(*)와 clocked always @(posedge clk)의 차이, 그리고 각각이 어떤 하드웨어를 만드는지 결정하는 규칙.

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

Behavioral Verilog의 일꾼

assign은 단일 식의 조합 논리를 기술합니다. if/else, case, 또는 메모리가 필요해지는 순간 always로 손이 갑니다. always 블록은 특정 신호가 변할 때마다 다시 실행되는 procedural 코드 조각입니다. 재실행을 트리거하는 신호들이 sensitivity list 입니다.

가장 자주 보게 될 always의 두 형태.

  1. always @(*) - 블록에서 읽는 어떤 신호든 변하면 재실행. 조합 논리를 만듭니다.
  2. always @(posedge clk) - clk의 rising edge에서만 재실행. clocked 순차 논리(플립플롭)를 만듭니다.

다른 형태(@(a or b), @(negedge clk), @(posedge clk or negedge reset_n))도 있지만, 작성하게 될 거의 모든 synthesizable 블록은 위 두 가지입니다.

조합 always @(*)

세 가지 주목할 점.

  • outreg. always 안에서 대입되는 모든 것은 reg여야 합니다. 키워드가 "이건 플립플롭이다"라는 뜻은 아니며, 여기서는 단지 "procedural 블록에서 쓰이는 신호"라는 뜻일 뿐입니다.
  • always @(*). *는 "내가 읽는 무엇이든 변하면 깨어나라"는 뜻입니다. 시뮬레이터가 자동으로 sensitivity list를 알아냅니다. always @(sel)로 수동으로 적을 수도 있지만, @(*)가 더 안전합니다 - 신호를 빠뜨리는 것이 고전적인 버그 원인이기 때문입니다.
  • 클럭 없음. 이 블록은 조합 논리를 기술합니다. synthesizer는 outsel로부터 직접 계산하는 로직 - 플립플롭이나 클럭 핀이 필요 없는 - 을 만들어 냅니다.

default case는 문법적으로는 선택이지만 정신적으로는 그렇지 않습니다. 빠뜨리면 명시되지 않은 입력 값이 out의 이전 값을 유지하게 만들어 - 의도치 않은 latch 로 synthesize됩니다. 항상 default를 포함하세요.

순차 always @(posedge clk)

조합 버전과의 핵심 차이.

  • always @(posedge clk). 블록은 clk의 rising edge에서만 재실행됩니다. edge 사이에는 아무 일도 일어나지 않습니다.
  • Non-blocking assignment <=. clocked 블록 안에서는 이게 맞는 연산자입니다. "count가 time step 끝에서 새 값을 갖도록 스케줄"이라는 뜻이고, 정확히 플립플롭이 동작하는 방식입니다. 이유와 대안은 Blocking vs Non-blocking에 있습니다.
  • default 불필요. if가 두 분기(reset, not-reset)를 모두 커버합니다. latch 위험 없음.

synthesizer는 이 모양 - clocked sensitivity, non-blocking assignment - 을 보고 4비트 register(플립플롭 넷)와 count + 1을 계산하는 조합 논리, 그리고 reset과 increment 사이를 선택하는 mux를 만들어 냅니다.

Synthesis 구분

같은 모듈 소스가 always 블록의 모양에 따라 완전히 다른 두 하드웨어를 기술할 수 있습니다.

블록하드웨어
always @(*) y = expr;순수 조합 논리. 메모리 없음.
always @(posedge clk) y <= expr;플립플롭. 클럭 사이클마다 expr를 포착.
always @(*) if (en) y = expr;Latch - 보통 버그. "else" case가 옛 값을 유지.
always @(posedge clk) if (en) y <= expr;enable이 있는 플립플롭. en이 high일 때만 포착.

세 번째가 latch trap입니다. latch는 입력이 assert되지 않을 때 출력을 유지하는 transparent 메모리 셀입니다 - 특정 설계에서는 유용하지만 우연히 만들어진 경우 거의 항상 버그입니다. 대부분의 synthesis 도구는 의도하지 않은 latch를 inference할 때 시끄럽게 경고합니다. 그 경고를 오류로 다루세요.

Sensitivity List 변형

덜 흔한 sensitivity list도 가끔 봅니다.

  • always @(a or b or c) - 명시적 목록. Verilog-2001은 , 구분자를 추가: always @(a, b, c). 둘 다 됨.
  • always @(posedge clk or negedge reset_n) - 비동기 reset. 블록이 rising clock edge 또는 falling reset edge에서 실행됨. reset이 다음 클럭을 기다리지 않고 즉시 효력을 발해야 할 때 쓰입니다.
  • always @(negedge clk) - falling-edge clocking. 드뭄; rising 대신 falling에서 포착하는 "negative-edge-triggered" 플립플롭을 쓰는 일부 설계에서 사용.

새 설계라면 조합에는 always @(*), 순차에는 always @(posedge clk)를 선호하세요. 비동기 reset은 설계가 정말로 필요할 때만.

두 블록은 두 하드웨어

같은 모듈의 여러 always 블록은 독립적입니다 - 각각이 자기 하드웨어가 됩니다.

clocked 블록은 플립플롭 register를 만듭니다. 조합 블록은 XOR 게이트를 만듭니다. 둘은 나란히 살고, 서로를 모릅니다. 두 출력은 완전히 다른 일정으로 변합니다.

always 블록이 할 수 없는 것

매혹적으로 보이지만 허용되지 않는 것들.

  • wire에 대입: 타겟은 reg여야 합니다. 컴파일러가 강제.
  • 같은 reg에 서로 다른 두 always 블록에서 대입: 시뮬레이션에서 정의되지 않은 동작이 되고 synthesize되지 않습니다. 신호 하나에 드라이버 하나.
  • 같은 조합 블록에서 같은 신호를 읽고 쓰는데 그것이 피드백 루프를 만드는 경우: always @(*) x = x + 1;은 시뮬레이터가 해결할 수 없는 zero-delay 루프입니다.

앞 두 개는 컴파일러가 잡고, 세 번째는 시뮬레이션 시점에 hang으로만 나타나기도 합니다.

다음에 볼 내용

다음 문서 - Initial Block - 은 always의 형제를 다룹니다: 시뮬레이션 시작 시 정확히 한 번 실행되는 블록. testbench의 일꾼입니다. 그다음에는 clocked 블록이 의도한 대로 동작할지를 결정하는 blocking vs non-blocking assignment 규칙입니다.

자주 묻는 질문

Verilog의 always 블록이란 무엇인가요?

alwayssensitivity list 안의 신호가 변할 때마다 다시 실행되는 procedural 블록을 도입합니다. 두 가지 형태가 있습니다: always @(*)는 조합 논리를 만들고(어떤 입력이든 변할 때마다 재실행), always @(posedge clk)는 순차 논리를 만듭니다(clk의 매 rising edge에서 재실행). always 블록의 본문에는 if, case, for, procedural 대입이 들어갈 수 있습니다.

always @(*)와 always @(posedge clk)의 차이는?

always @(*)는 블록에서 읽는 어떤 신호에도 sensitive합니다. 메모리 없는 조합 논리를 만듭니다. always @(posedge clk)는 오직 clk의 rising edge에만 sensitive합니다. 클럭 사이클마다 한 번씩 상태를 포착하는 플립플롭을 만듭니다. 첫 번째에는 클럭도 register도 없고, 두 번째에는 둘 다 있습니다.

Verilog의 sensitivity list란?

@ 뒤의 신호 목록으로, always 블록이 언제 재실행되는지를 결정합니다. @(*)는 '블록에서 읽는 모든 신호'의 단축 표기입니다. @(posedge clk)clk의 rising edge에서만 실행됩니다. @(posedge clk or negedge reset_n)은 두 이벤트 중 하나에서 실행됩니다 - 비동기 reset에 쓰입니다. sensitivity list를 잘못 두는 것은 시뮬레이션과 synthesis 불일치의 가장 흔한 원인입니다.

always 블록 안에서 wire에 대입할 수 있나요?

아니요. always 블록은 reg(또는 SystemVerilog의 logic)에만 대입할 수 있습니다. 컴파일러가 강제합니다. wire가 procedural 로직의 출력이어야 한다면, 중간 reg를 선언해 always 안에서 구동하고, 바깥에서 wire를 reg로부터 assign하거나 - 그냥 wire를 reg로 바꾸세요.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기