강의

[기본] 타이머와 카운터에 관하여

토리장인 2024. 1. 7. 17:30

번 글은 (링크)"그려보는 베릴로그"의 (링크) "#프로젝트" 강의 관련 자료 입니다.


 

타이머/카운터 로직 설계

타이머와 카운터는 어떻게 활용하느냐 정도의 차이가 있을 뿐, 기본적으로는 동일한 로직입니다. 타이머 또는 카운터라는 이름은 해당 로직의 활용 목적에 따라 붙여집니다. 시간을 재는 목적으로 활용한다면 타이머, 어떤 신호의 입력 횟수를 세는 목적으로 활용한다면 카운터라고 부릅니다. 엄밀히 따지자면 그렇다는 말이지, 실제로는 이러한 명칭 구분에 그다지 의미를 두지 않고 섞어 쓰는 경우도 흔합니다.

 

타이머와 카운터의 차이는 ‘수를 세는 대상이 무엇인가’에 있습니다. 타이머는 동작하는 동안의 클럭 수를 셉니다. 즉, 별도의 입력 포트가 필요 없습니다. 반면에 카운터는 (클럭이 아닌) 특정 신호가 몇 번 발생했는지를 카운트하기 때문에 그에 대한 입력 포트가 필요합니다. 이번 글은 타이머로서 중점을 두고 설명을 하겠습니다.

 

타이머 로직의 기본 동작 (Block diagram)

타이머는 앞서 설명한 대로, 수를 세는 대상에 대한 입력 포트가 존재하지 않습니다. 하지만 시간을 재기 위해서는 이 로직을 선택적으로 구동할 수 있어야 합니다. 아래의 다이어그램과 같이, 해당 로직을 작동하거나 멈추게 하는 제어 신호를 입력으로 받습니다.

그림 1. 타이머 로직의 블록 다이어그램

 

타이머 로직의 기본 동작 (Timing diagram)

아래의 타이밍 다이어그램은, 우리가 설계할 로직이 시간에 따라 어떻게 동작해야 하는지를 표현한 것입니다. 파형(waveform)으로 그릴 수도 있겠지만 여기서는 간단히 테이블 형식으로 표현하였습니다.

 

맨 왼쪽에는 신호 이름이 적혀있고 오른쪽에 해당 신호의 값이 시간 순으로 적혀 있습니다. 디지털 로직에서는 클럭 신호를 기준으로 모든 동작이 이루어지기 때문에, 클럭 신호를 맨 위에 적어주는 것이 일반적입니다. 특정 시점을 기준으로 하여 T0부터 T1, T2, T3, … 이렇게 매 클럭 사이클마다 번호를 매겨줍니다.

 

앞서 블럭 다이어그램을 통해 설명했듯이, 우리가 설계할 로직에는 입력 신호와 출력 신호가 각각 하나씩 있습니다. (‘timer_en’ 및 ‘timer_out’) 해당 로직은 timer_en이 1일 때에만 작동해야 합니다. 

그림 2. 테이블 형태의 타이밍 다이어그램

 

타이머 로직의 회로 설계

이제 회로도로 구체화 해보겠습니다. Adder, Mux, Flip-Flop(FF)과 같은 기본적인 디지털 논리 소자들을 조합하여 앞에서 기술한 대로 작동하는 회로를 만들어내는 것입니다. 각 소자들이 무엇이고 어떻게 동작하는지는 이미 알고 계시리라 짐작합니다. 이와 관련된 기초 내용은 디지털 논리 회로를 참고해 주세요.

 

우선, 현재 카운트 값을 기억하는 소자가 필요합니다. 이를 위해 가운데에 Flip-Flop을 배치해 봅니다. 이 FF에 저장된 값은 매 클럭 사이클마다 업데이트 될 것입니다. 업데이트되는 값은 제어 신호 ‘timer_en’의 값이 0이냐 1이냐에 따라 달라져야 하므로 FF 앞에 mux를 추가합니다. 이미 아시다시피, mux는 제어 신호에 따라 여러 신호 중 하나를 선택해 다음 로직으로 전달합니다. 여기까지 문제 없이 따라오셨다면, 어렵지 않게 아래와 같은 회로를 완성하셨을 겁니다.

그림 3. 타이머 로직 회로도

 

타이머 로직의 회로 설계 (RTL 코드 작성)

회로도까지 작성했으니 이제는 RTL 코드를 작성할 수 있습니다. 개발 경험이 충분하지 않을 때에는, 특히 처음 배우는 단계에서는 반드시 회로도부터 그린 다음에 RTL 코드를 작성하시기 바랍니다. 이를 철저하게 습관으로 만드신다면 그렇지 않을 경우보다 훨씬 빠르고 효과적으로 실력을 키울 수 있습니다. 이 점을 매우 강조하는 편이기에, 아마 앞으로도 여러 번 다시 언급하게 될 겁니다.

 

우리는 Verilog HDL이라는 언어로 타이머 회로를 기술할 것입니다. C/C++로 대표되는 ‘프로그래밍 언어’와 달리, HDL은 하드웨어(회로)의 구조 또는 동작을 코드로 표현하기 위한 언어입니다. 당장은 왜 이런 이야기를 하는지 이해가 잘 안되더라도, 일단은 이 말을 잘 기억해 주셨으면 좋겠습니다.

 

앞에서 회로도(그림3)를 그렸으니 이를 코드(글)로 옮겨 적으면 됩니다. Verilog 문법에 대해서는 여기서 다루지 않겠습니다. 코드 작성까지 마무리되었다면 이제 검증을 해 볼 차례입니다.

 

검증 (RTL 시뮬레이션)

초심자와 숙련자를 가르는 가장 큰 차이는 문제 해결 능력에 있습니다. 어쩌면, 여러분들은 경험이 좀 쌓인 후에는 초심자가 하는 실수들을 하지 않을 것이라는 기대를 하고 계실지도 모르겠습니다. 하지만 실제로는 아무리 능숙한 개발자라도 개발 과정에서 의외로 사소하고 바보같은 실수를 많이 저지릅니다. 실수를 하는 것 자체는 실력과 아무런 상관 관계가 없는 듯합니다. 다만, 숙련자는 자신이 저지른 실수를 금방 찾아내고 빠르게 수정할 수 있습니다.

 

검증이 필요한 이유가 바로 여기에 있습니다. 우리가 저지르는 온갖 어처구니 없는 실수들을 이 검증 단계에서 모두 잡아낼 수 있습니다. 검증까지 마쳐야 설계가 끝났다고 말할 수 있습니다.

 

일반적으로 behavioral 수준의 시뮬레이션을 하여 RTL 코드로 작성된 모듈을 검증하게 됩니다. 검증할 대상을 가리켜 DUT(device under test)라고 부르고 이 DUT를 테스트하는 가상의 공간을 가리켜 testbench라고 부릅니다. 입출력 포트가 전혀 없다는 점에서 차이가 있을 뿐, 형식적으로는 testbench 역시 DUT처럼 하나의 모듈입니다.

그림 4. Testbench 회로도