이번 포스트에서는 Verilog에서의 할당과 관련해 정리해보려고 합니다.

Verilog 할당문에는 연속적 할당(Continuous Assignment)과 절차적 할당(Procedural Assignment)이 존재하는데요.

연속적, 절차적이라는 단어에서 느낌이 오듯이
연속적 할당은 특별한 조건 없이 연속적으로 이루어지는 할당인 반면
절차적 할당의 경우 어떤 절차, 조건이 만족됐을 때, 할당이 되는 방식으로 볼 수 있습니다. 

한번 자세히 살펴봅시다.


- 연속적 할당(Continuous Assignment)

 

연속적 할당의 경우, 우변의 값에 변화가 있을 때마다 좌변에 할당을 해주게 됩니다.

즉, 우변 값이 변하게 되는 순간에 좌변에 할당이 되는 것이죠.

그러면 이전 포스트에서 공부했었던 wire와 reg와 연관지어서 생각을 해봅시다.

연속적 할당은 wire와 reg 중 어떤 port type과 어울릴까요?

 

연속적 할당의 경우, Clock을 기다리는 어떤 절차(조건)를 기다렸다가 할당하는 것이 아니라 우변의 값이 변화하는 즉시 좌변에 할당을 하는 것이죠.

따라서 값을 저장하고 있다가 어떤 특정 타이밍에 값을 Update하는 reg보다는
값을 저장할 수 없기에 항상 값을 Update하는 wire와 연관이 깊습니다.

 

참고로 연속적 할당을 할 때, assign 구문을 이용하여 assign (좌변) = (우변) ; 꼴로 작성을 하게 되는데
좌변에는 항상 wire type 변수만이 올 수가 있습니다. (reg는 assign 문의 우변에는 올 수 있지만 좌변에 올 수 없습니다.)

module add(
  input wire  [1:0] a,
  input wire  [1:0] b,
  output wire [2:0] sum
);
  
  sum = a + b;
  
endmodule

위는 간단히 a와 b를 더해서 sum을 구하는 Verilog 코드입니다.

assign문을 이용해서 sum에 a+b 값을 더한 결과를 할당해준 것을 확인할 수가 있습니다. 


- 절차적 할당(Procedural Assignment)

 

절차적 할당은 절차적 할당문에 도달 했을때 해당 문장이 실행되면서 할당이 되는 방식입니다.

앞서 연속적할당의 경우는 wire에 assign을 하여 할당을 해주었는데요.

절차적할당은 always 문이나, initial~begin 문 안에서 reg에 할당을 해주게 됩니다.

 

- always 문

always문은 Verilog를 접해봤다면 정말 많이 보게 되는 구문이죠. 

always문은 always @([sensitive list]) begin ~ end 형태로 작성을 하게 되는데 
sensitive llist가 변할 때마다 할당을 해라~ 라는 의미입니다. 

앞선 포스트에서도 소개했었던 D Flip-Flop 코드를 예로 한번 살펴봅시다.

module d_flipflop(
  input wire clk,
  input wire rst_n,
  input wire d,
  output reg q
);
  
  always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
      q <= 1'b0;
    else
      q <= d;
  end
  
endmodule

Verilog 코드를 보게 되면 always문이 나타나 있고 sensitive list에 posedge clk or negedge rst_n이라고 되어있습니다.

posedge라는 것은 clk의 positive edge(상승 엣지)를 의미하고 negedge는 negative edge(하강 엣지)를 의미합니다.

즉, 여기서는 clock의 상승 엣지마다 혹은 reset의 하강 엣지마다 begin~end에 있는 코드를 실행시켜서 reg q에 할당해라! 라는 의미가 됩니다.

 

연속적 할당과는 확연한 차이죠.
항상 할당이 되는 것이 아닌 clock의 상승엣지를 만났을 때와 같은 어떤 조건이 됐을 때, 비로소 할당이 되는 방식이니깐요.

이러한 always 문은 assign과는 반대로 좌변에 wire를 사용할 수가 없습니다. 
예를 들어 위의 예시에서 q가 wire였으면 아마 컴파일 오류가 발생했겠죠?

 

- initial 문

이번엔 inital 문을 한번 살펴볼게요. 

initial 문은 사실 module을 설계해서 실제 Chip으로 나올 코드로는 쓰이지 않기도 하고 쓰일 수가 없습니다.

왜냐면 initial 문은 합성(Synthesis)이 되지 않기 때문이에요. 

즉, Verilog 코드에 initial 문을 포함해 작성을 하고 실제 Chip으로 변환하려고 합성을 하게 되면 initial 문은 합성이 되지 않기 때문에 날라가버립니다.

 

그럼 initial문은 어디에 쓰이는가 하면 Testbench를 구성 시 많이 쓰입니다.

Testbench는 Verilog 설계 단에서 의도했던 Function이 잘 동작하는지 보기 위해 필요한 것이기 때문에 실제 Chip으로 나올 필요가 없죠. 

따라서 합성이 될 필요도 없으니 Testbench에서는 이 inital 문을 사용합니다.

Initial문은 initial begin~end 구문으로 사용이 되고 initial 구문은 반복해서 동작하는 것이 아닌
simulation이 시작할 때 단 한번만 실행된다는 의미에서 initial이라는 이름을 가집니다.

 

그럼 위의 initial 문으로 Testbench를 구성해서  D Flip-flop을 Test해볼까요?

module d_flipflop_tb;

  reg clk;
  reg rst_n;
  reg d;
  wire q;
  
  initial begin 		//clock generation
    clk = 1'b0;
    forever begin
      #5;
      clk = ~clk;
    end
  end

  initial begin			//Input
    rst_n <= #1 1'b0;
    d     <= #1 1'b0;
    
    @(posedge clk);
    rst_n <= #1 1'b1;
    
    @(posedge clk);
    d     <= #1 1'b1;
  end
    
  d_flipflop u_d_flipflop(	//Instantiation
    .clk   (clk),
    .rst_n (rst_n),
    .d     (d),
    .q     (q)
    );
  
endmodule

위와 같이 initial begin~end 구문을 이용해서 testbench를 구성할 수가 있습니다.

testbench를 어떻게 구성하는지는 다음에 설명할 기회가 있을 것 같고 내부 코드를 살짝 볼까요?

 

음~ 저희가 배웠던 reg, wire 선언이 보입니다.

Module을 설계할 때, module 내부의 input은 wire, output은 reg/wire type을 사용해야 한다고 배웠는데

Testbench의 경우는 input을 reg, output은 wire type을 사용하게 된다는 점을 참고해주세요 :)

다음으로 initial begin~end 문으로 5ns마다 Toggle하는 clock을 생성한 것이 보이고 
두번째 initial begin~end 문으로 Test Input을 넣어주는 것을 볼 수가 있습니다.

* 그런데 = 이 아니라 <= 으로 쓰여져 있는게 좀 이상한데 이 이유에 대해서는 다음 포스트에서 알아볼께요.


그리고 아래에 저희가 공부했던 instantiation이 보이네요.

instantiation을 설명할 때, testbench를 구성시에 사용된다고 했는데 이렇게 사용이 되는 것이었군요!

 

아무튼 위와 같이 D Flip-Flop 모듈과 Testbench 모듈을 이용해 시뮬레이션을 해보면

 의도한 대로 Clock의 상승 edge에서 d값이 q로 할당 되는 것을 볼 수가 있습니다. 

 

 

반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기