본문 바로가기

실전! Verilog HDL RTL Design

[Verilog HDL] 9. Function 을 사용한 뺄샘기(Subtractor) 설계

반응형

* 이 장에서 배우는 것

  • function
  • clock gating
  • enable

 

이번에는 verilog function block을 사용하여 뺄셈기를 설계해 보자.

function block은 C언어에서의 함수와 같이 특정 코드 블록을 모아 호출할 수 있는 기능을 제공해 준다. verilog에서는 주로 조합회로를 구현 하기 위해 사용한다. 특정 조합회로를 function으로 구현하면 같은 기능을 필요로 하는 곳에 중복해서 코딩을 할 필요 없이 function을 호출하면 된다. 뺄셈기의 기본 설계 개념은 시퀀셜 가산기와 동일하다. 아래는 완성된 뺄셈기 코드이다.

module subtractor (
    input               clock,
    input               resetn,
    input               en,
    input   wire [7:0]  a,  
    input   wire [7:0]  b,  
    output  reg  [7:0]  y
    );  


    function [7:0] sub (input [7:0] a, input [7:0] b); 
    begin
        sub = a - b;
    end 
    endfunction

    always @(posedge clock or negedge resetn)
    begin
        if (~resetn)
            y <= 0;
        else if (en)
            y <= sub(a, b); 
    end 

endmodule

11라인 부터 15라인까지 'sub'라는 이름의 뺄셈 기능을 function으로 구현한 것이다. 

verilog function은 여러개의 입력을 받을 수 있지만 출력은 하나만 가능하고 출력 변수는 함수 이름과 동일하다.

'function [7:0] sub'의 의미는 8bit의 sub라는 출력 값을 리턴하는 함수라는 의미이다. 함수 이름 'sub' 뒤에는 입력 목록을 기술한다. 여기서는 8bit 입력 'a'와 8bit 입력 'b'를 입력 파라메터로 받고 있음을 알 수 있다.

 

17라인에서 23라인 까지는 DFF의 코드와 동일한 패턴으로 시퀀셜 로직을 구현하고 있다. DFF의 입력 값이 함수 리턴 값으로 바뀌어 있는 것을 알 수 있다. 즉 8bit 'y' 레지스터의 입력이 뺄셈 로직의 출력 값이 되는 것이다. 그리고 이번에는 'else if'부분이 추가 되었는데 이는 'en'신호가 '1'일 때만 입력을 출력에 전달한다는 의미이다. 이런 패턴으로 코딩하는 이유는 합성기에서 이 부분에 clock gating 로직을 추가하기 위함이다. DFF이 실제 하드웨어로 구현 될 때 CMOS logic으로 구현된다. CMOS 로직은 클록신호의 edge에서만 전류를 흐르게 하므로 클록이 동작 할 때마다 DFF이 동작한다면 전류 소모가 많아 지게 된다. 이를 방지 하기 위해 필요할 때만 clock이 입력 되도록 하는 데 위의 코드와 같이 'else if'구문으로 enable 신호를 만들어 주면 합성기가 자동으로 clock gating, 즉 clock 차단 로직을 만들어 준다. 이렇게 함으로써 다이나믹 전류를 많이 줄일 수 있어 저전력 회로 구현에 이 기법은 필수적으로 사용된다.

 

아래는 뺄셈기를 테스트 하는 테스트벤치 코드이다. 덧셈기의 테스트 벤치와 거의 동일하고 'en' 신호가 추가 되었다.

`timescale 1ns/1ns
 
module test_sub;
 
// delare variables
reg clock;
reg resetn;
reg [7:0] a;
reg [7:0] b;
reg en;
 
wire [7:0] y;
 
// clock generation
always #10 clock = ~clock;
 
// subtractor instantiation
subtractor u_sub (
    .clock  (clock),
    .resetn (resetn),
	.en		(en),
    .a      (a),
    .b      (b),
    .y      (y)
);
 
 
// create wave dump file
initial
begin
    $dumpfile("sub.vcd");
    $dumpvars(0, test_sub);
end
 
// input stimulus
initial
begin
    a = 0;
    b = 0;
	en = 0;
    clock = 0;
    resetn = 0;
    #100;
    resetn = 1;
    a = 2;
    b = 3;
    @(posedge clock);
	en = 1;
    a = 4;
    b = 5;
    @(posedge clock);
    a = 10;
    b = 30;
    @(posedge clock);
    #100;
    a = 33;
    b = 13;
	en = 0;
    @(posedge clock);
    #100;
    a = 100;
    b = 50;
	en = 1;
    @(posedge clock);
    #100;
    $finish();
end
 
endmodule

 

아래와 같이 프롬프트 창에서 iverilog로 컴파일하고 a.out file을 실행시킨다. 그리고 gtkwave로 dump file을 열어본다.

>>iverilog test_sub.v subtractor.v
>>./a.out
>>gtkwave sub.vcd

아래는 gtkwave로 찍어 본 각 신호의 wave form이다. 

위의 그림을 보면 'en'이 1인 경우에만 뺄셈이 수행 되는 것을 알 수 있는데 뭔가 이상하다. 빨간색 마커를 살펴 보면 clock의 positive edge에서 'en'신호가 1인 것처럼 동작한다. 하지만 실제 하드웨어를 합성하여 F/F이 생긴다면 'en'을 0으로 인식할 것이다. 왜 이런 현상이 발생할 까? 이는 테스트 벤치에서 입력 변수들을 대입할 때 blocking assignment를 사용했기 때문이다. 즉 clock의 positive edge에서 F/F의 입력인 'en'신호가 1로 업데이트 될 때까지 다음 구문이 실행되지 않기 때문에 F/F의 assignment 구문이 실행 될 때 'en'신호는 1이 되어 있는 것이다. 이런 현상을 해결하려면 테스트 벤치에서도 non-blocking assignment를 사용하면 된다. 아래는 non-blocking assignment를 사용한 테스트 벤치이다.

`timescale 1ns/1ns
 
module test_sub;
 
// delare variables
reg clock;
reg resetn;
reg [7:0] a;
reg [7:0] b;
reg en;
 
wire [7:0] y;
 
// clock generation
always #10 clock = ~clock;
 
// subtractor instantiation
subtractor u_sub (
    .clock  (clock),
    .resetn (resetn),
	.en		(en),
    .a      (a),
    .b      (b),
    .y      (y)
);
 
 
// create wave dump file
initial
begin
    $dumpfile("sub.vcd");
    $dumpvars(0, test_sub);
end
 
// input stimulus
initial
begin
    a <= 0;
    b <= 0;
	en <= 0;
    clock <= 0;
    resetn <= 0;
    #100;
    resetn <= 1;
    @(posedge clock);
    a <= 2;
    b <= 3;
    @(posedge clock);
	en <= 1;
    a <= 4;
    b <= 5;
    @(posedge clock);
    a <= 10;
    b <= 30;
    @(posedge clock);
    a <= 33;
    b <= 11;
	en <= 0;
    @(posedge clock);
    a <= 100;
    b <= 50;
	en <= 1;
    @(posedge clock);
    #100;
    $finish();
end
 
endmodule

이렇게 하면 아래와 같이 wave form에서 'en' 신호를 제대로 인식함을 볼 수 있다. 이것은 입력 'a', 'b'에 대해서도 마찬가지이다.

빨간색 마커에서 clock의 positive edge event에 대하여 'en' 신호는 0으로 인식함을 알 수 있다. 다음 번 사이클인 하얀색 마커를 보면 'en'을 1로 인식하고 그 때 입력인 4-5를 계산한 결과 -1을 제대로 출력하는 것을 볼 수 있다. 다시 한 번 blocking assignment와 non-blocking assignment를 언제 사용하는지 정리해 보자.

 

 

1. blocking assignment

  • cominational logic (조합회로)를 기술 할 때. 
  • begin/end 안에서 대입 문이 순차적으로 실행 되어야 할 때.

2. non-blocking assignment

  • sequential logic (순차회로)를 기술 할 때.
  • 일련의 대입 문이 병렬로 동시에 수행되어야 할 때.
반응형