[Verilog] 문법 1 – 기본 구성, 절차 할당과 연속 할당

이 글에서는 verilog(베릴로그) 기본 문법을 살펴보겠습니다. 먼저 알아야 할 것은 한 문장의 끝은 세미콜론(;)으로 마무리해야 한다는 겁니다.

Verilog 기본 구성

module module_name (port_list);
  
  //선언 
  reg reg_name;
  wire wire_name;
  parameter parameter_name;
  
  //회로 설명 part
  인스턴스화
  always
  initial
  function, task 정의
  assign
    
endmodule

위를 보시면 verilog 작성 시 기본적인 구조에 대해 알 수 있습니다. 하나씩 알아볼까요?

모듈(module) 선언

module: 기본 설계 단위입니다. module – endmodule로 작성하며, 한 파일에 여러 개의 module이 들어갈 수 있지만 한 개의 module만 작성하는 것을 추천합니다.

port_list: 해당 회로에 input port와 output port를 정의해 줍니다. input과 output port가 없으면 비워도 됩니다. ()

자료형, 상수 선언

베릴로그의 자료형은 크게 2가지로 나뉩니다. (Net, register)

Net

Net은 하드웨어 간의 단순한 물리적 연결입니다, 따라서 연속 할당(assign 문)에 사용됩니다. Net의 종류에는 wire, wand, wor 등이 있지만 실무에서 wire 말고 다른 net을 본 적은 없으므로 저는 wire만 사용하겠습니다.

Register

Variable 자료형으로 절차적 할당(=, <=)에 사용됩니다. Register의 종류에는 reg, integer, real 등이 있지만 reg를 주로 사용합니다.

reg: 플립플롭(Flip-Flop)이나 래치(Latch) 같은 저장소자를 모델링할 수 있습니다.

마지막으로 parameter를 통해 상수를 선언할 수 있습니다.

자료형의 초기값

reg는 initial이나 always 구문 안에 있는 절차 할당을 통해, wire는 연속 할당을 통해 값을 할당해 줍니다. 만약 값이 할당되지 않으면 어떻게 될까요?

reg : reg의 초기값은 x(unknown)입니다.

wire: wire의 초기값은 z(high impedance)입니다. 스위치가 열려있는 회로라고 생각하시면 됩니다.

회로 설명

실제 회로의 기능이나 동작하는 방식, timing이 기술됩니다. 인스턴스화를 통해 회로의 구조를 기술할 수 있고 initial 문, always 문 등을 통해 회로 동작을 기술합니다.

여기서 인스턴스화는 이미 완성된 모듈은 서브 모듈로 가져오는 것을 말합니다.

회로 설명에서 사용되는 구문에는 크게 2가지가 있는데 논리 합성에 사용되느냐, 되지 않느냐로 나눌 수 있습니다. 논리 합성이 되는 구문은 하드웨어 설계에, 합성되지 않는 구문은 시뮬레이션에 사용된다고 보시면 됩니다. 

논리합성Simulation
assign
if ~ else
case
always 등
테스트벤치의 회로 설명
시스템 task ($display, $finish 등)

마지막으로 주의할 점은, 선언된 자료형과 상수만 사용해야 한다는 것입니다. 그렇지 않을 경우, syntax error가 발생합니다.

주석

주석은 //으로 처리하며 해당 line 끝까지 주석 처리됩니다. 구간을 주석 처리하고 싶으면 /* ~ */을 사용하면 됩니다.

reg a; //이후로는 주석처리
reg b;

//wire f만 선언됨
wire /*c;
wire d;
wire e;
wire */ f;

절차 할당

initial 구문

initial 구문은 주로 testbench(시뮬레이션을 위한 환경이라고 보시면 됩니다)에서 시뮬레이션을 위해 사용하는 구문으로, 논리 회로 합성에 사용되지 않습니다. initial 구문 안에는 절차형 할당들이 들어가며, 이 때문에 reg 값 할당을 작성해 주면 됩니다. 주의할 점은, initial 구문은 testbench 용 구문이라는 것입니다. 이 말은, 실제 합성 시 회로로 구현되지 않는다는 것입니다.

한 initial 구문 안에서 작성된 절차형 할당은 문장의 순서에 따라 시뮬레이션이 진행됩니다. 이게 무슨 의미인지는 예시를 보면서 이해해 봅시다.

Testbench 작성

module top ();
  
  //reg 선언
  reg a;
  
  //1. reg control
  initial begin
    a = 1;
    #100
    a = 0;
    #100
    a = 1;
  end
  
  //2. 시뮬레이션 time
  initial begin
    #1000
    $finish;
  end
  
  //3. dump file 만들기
  initial begin
    $dumpfile("dump.vcd");
    $dumpvars;
  end
  
endmodule

initial 구문은 시뮬레이션이 시작된 후 동작을 나타냅니다. 크게 3개의 initial 구문이 있는데요, 각각에 대해 설명하겠습니다.

reg control

사전에 선언된 reg a의 값을 할당합니다.

#: delay를 나타냅니다. 시뮬레이션이 시작할 때 a 값은 1이고 100 time이 지난 뒤 a는 0으로 바뀌고, 다시 100 time이 지나면 1로 바뀐다는 것을 의미합니다. verilog에서 timescale은 대부분 ns를 쓰므로 100ns, 100ns가 지났음을 알 수 있습니다. 이것 또한 initial 구문과 마찬가지로 실제 회로로 합성되지 않는 문법입니다.

=: initial 구문이나 always 구문 안에 있는 =은 절차 할당문을 의미하며 reg에 할당됩니다. 절차 할당에는 non-blocking 할당(<=)과 blocking 할당(=)이 있으며, =은 blocking 할당입니다. 이 둘의 차이는 나중에 다시 설명하도록 하겠습니다.

initial 구문 안에 두 줄 이상 있으면 begin ~ end를 써줘야 하지만, 한 줄만 있는 경우 begin ~ end를 생략할 수 있습니다.

또한 각각의 initial 구문은 시뮬레이션이 시작된 뒤 병렬적으로 진행되기 때문에 순서에 상관이 없습니다. 한 initial 구문 안에서의 순서만 중요합니다.

  initial begin
    a = 1;
    #100
    a = 0;
    #100
    a = 1;
  end
  
///////////////////
////위 아래 같음////
///////////////////
  
  initial      a = 1;
  initial #100 a = 0;
  initial #200 a = 1;
  
///////////////////
////위 아래 같음////
///////////////////
  
  initial #100 a = 0;
  initial      a = 1;
  initial #200 a = 1;

Simulation time

$finish: $는 verilog 시스템 task입니다. 그중 $finish는 시뮬레이션 종료를 말합니다. 즉, 이 initial 구문에서는 시뮬레이션 시작 후 1000ns 뒤에 시뮬레이션을 종료하겠다는 의미입니다.

Dump file 만들기

Waveform을 띄우려면 dump 파일을 만들어야 합니다. Waveform 결과를 확인하기 위한 dump 파일을 만들라는 의미입니다.

always 구문

always 구문은 initial 구문과 마찬가지로 reg에 값을 할당할 때 사용합니다. 다음 예시를 실행해 볼까요?

module top();

  parameter period_clk = 10;

  reg  clk;    initial clk = 1'b0;
  reg  resetn; initial resetn = 1'b0;

  always #(period_clk*0.5) clk = ~clk;

  initial begin
    #(period_clk);
    resetn = 1'b1;
  end
  
  initial begin
    #500
    $display("//////////////");
    $display("//sim_finish//");
    $display("//////////////");
    $finish;
  end

  initial begin
    $dumpfile ("dump.vcd");
    $dumpvars();
  end

endmodule

결과는 다음과 같습니다.

clk, resetn result
clk, resetn result

clk, resetn이 선언된 뒤, 아래 명령이 항상 실행됩니다.

#(period_clk*0.5) clk = ~clk;

위의 always 문 표현은 testbench에서만 활용되는 방법이고 always 문의 정석은 다음과 같습니다.

always @(sensitive_list) begin
  //blocking or non-blocking 할당
end

여기서 @은 event를 감지한다는 의미입니다. 유저가 설정한 sensitive_list에 변화가 감지되면 always 구문 안의 절차 할당이 실행됩니다. 아래 예시를 보면서 이해해 볼까요?

//testbench
module top ();
  
  //자료형 선언, 초기화
  reg  [3:0] a; initial a = 4'h0;
  reg  [3:0] b; initial b = 4'h0;
  
  wire [7:0] c;
  
  //dut 인스턴스화
  multiplier u_dut (
     .x   (a)
    ,.y   (b)
    ,.out (c)
  );
  
  //reg control
  initial begin
    #100
    a = 4'h3;
    b = 4'h5;
    #100
    a = 4'h7;
    b = 4'h6;
    #100
    a = 4'h0;
    b = 4'hf;
    #100
    a = 4'hd;
    b = 4'hc;
  end
  
  //시뮬레이션 time
  initial begin
    #500
    $display("////////////////");
    $display("///sim_finish///");
    $display("////////////////");
    $finish;
  end
  
  //dump file 만들기
  initial begin
    $dumpfile("dump.vcd");
    $dumpvars;
  end
    
endmodule

//DUT module
module multiplier (
   input  wire [3:0] x
  ,input  wire [3:0] y
  ,output reg  [7:0] out
);
  
  always @(x or y) begin
    out = x * y;
  end
  
endmodule

위 코드에 있는 multiplier는 이전 글의 모듈을 변형한 것입니다. assign 대신 always 구문을 사용하여 out에 값을 할당했는데요, 주의할 점은 assign이 아니라 always 구문을 사용하기 때문에 out port를 wire가 아닌 reg로 선언해야 한다는 것입니다.

multiplier 모듈 안에 있는 always 문을 살펴봅시다. sensitive_list에 x or y가 입력되어 있는데요, x나 y 값에 변화를 감지하면 always 문이 실행됩니다. 결론적으로 이전 글에 있던 multiplier와 동일한 결과를 얻을 수 있습니다.

또한 multiplier 모듈을 아래와 같이 표현할 수 있습니다.

module multiplier (
   input  wire [3:0] x
  ,input  wire [3:0] y
  ,output reg  [7:0] out
);
  
  always @(x or y) begin
    out = x * y;
  end
  
endmodule
////////////////////////////
////////////////////////////
////////////////////////////
module multiplier (
   input  wire [3:0] x
  ,input  wire [3:0] y
  ,output wire [7:0] out
);

  reg [7:0] result;
  always @(x or y) begin
    result = x * y;
  end
  
  assign out = result;
  
endmodule
////////////////////////////
////////////////////////////
////////////////////////////
module multiplier (
   input  wire [3:0] x
  ,input  wire [3:0] y
  ,output wire [7:0] out
);

  reg [7:0] result;
  always @(*) begin
    result = x * y;
  end
  
  assign out = result;
  
endmodule

2번째 표현은 output port를 wire로 선언하고 assign을 한 것입니다. 단순한 변형이지만 회사에 있을 때 대부분의 IP는 output port도 wire로 선언하더군요.

3번째 표현은 sensitive_list에 x or y가 아닌 *를 쓴 것입니다. *란 sensitive_list가 모든 입력을 포함한다는 뜻입니다, 즉 x y 모두 포함한 것입니다.

연속 할당

initial 구문이나 always 구문에 포함되지 않은 = 연속 할당을 나타내며 wire에 할당됩니다.

//연속 할당 형식
assign wire_name = logic;

그럼, testbench를 다음과 같이 수정해 봅시다.

module top ();
  
  //자료형 선언
  reg  [7:0] a;
  wire [7:0] b;
    
  //연속 할당
  assign b = a;
  
  //1. reg control
  initial begin
    a = 8'b1;
    #100
    a = 8'b0;
    #100
    a = 8'b1;
  end
  
  //2. 시뮬레이션 time
  initial begin
    #1000
    $finish;
  end
  
  //3. dump file 만들기
  initial begin
    $dumpfile("dump.vcd");
    $dumpvars;
  end

endmodule

여기서 reg [7:0]과 wire [7:0]은 해당 reg와 wire의 bit-width가 0~7번째까지 총 8비트임을 의미합니다. 그렇기 때문에 첫 번째 initial 구문에서 reg a의 값을 할당해 줄 때 8’b1, 8’b0라고 표현했습니다.

8′: 8비트임을 의미합니다.

b: 진법을 의미합니다. b는 2진법, o는 8진법, d는 10진법, h는 16진법을 의미합니다.

그럼 위 testbench의 simulation 결과를 확인해 볼까요?

Simulation result
Simulation result

reg a 값이 wire b에 잘 할당된 것을 알 수 있습니다. 다른 예시를 하나 더 보겠습니다.

module top ();
  
  //자료형 선언
  reg  [7:0] a;
  reg  [7:0] a_1;
  wire [7:0] b;
    
  //연속 할당
  assign b = (a | a_1);
  
  //1. reg control
  initial begin
    a = 8'h1;
    #100
    a = 8'h2;
    #100
    a = 8'h3;
  end
  
  initial begin
    a_1 = 8'h40;
    #100
    a_1 = 8'h50;
    #100
    a_1 = 8'h60;
  end
  
  //2. 시뮬레이션 time
  initial begin
    #1000
    $finish;
  end
  
  //3. dump file 만들기
  initial begin
    $dumpfile("dump.vcd");
    $dumpvars;
  end

endmodule

위와 같이 assign(연속 할당)에 logic을 넣어도 됩니다.

Simulation result
Simulation result

참고로 assign 문은 사전에 wire가 선언만 되어있다면 순서는 상관없습니다. 다만 코드 재사용을 위해 정리를 잘해야겠죠?

참고: Verilog standard

Similar Posts