Tag Archives: Coding Guideline

Coding style; 잘 사는 방법

예전에 C 언어를 한참 할때, 코딩스타일이란 이야기를 처음 들었었습니다. K&R style이라느니, ansi style이라느니.. 그런 것이지요. indent를 2를 써야 한다.. 아니다 4를 써야 한다 등등도 있었구요.
Windows API에 와서는 이게 좀 더 복잡해져서 hungarian 표기법이 등장했습니다.
그런데, Verilog HDL에서는 이 Coding Style이라는 것이 아주 중요한 요소로 등장하고 있지요.

코딩 스타일이란 쉽게 이야기하자면, 어떤 설계를 하고자 할때, 혹은 어떤 표현을 하고자 할때 사용할 수 있는 방법이 여러가지가 있겠지만, 이런 형태로 표현 하는 것이 어떠냐~ 라는 식의 일종의 가이드입니다.
HDL에서는 FSM에서 one-hot을 표현하는 방법이라던지, mux를 합성하는 방법이라던지와 같은 언어적인 특성을 잘 표현하기 위한 가이드도 있고, negative edge신호는 n으로 끝나고, 플립플롭의 출력은 _r로 끝난다는 등의 이름 정하기 규칙(네이밍 룰), 그 이외에 indent(들여쓰기)는 tab이 아닌 2칸의 공백 문자로 한다는 등등일 일반적인 가이드를 포함합니다.

HDL에서 코딩 스타일이 중요한 이유는 여기에서도 간단히 말씀드렸습니다만, 생각하면서 따져나가야 하는 약간은 미묘한 문제들을 쉽게 (머리쓰지 않고 기계적으로.. 혹은 습관적으로) 해결할 수 있는 방법이 되는 경우가 많기 때문입니다. 또한, HDL의 경우 궁극적으로 “하드웨어를 만들기 위한 언어”이므로, 어떤 방식으로 기술하는 것이 로직 합성기에서 가장 잘 이해하도록 만들어서 원하는 하드웨어의 형태로 가장 효과적으로 만들어질 수 있도록 잘 기술하는 것이 필요한 것이지요.

대부분의 문제는 좋은 코딩스타일로 해결 가능합니다. 그리고, 코딩 스타일과 코딩 가이드를 적절히 조합한 문서가 제 블로그에서 몇번 소개해 드린바 있는 Reuse Methodology Manual 입니다.

하지만, 가끔은 코딩 스타일로 부족할 때가 있지요. 이럴 때 보통 synthesis directive를 줍니다.
synopsys툴은 ‘//synopsys 어쩌구’.. synplify는 ‘//synthesis’ 로 시작되고, verilog 2001에서는 이 synthesis directive주는 방법이 통일 되었지요.. 그래도 저는 synopsys를 사용할 때 //synopsys 어쩌구를 계속 사용하고 있습니다..  ^^;

가장 유명한 synthesis directive는 바로 Donny님의 posting에 나온 SNUG99논문(Sunburst의 Cumming씨 논문으로 기억되는데요..Cumming씨의 SNUG논문들은 synopsys툴을 이용해서 verilog를 사용하시는 분들에게 아주 좋은 아이디어를 많이 제공해 줍니다. – 설계적인 측면이 아니라 코딩의 기술적인 측면에서 말이지요)에서 언급된 case문을 지배하는 evil twins이지요.
case문에서 parallel case는 MUX와 같이 priority가 없는 로직을 만드는 순서 없는 case를 만들어 낼 때 사용이 되고, full case는 그야 말로 fully covered case를 나타내지요.. 근데, 좋은 약도 남용하면 독이 되듯, 이 좋아보이는 synthesis directive도 잘 알지 못하고 쓰면 오히려 독이 되어 불필요한 latch를 만들 수도 있는 법이지요. 자세한 것은 도니님이 남겨주신 문서를 참조하세요. ^^;

제가 회사에서 강조하고 있는 건, “합성시에 case문에 대한 분석이 auto로 나오도록 만들 것입니다.”
auto 로 나온다는 것은 코딩 자체에서 “parallel case”혹은 “full case”를 cover 하고 있다는 의미이거든요. 즉, one-hot FSM과 같이 특별한 케이스로 directive의 사용을 제한하고 있지요.

이런 코딩 스타일은 궁극적으로 설계자들에게 불필요한 고민을 줄여주어, 1분이라도 시간을 더 확보하게 함으로써 삶의 질을 높일 수 있는 기회가 될 수 있겠습니다. (혼자만의 논리적 비약일까요?)
귀찮고, 당연한 이야기 같더라도 코딩 스타일.. 꼭 숙지하세요.

p.s. 여담인데, 저의 경우 excel도 훌륭한 코딩 툴로 사용됩니다. 약간 복잡한 case를 다룰때 말이죠 ㅎㅎ, csv format으로 뽑아내는 재미가 쏠쏠하죠.

Verilog newsgroup에서의 몇가지 이야기

verilog news group에는 여러가지 verilog 관련 이야기가 나오는데.. 몇가지만 옮겨 봅니다.

1. Implicit Zero Padding?
verilog의 bit 확장에 대한 부분인데요.. 간략히 써보면 다음과 같은 질문입니다.

verilog가 큰 수에 작은수를 대입할때 ‘0’으로 채우는 것으로 알고 있어.

[CODE] module tilde (output reg[7:0] z, input [3:0] a);
    always @* begin
      z = ~a;
    end
endmodule[/CODE]

위의 예에서도 상위 4비트는 ‘0’이 되어야 겠지? 하위 4비트는 당연히 a의 반전이겠지만 말야.. 근데, 적어도 modelsim에서는 상위 4비트가 항상 1이 된다! 내가 잘못 이해한거야? 아님 모델심 문제야?

여기에 대해서 여러가지 대답들이 있는데.. 대답을 보기전에 우선 몇가지 정리해 보시지요..^^;
기본적으로 큰 값에 대한 assign시에 RHS의 값이 LHS값보다 작은 경우 그 값은 확장되면서 상위값은 ‘0’으로 채워집니다. 왜냐하면 verilog에서 기본적으로 다루어지는 수치형은 ‘unsigned’이기 때문이죠.
verilog 2001에서는 signed라는 예약어가 들어가서 signed 수로 인식되기도 합니다만, 위의 예에서는 관련이 없겠지요?

질문자는 assign 동작이 ~(inversion)보다 늦게 일어나므로, inversion 이후에 assign이 일어나야 하며, assign과정에서 확장 동작이 일어나야 하는데, 왜 동작은 inversion이전에 크기 확장이 일어난것 같이 동작하느냐는 것이 가장 중요한 질문의 요지입니다.

그런데, 위의 예에서는 왜 ‘1’로 채워지는 것일까요? 이것은 verilog에서 연산을 처리하는 rule과 관계가 있습니다.
이 문제에 대하여 지존급의 대답을 해준 케이던스의 sharp님의 글을 보면 아주 명확히 설명하고 있습니다.

Verilog first determines the width of an expression based on the largest operand in it, including the LHS of any assignment.  Then it extends all operands (actually, all context-determined operands) to the width of their expression before performing any operations.  All extensions are done as early as possible, in an attempt to avoid overflows in intermediate results.

가장 중요한 verilog 연산에서의 크기 확장 법칙이 위의 법칙입니다. 즉, 연산 이전에 LHS를 포함한 모든 연산 요소를 살펴보아서 가장 큰 값에 맞추어 값을 확장하는 것입니다. 이는 “C”언어에서의 type casting rule과 완전히 다르기 때문에 많은 분들이 헷깔리게 되는 것이지요.

sharp님이 추가적으로 설명한 부분은 사실 저도 정확히는 모르고 있던 부분인데.. (경험으로는 알고 있었습니다만, 명확한 이론은 없었습니다.) 연산에 따른 확장에 대해서 알려주고 있습니다.

When the result of an operation will have a fixed width regardless of the width of its operand, there is no reason for the operand to care about the width of the context.  This is true of the reduction operators.  The result will always be 1 bit, no matter what the operand width is.  There is no point in extending the operand.  Instead, the 1-bit result will be extended to the width of the expression as soon as it is produced.  The operand of a reduction operator is
self-determined.
On the other hand, all the bits of a bitwise NOT will be available to the expression containing it, and may be assigned or used in another bitwise operation.  So the operand of a bitwise NOT is context-determined.

But one has a self-determined operand and the other has a context-determined operand.  The Verilog LRM fully specifies this for all the operands of all the operators.  They generally follow a logical scheme that makes sense.  And again, in most cases they are designed to give reasonable results, so that most users don’t have to worry about them.

상당히 어려운 이야기인 것으로 보입니다만, 실은 같은 width를 보장해야 하는 연산과 그렇지 않은 연산이 있고, 이에 따라 확장을 하는 부분이 있다.. 라고 요약하시면 되겠습니다. 위의 룰은 처음 설명 드린 룰의 보충내용이므로 그리 중요하지는 않습니다.

이러한 문제는 “고민을 시작하면” 엄청나게 신경쓰이는 문제이므로, 일반적인 coding style을 따져서 문제가 될만한 부분을 초기에 잡아나가는 것이 현명하겠습니다.
즉, 1) verilog coding시에서 width 지정을 정확히 하고, 확장이 필요한 경우 명시적으로 concat operation을 쓰자. 2)일반적으로 width를 지정하지 않은 상수는 verilog에서 32bit으로 처리되므로, 이로 인해 원하지 않는 동작을 피하고 싶으시다면, 항상 상수를 지정할때 width를 지정하는 하자.
이런 일반 룰만 지키신다면 머리 아플일이 없겠지요..^^;

좋은 Coding Style의 존재 이유가 “불명확한 코드로 나중에 머리 아프지 말고 코딩 부터 잘하자..”이런 것이니까요..^^;

추가적으로..
comp.lang.verilog에 인터뷰에서 asynchonous설계시 주의할점에 대해서 물어봤는데.. 대답을 못했다는 내용도 있네요.^^; 얼마전에 이야기한 metastable에 대한 이야기를 묻는 것이지요.. 정말 많이 물어보는 질문이기는 한가보군요..