티스토리 뷰

 

지난 포스팅에서 wire와 reg의 올바른 사용에 대해서 살펴보았다. 해당 개념을 설명하면서, 자연스럽게 연속적 할당(continuous assignment)과 절차적 할당(procedural assignment)에 대해서 언급했었다. Verilog에서 값을 할당할 때 사용하는 대입 방법은 아래와 같이 2가지가 존재한다.

 

1. "="를 사용하는 Blocking 방법

2. "<="를 사용하는 Non Blocking 방법

 

조금 더 정확히 말하면 이러한 할당 방법은 Verilog simulator에서 사용자가 값을 할당하기 위해 선언한 부분을 어떻게 해석할 것인가에 대해 명시해주는 것이다. 이를 어떻게 기술하느냐에 따라서 simulator의 결과가 달라질 뿐만 아니라, 합성의 결과가 simulator와 다르게 구현되는 치명적인 사고를 초래할 수도 있다. 그러므로 이 부분에 대해서는 조금 깊이 있게 다룰 예정이다. 이 내용을 포스팅하기 위해 S/W적인 지식도 조금 필요했기에 추가적인 공부를 많이 해야 했었는데, 이전까지 잘 몰랐던 Verilog simulation의 감춰진 이야기(?)를 이해할 수 있는 좋은 기회가 되었다.

 

 

 

사실 RTL 엔지니어가 Verilog simlator의 동작 원리에 대해 속속들이 전부 다 알아야 할 필요까지는 없다고 생각한다. 그러나 설계자가 스케줄링의 우선순위에 대해 정확하게 알지 못하면 의도치 않은 실수를 하게 될 수 있다. 기본적으로 Verilog simulator는 time stamp를 기반으로 스케줄을 처리한다. 그러므로 동일한 시점에 어떤 스케줄을 먼저 처리할 것인가에 대한 알고리즘이 이미 정의되어 있다. 이는 deterministic(결정론적)인 부분과 non-deterministic(비결정론적)인 부분으로 구분되어 있는데, 우선순위가 명확하게 구분된 항목들이 deterministic이며 어느 것이 우선되어도 상관이 없는 경우 non-deterministic이다. non-deterministic인 부분은 자칫하면 race condition을 야기하므로, 의도에 맞게 코딩을 해야 한다. 

 

 

Verilog Simulator의 스케줄링

 

위의 그림에서 schedule이란, 거창한 것이 아니고 동일한 타이밍에 정의된 신호들의 방향/할당 등을 의미한다. 예를 들어 동 타이밍에 always문 내부에 2줄의 신호처리 할당을 코드로 작성했다면 이는 각각 schedule 0, schedule 1로 볼 수 있다. 이번 포스팅에서 다루고자 했던 내용인 Blocking assignment와 Non blocking assignment도 무엇을 사용하냐에 따라서 처리의 우선순위가 달라지기 때문에 항상 주의해서 사용해야 한다는 것을 알 수 있다.   

 

동일 time-stamp에서 스케줄링

 

위의 그림에서 동일한 time-stamp에서 어떠한 스케줄들이 처리되는지 알 수 있다. Active event queue에서는 어느 순서든 상관없이 동작할 수 있다. 그러므로 어떠한 결과를 초래할지는 아무도 알 수 없다. 대표적으로 race condition을 야기하는 코드는 아래의 첫 번째 예제와 같다. Simulator가 순서를 명확히 할 수 있도록 아래의 두 번째 예제와 같이 코딩하는 것이 낫다. (물론 always문으로 순차 회로를 구현할 때는 Non-blocking을 사용하는 것이 가장 좋다)

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Read-Write or Write-Read race condition example
 
always @(posedge clock)
    x = 2;
 
always @(posedge clock)
    y = x;
 
// Better coding style example
 
always @(posedge clock) begin
    x = 2;
    y = x;
end
 
 
 

 

그럼 이제 본격적으로 Blocking과 Non-blocking에 대해서 알아보도록 하자.

 

 

 

사나이의 만화라 하면, 요즘에야 원나블이니 뭐니하지만 라떼만해도 드래곤볼과 슬램덩크가 모든 남학생의 1순위 픽이었다. 그중에서도 "뜨거운 코트를 가르며"라는 가사로 시작하는 슬램덩크의 오프닝송을 듣노라면 당장에라도 농구공 하나 들고 근린공원으로 뛰쳐나가게 만들곤 했다. 이 만화에는 주인공의 농구 스승이자, 여주인공의 오빠로 등장하는 채치수라는 캐릭터가 있다. 그의 주특기가 바로 "파리채 블로킹"인데, 그 누구도 그 앞에서 슛을 마음 편히 쏠 수 없었다. 백이면 백 공을 블로킹하는 장면들은 말 그대로 압권이었다. 

 

채치수의 트레이드마크 "파리채 블로킹"

갑자기 왜 뜬금없이 만화 이야기로 빠지는 건지라고 의아해할 지도 모르겠다. 사실 여기서 설명하고 있는 Blocking의 개념을 농구에서의 blocking 개념으로 조금 더 쉽게 이해할 수 있도록 설명하고 싶었다. 아무래도 처음 Verilog를 공부하는 사람들에게는 모든 개념이 다 쉽지 않기 때문에 알기 쉬운 내용으로 빗대어 이야기를 풀어나가고자 한다.

 

 

[Blocking 예시] 같은 time-stamp에서는 선언된 순서대로

 

위의 그림처럼 Blocking으로 선언을 하게 되면, 동일 time에서는 순차적으로 실행이 된다. 즉, 위 예제의 결과는 a=1, b=1로 나타나게 된다. H/W는 a라는 wire가 Vdd(1)에 tie 되어 있으면서 바로 b라는 wire로 연결된 것처럼 구현될 것으로 예상된다. Blocking으로 선언된 구문은 먼저 처리되기 전에는 다음 구문이 처리되는 것을 허용하지 않는다. 마치 채치수처럼 모두 blocking 해버리고, 본인이 실행된 후에야 다음 구문을 처리하게 해주는 자비 없는 녀석이다.

 

 

Blocking 구문 : "넌, 일단 대기하고 있어! 내가 먼저야. 슛을 쏘기만 해봐, 모조리 블로킹이야." 

 

 

[Non-blocking 예시] 같은 time-stamp에서는 순서 관계없이 동시에

이에 반해 Non-blocking은 자비가 넘치는 성격이라, 동일 time에서는 동시에 실행이 된다. 그러므로 clock에 동기화되어, 동시에 처리되어야 하는 순차 회로에 아주 적격인 구문이라고 볼 수 있다. 위의 예제에 결과는 a=1, b=x이 될 것이다. 이는 두 구문이 동시에 처리되기 때문에, a 값이 결정되지 않기 때문이다. 여기서는 b의 값이 보장되지 않는 알 수 없는 logic이 만들어지게 된다. 

 

조금 더 자세하게 알아보면, Non-blocking 구문은 처리되는 절차가 2-step으로 진행된다.

1st Step : RHS(Right Hand Side) 값이 Blocking 구문들과 같은 Event queue에서 평가된다. 위의 예제에서는 b=a의 우측 값이 평가되는 것이므로 a가 어떤 값인지 미리 확인하는 것이다. 물론, a는 아직 어떤 값인지 알 수 없으므로 x로 평가될 것이다. 

2nd Step : LHS(Left Hand Side) 값이 업데이트된다. Blocking 구문들이 다 처리된 후에 다음 Event queue에서 b=x로 처리된다. 그러므로 Blocking 구문과 함께 처리되는 $display task로는 Non-blocking 구문들의 값을 확인할 수 없다. 이는 아직 RHS 값이 평가되고 있기 때문이다. Non-blocking 구문의 값을 모니터링하기 위해서는 반드시 $monitor, $strobe 등의 task를 사용해야 한다.

 

 

Non-blocking 구문 : "자, 동시에 슛을 쏘자. 순서는 상관없어!" 

 

 

앞에서 살펴본 예제를 통해 blocking 구문과 Non-blocking 구문의 개념에 대해 간략히 알아보았다. 물론 아직은 생소할 수 있다. 이러한 개념을 바탕으로, 아래 코딩 가이드라인을 잘 지킨다면 커다란 문제 없이 디지털 회로를 설계할 수 있을 것이다.

 

 

 

#1. Sequential logic은 Non-blocking assignment를 사용한다.

#2. Latch design 시에도 Non-blocking assignment를 사용한다.

#3. Combination logic을 always문으로 구현 시, Blocking assignment를 사용한다.

#4. Sequential/Combination logic을 하나의 always문으로 구현 시, Non-blocking assignment를 사용한다.

#5. 같은 always문 안에 Blocking/Non-blocking assignment를 혼용하지 않는다.

#6. 하나보다 많은 always문을 사용하여 동일한 variable을 여러 곳에서 처리하지 않는다.

#7. Non-blocking assignment를 사용한 variable을 확인할 시에는 $strobe를 사용한다.

 

 

 

지난번 포스팅 이후로, 6개월이 넘는 시간이 훌쩍 지나갔다. 야심 차게 시작했던 블로그이지만 바쁜 일상 속에서 일주일에 하나의 포스팅이라는 계획은 항상 천덕꾸러기처럼 날 힘들게 했다. 내가 조금 더 잘 알았더라면, 더욱더 쉽게 글을 쓸 수 있었을까 하는 의문이 항상 날 따라다녔다. 일과 공부, 그리고 결혼 생활 가운데 제대로 중심을 잡기란 쉽지 않았다. 그럼에도 불구하고 다시금 마음을 잡아본다. 2020년에 나에게 조금 더 힘을 내자고 용기를 북돋워본다. 분명히 나는 성장하고 있을 것이고, 블로그에 정리된 내용이 쌓여갈수록, 나의 부족한 설계 실력도 더욱 강건해지리라 믿는다.

 

댓글
공지사항