[[분류:프로그래밍 언어]][[분류:하드웨어 기술 언어]] [include(틀:전기전자공학)] [include(틀:컴퓨터공학)] ||<-2><#00001b><:> '''{{{#white 하드웨어 기술 언어(HDL)}}}'''|| || [[베릴로그]] || [[VHDL]] || [[파일:external/asic.co.in/fsm_3.gif]] [목차] == 개요 == 베릴로그(Verilog)는 전자 회로 및 시스템에 쓰이는 하드웨어 기술 언어(HDL, Hardware Description Language)다. 베릴로그 HDL이라고도 부를 수 있으나, 이 경우 [[VHDL]]과 헷갈리기 때문에 베릴로그라고만 부른다. 회로 설계, 검증, 구현 등 여러 용도로 사용된다. VHDL은 과거에는 많이 사용되었지만 최근에는 주로 회사에서 Verilog HDL을 사용하고, 학교나 연구실에서는 주로[* Verilog 기준으로 하는 학교도 있다. 예시로 광운대, 서울시립대] VHDL을 사용한다.[[https://blog.lgcns.com/1804?category=515093|LG CNS 칼럼]] 통계적으로는 유럽을 제외하면 베릴로그의 점유율이 VHDL을 압도하였다. [[Ada]]기반인 VHDL과 달리 [[C언어]]와 비슷한 문법을 가진 것이 특징이다. 'if'나 'for', 'while' 같은 제어 구조도 동일하며, 출력 루틴 및 연산자들도 거의 비슷한 것 등 사용자들이 쉽게 접근할 수 있다. 다만, case문의 형태와 블록의 시작과 끝을 중괄호 대신 Begin과 End를 사용하는 것은 [[파스칼(프로그래밍 언어)|파스칼(Pascal)]]언어와 유사하다. 그러나 하드웨어 설계와 소프트웨어 설계에는 본질적인 차이가 있기 때문에 문법이 비슷하다고 C언어 프로그래밍 하듯이 베릴로그를 짜게 되면 엉터리 코드가 나오게 된다. HDL에 입문하는 사람들이 많이 고생하는 부분인데, 디지털 회로에 대한 충분한 지식을 갖고, 작성한 코드가 하드웨어로 어떻게 합성될지에 대한 감을 키워나가야 한다. HDL이 일반 프로그래밍 언어와 크게 다른 점은 시간, 클럭으로 대표되는 이벤트, 아주 작은 프로세스 개념이며, 각각 #, @, fork/join 등의 키워드로 지원한다. 다음과 같이 10의 주기를 가지는 클럭을 시뮬레이션에서 만들 수 있다.[* 하드웨어적으로 클럭은 외부의 클럭 발생기를 쓰기 때문에 이 문장이 하드웨어적으로 구현되지는 않는다. 게다가 하드웨어에서 딜레이는 회로의 형상과 반도체 소자의 성능에 따라 결정되는 값으로 시뮬레이션을 할 때 처럼 임의로 정할 수도 없다.] {{{initial clk = 0; always #5 clk = ~clk; }}} 8비트 다운 카운터 예 {{{module counter (clk, load, Q); parameter WIDTH = 8; input clk; input load; output [WIDTH-1:0] Q; reg [WIDTH-1:0] cnt; always @(posedge clk) begin if (load) cnt <= {WIDTH{1’b1}}; else cnt <= cnt - 1’b1; end assign Q = cnt; endmodule }}} 베릴로그로 작업을 할 때 컴퓨터 소프트웨어를 프로그래밍 하듯이 만들면 실제 FPGA상에서는 동작을 아예 안 할 가능성이 있다. 일반 소프트웨어 특히 파이썬 같은 스크립트 언어는 매우 추상적인 런타임과 컴파일러 상에서 동작을 하기 때문에 개발자가 신경을 쓸 게 적다. 하지만 베릴로그는 실제적인 [[하드웨어]]를 기술하고 만드는 것에 목적을 둔 언어이기에, 현실의 하드웨어로는 도저히 구현이 어려운 구문이라면 논리적으로는 문제가 없더라도 합성이 되지 않는다. 시뮬레이션은 잘 돌아가도 합성이 안 되는 경우도 있는데, 시뮬레이션 프로그램일지라도 구문을 하드웨어로 구현해서 시뮬레이션 해 주는 것이 아니라 소프트웨어적으로만 해석하려고 하기 때문에 하드웨어로 합성을 못 하는 구문도 시뮬레이션상에서는 동작이 잘 될 수도 있다.[* initial이라던지 딜레이 같은 경우는 원래 시뮬레이션에서 쓰라고 만든 기능이기 때문에 실제로 합성을 시킬 구문이라면 아예 쓸 생각 자체를 하면 안된다.] == 역사 == 본래는 Phil Moorby가 모의시험용 언어로 개발하였다. 이후 Cadence Systems[* OrCad와 알레그로, 각종 전자 시뮬레이션을 개발하는 회사다.]에 인수되어 업계표준 HDL로 자리를 잡았다. 이후 95년 IEEE표준이 만들어졌고, 2001년 변경이 있었다(Verilog-2001). 그러나 디지털 시스템의 설계에서 검증의 필요성이 커짐에 따라 이를 지원하기 위해 대대적인 확장이 필요하게 되었다. 이를 위해 2005년 SystemVerilog가 IEEE 표준으로 만들어 졌다. 2020년대 이전까지는 SystemVerilog는 검증용 테스트벤치 작성시 외엔 실제 업계에서는 잘 쓰이지 않았다. C에서 쓰이는 enum, class, struct, union등의 data type은 실제 하드웨어 합성시 결과물로 나오게 될 netlist[* 쉽게 말해서 gate-level]를 직관적으로 예측하기 어렵게 하고 툴 간에 문법 해석 차이가 있는 경우 netlist 기반의 시뮬레이션을 토대로 RTL[* 물론 이 경우 SystemVerilog 소스코드에 해당] 디버깅을 하기 상당히 까다로워졌기 때문. 물론 베릴로그의 wire, reg data type도 소스코드에 선언된 그대로 wire와 register로 합성되는 것은 아니지만 SystemVerilog의 추상화에 비하면 사실상 하드웨어에 매우 가까운 편이라 소스코드에 존재하는 버그를 찾더라도 netlist를 직접 고치는 것이 가능할 정도였다. 또한 SystemVerilog는 툴체인에서 지원을 하지 않는 경우가 있기 때문에 더더욱 사용도가 떨어진 점도 있었다.[* 칩을 제대로 하나 내보내려면, 단순 설계만 아니라 각종 합성부터 해서 backend작업까지 Pass를 시켜야 되는데, 이 backend 작업들 중에서 SystemVerilog를 지원하지 않거나 추가 라이센스비용을 내게하는 경우가 많다.] 다만 2020년대에 돌입하면서 공정이 고도화됨에 따라 Frontend 디자인을 Real 값과 유사하게 구현하기 위한 방법으로 SystemVerilog Format이 채택되었고, 이를 보다 고도화 한 UVM 등의 방법론과 프레임워크도 지속적으로 개발되고 있다.[[https://en.wikipedia.org/wiki/Universal_Verification_Methodology|UVM Wikipedia]][[https://en.wikipedia.org/wiki/Open_Verification_Methodology#|OVM Wikipedia]][[https://ieeexplore.ieee.org/document/9195920|IEEE UVM Reference Manual]] == 예제 == === 조합 논리(Combinational Logic) === always 문 스타일 예제 {{{module encoder(input [3:0] x, output reg [1:0] y); // always 문으로 조합회로를 기술할 때는 sensitivity list에 * (모두)를 넣도록 한다. // 시뮬레이터는 sensitivity list에 기술된 신호들이 변화할 때만 always 내부 요소들의 값을 갱신한다. // 하지만 합성기는 sensitivity list가 * 인 것으로 간주하므로 시뮬레이션과 합성 결과물 간에 차이점을 발생시키기 때문이다. always @(*) begin if (x == 4'b0001) y = 2'b00; else if (x == 4'b0010) y = 2'b01; else if (x == 4'b0100) y = 2'b10; else // if (x == 4'b1000) // 이외의 모든 경우의 수에도 값이 할당되도록 한다. y = 2'b11; // 모든 경우의 수에 값이 할당되지 않으면 합성 결과물에 latch가 발생하므로 if나 case문을 사용할 때는 유의하자. // latch를 포함한 회로는 조합회로가 아닐뿐더러, 일반적으로 지양되는 요소이다. // latch는 glitch에 취약하며 정적 타이밍 분석에도 불리하게 작용하기 때문이다. end endmodule }}} assign 문 스타일 예제 {{{module encoder(input [3:0] x, output [1:0] y); // output에 reg가 없음에 유의 assign y[0] = (!x[3] && !x[2] && x[1] && !x[0]) || (x[3] && !x[2] && !x[1] && !x[0]); assign y[1] = (!x[3] && x[2] && !x[1] && !x[0]) || (x[3] && !x[2] && !x[1] && !x[0]); endmodule }}} === 순차 논리(Sequential Logic) === {{{module flip_flop(input clk, input d, output reg q); always @(posedge clk) begin q <= d; end endmodule }}} non-blockinig assignment인 {{{<=}}}를 사용해야 한다. [* {{{=}}}을 사용해도 합성되기는 하나 시뮬레이션 시 레이스 컨디션 문제를 방지하기 위해 {{{<=}}}의 사용이 강력하게 권장된다. 참조: [[https://course.ece.cmu.edu/~ece447/s13/lib/exe/fetch.php?media=synth-verilog-cummins.pdf]]] == 문법 == === 상수 === [bit 수]'[진수][숫자] 4'b0110 - 4 bit 이진수 0110 32'd2020 - 32 bit 십진수 2020 8'ha12 - 8 bit 16진수 a12 16'o20 - 16 bit 8진수 20 === 연산자 === === 시스템 태스크(System tasks) === 간단한 I/O 기능을 제공하는 함수들로 주로 '''시뮬레이션''' 과정에서 활용한다. 다른 함수와 구분하기 위해 모든 시스템 태스크는 '''$'''를 접두사로 사용한다. * '''$display''' 문자열과 지정한 신호의 값을 화면에 출력한다. 자동으로 개행된다. {{{$display("Hello World!");}}} {{{$display("%b, %d", a, b);}}} * '''$write''' 기능은 {{{$display}}}와 동일하나 개행 없이 출력한다. * '''$monitor''' 지정한 신호의 값이 바뀌면 화면에 출력한다. * '''$stop''' 시뮬레이션을 중단한다. {{{$finish}}}와 달리 control이 여전히 simulator에 있으며 simulator가 Interative Mode가 된다. * '''$finish''' 시뮬레이션을 종료한다. {{{$stop}}}과 달리 control이 OS로 돌아온다. * '''$time''' 현재의 시뮬레이션 시간 {{{$display($time);}}} * '''$random''' 랜덤값을 반환한다. {{{$display($random);}}} === 반복문 === 베릴로그에서는 for, while의 루프문을 지원하지만 초심자의 경우 테스트벤치 작성을 위한 경우 말고는 사용을 지양하는 것이 좋다. 베릴로그는 일반적인 프로그래밍 언어와는 다르게 하드웨어 수준의 구조를 코드로 작성하는 것이기 때문에 루프문의 무분별한 오용은 하드웨어의 합성 과정에서 문제를 일으킬 가능성이 높다.[* 보수적인 엔지니어의 경우 for문의 사용 자체를 금기시하기도 할 정도이다.] 따라서 설계할 루프문이 하드웨어로 합성 가능한 구조인지 잘 따져가면서 사용하는게 중요하다. 반복문이 다이나믹하다면 당연히 합성 불가능하고, 생각하기 힘든 다양한 함정들이 존재하므로 단순한 규칙적인 구조를 기술하는 수준이 아닌[* 이것도 초심자의 경우 경우에 따라 생각치도 못한 문제가 발생할 수 있다.], 루프끼리 복잡한 상호 작용이 있는 구조를 기교를 부려서 기술하는 것은 지양하는 것이 좋다. 소프트웨어 프로그래밍에서의 반복문과 달리, 베릴로그에서의 루프는 일종의 단순 복사-붙여넣기와 같다. 예를 들어 {{{ wire [3:0] w_sig_a; wire [3:0] w_sig_b; genvar i; generate for (i = 0; i < 4; i = i + 1) begin// 베릴로그에서는 i += 1과 같은 표현이 불가능하다. assign w_sig_b[i] = w_sig_a[i]; end endgenerate }}} 과 같은 코드는 다음과 같이 unroll 된다. {{{ wire [3:0] w_sig_a; wire [3:0] w_sig_b; assign w_sig_b[0] = w_sig_a[0]; assign w_sig_b[1] = w_sig_a[1]; assign w_sig_b[2] = w_sig_a[2]; assign w_sig_b[3] = w_sig_a[3]; }}} 이 때문에 반복문이 몇 번 반복되는지에 대한 정보를 합성기가 컴파일 타임에 알 수 있어야 한다. == 관련문서 == * [[systemC (언어)|systemC]] * [[VHDL]], [[ASIC]]