Verilog 할당 구문을 다시 정리해 본다.
일단 조합회로 절차 블록은 =, clock 기반 순차회로 블록은 <=를 사용한다. 연산자 하나만 보고 합성되는 회로가 결정되는 것은 아니다.
일단 결론
// 조합회로
always @* begin
y = (a | b) & (~c ^ d);
end
// 순차회로
always @(posedge clk) begin
q <= d;
end
한 always 블록 안에서 blocking과 non-blocking 할당을 섞지 않는 편이 좋다. AMD Vivado 합성 가이드도 두 방식을 섞으면 simulation 오류가 생길 수 있다고 설명한다.
continuous assignment
assign은 연속 할당이다. 우변이 바뀌면 좌변 값도 다시 계산된다.
wire y;
assign y = b & c;
Verilog 기준으로 assign의 좌변은 net type을 사용한다. 여기서는 wire로 선언했다.
blocking assignment
=는 문장이 실행되는 즉시 좌변을 갱신한다. 조합회로를 표현하는 always @* 블록에서 주로 사용한다.
always @* begin
temp = a & b;
y = temp | c;
end
아래 문장은 갱신된 temp를 사용한다. 그래서 블록 안의 문장 순서가 simulation 결과에 영향을 줄 수 있다.
조합회로 블록에서는 모든 경로에서 출력값을 할당해야 한다. 빠진 경로가 있으면 latch가 만들어질 수 있다.
non-blocking assignment
<=는 우변을 먼저 평가하고 좌변 갱신을 현재 simulation time step의 뒤쪽에 예약한다. clock edge에서 여러 register를 함께 갱신할 때 사용한다.
always @(posedge clk) begin
a <= b;
b <= a;
end
clock edge 직전의 a, b 값을 각각 읽은 뒤 두 값을 갱신한다. blocking 할당처럼 첫 문장에서 바뀐 a를 두 번째 문장이 바로 읽는 구조가 아니다.
위 그림은 실제 simulator waveform이 아니다. 값 평가와 갱신 순서를 설명하려고 만든 그림이다.
회로는 문맥으로 확인
=이면 무조건 조합회로, <=이면 무조건 flip-flop이라는 뜻은 아니다. sensitivity list, clock edge, 모든 분기의 할당 여부를 함께 봐야 한다.
// clock edge가 있으므로 순차회로를 의도한 블록
always @(posedge clk) begin
q <= d;
end
// 모든 입력 변화에 반응하고 모든 경로를 할당하는 조합회로
always @* begin
if (sel)
y = a;
else
y = b;
end
나는 조합 블록과 순차 블록을 나누고, 한 블록에서는 한 종류의 할당만 사용한다. 머 이 정도로 정해두면 다시 볼 일이 많이 줄어든다.
delay는 합성용으로 보지 않는다
assign #10 y = b & c;
#10 같은 delay는 simulation 모델에서는 의미가 있지만 Vivado 합성에서는 무시된다. 합성할 RTL의 실제 timing을 이 값으로 지정하면 안 된다.
확인하지 않은 부분
현재 이 저장소 환경에는 HDL simulator가 설치되어 있지 않아 예제를 직접 실행하지 않았다. 특정 simulator의 event queue 표시나 waveform 차이는 아직 확인하지 않았다.
공식 문서
- AMD Vivado Design Suite User Guide: Blocking and Non-Blocking Assignments
- AMD Vivado Design Suite User Guide: Procedural Assignments
- AMD Vivado Design Suite User Guide: Using Sequential Always Blocks
원문 기록
한 줄 요약
조합회로 절차 블록은 =, clock 기반 순차회로 블록은 <=로 나누고 실제 회로는 블록 전체 문맥으로 확인한다.
끝.