본문 바로가기

RTL Design/Verilog RTL 설계

Verilog RTL 설계(7월 15일 - 2, Register, TestBench)

1. Register는 D Flip Flop을 기반으로 구현 할 수 있다.

  • D Flip Flop은 1bit 데이터를 저장할 수 있는 기억 소자이다.
  • 따라서 D Flip Flop을 병렬로 여러 개를 이어붙어 Register를 생성할 수 있다.
  • 아래 코드는 8개의 D Flop Flop을 병렬적으로 이어 붙어 8bit register를 생성할 수 있다.

 

< Source, 8bit register >

// 8bit register
module Register_8bit_implemented_with_D_Flip_Flop_Positive(
    input clk, enable, reset_p,
    input [7:0] d,
    output reg [7:0] q );
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) q = 0;
        else if(enable) q =d;
    end
    
endmodule

 

< RTL analysis >

8bit register implemented with D Flip Flop

 

 

 

 

 

 

2. Register은 다양한 입력, 출력 구조를 갖으며, 이에 따라 종류를 구분한다.

  • Register는 아래와 같이 다양한 입출력 구조를 갖으며, 입출력 구조에 따라 Register를 구분한다.

레지스터의 입출력의 형태에 따라 레지스터의 종류를 구분한다.

 

  • 직렬 입력 - 직렬 출력 (SISO) : 송신기 + 수신기를 동시에 사용하고자 할 때, 사용
  • 직렬 입력 - 병렬 출력 (SIPO) : DEMUX 구조를 갖으며, 수신기를 구현하고자 할 때, 사용
  • 병렬 입력 - 직렬 출력 (PISO) : MUX 구조를 갖으며, 송신기를 구현하고자 할 때, 사용
  • 병렬 입력 - 병렬 출력 (PIPO) : 고속으로 데이터를 처리하고자 할 때, 사용

 

 

 

 

 

3. SISO Shift  Register ( Series Input Series Output )

  • 직렬 형태로 입력을 받고, 레지스터에 저장된 데이터를 직렬로 출력하는 레지스터
  • clk (Clock Pulse)의 한 주기마다 레지스터내에서 Shift시켜 데이터를 저장하거나 출력한다.
  • SISO Shift Register는 아래와 같은 블록도와 Timing diagram을 갖는다.

4bit SISO Shift Register

 

 

 

3.1.  Vivado 를 통해 SISO Shift Register를 구현하기

 

< Source, Non-blocking, 잘못된 예시>

// SISO Shift Register
// Series Input, Series Output
module shift_register_SISO_Non_blocking (
    input clk, enable, reset_p,
    input d, output q);
    
    reg [3:0] siso_reg;
    
    always @(posedge clk or reset_p) begin
        if(reset_p)  siso_reg = 4'b0000;
        else if(enable) begin
            siso_reg[3] = d;
            siso_reg[2] = siso_reg[3];
            siso_reg[1] = siso_reg[2];
            siso_reg[0] = siso_reg[1];
        end
    end
    
    assign q = siso_reg[0];
    
endmodule
  • 위 코드는 Non-blocking으로 구현하였다.
  • 따라서 always 문내에서 siso_reg 변수에 데이터를 저장하는 과정에서 순차적으로 진행되기 때문에 siso_reg [0] ~ siso_reg [3]는 모두 동일한 값을 갖는다.
  • 따라서 회로적으로나 논리적으로 심각한 오류를 발생시킨다.

 

< Source, Non-blocking >

// SISO Shift Register.
module SISO_Shift_Register_Insert_Negative (
    input clk, enable, reset_p, 
    input d,
    output reg q );
    
    reg [3:0] siso_reg;
    
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) q = 0;
        else if(enable) begin
            siso_reg[3] = siso_reg[2];
            siso_reg[2] = siso_reg[1];
            siso_reg[1] = siso_reg[0];
            siso_reg[0] = d;
        end
    end  
    
endmodule

 

 

< Source, Non-blocking, Shift Operator 대신에 결합 연산자를 사용하여 구현 >

// SISO Shift Register.
// MSB 자리에 입력 값,d을 append하고,
// LSB 자리의 값을 출력값으로 내보낸다.
module SISO_Shift_Register_Positive(
    input clk, enable, reset_p,
    input d,
    output reg q  );
    
    reg [3:0] siso_reg;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) siso_reg <= 0;
        else if(enable) siso_reg <= {d, siso_reg[3:1]};
    end
    
    assign q = siso_reg[0];
    
endmodule

 

 

< Source, Non-blocking, Shift Operator 대신에 결합 연산자를 사용하여 구현 + Parameter 사용 >

// SISO Shift Register by using parameter.
module SISO_Shift_Register_By_Using_Parameter #(parameter N = 8)(
    input clk, enable, reset_p,
    input d,
    output q );
    
    reg [N-1: 0] siso_reg;
    
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) siso_reg <= 0;
        else if(enable) siso_reg <= {1'b0 , siso_reg[N-1:1]};
    end
    
    assign q = siso_reg[0];
    
endmodule

 

 

 

 

 

 

4. Test Bench

  • 우리는 위 SISO Shift Register의 시뮬레이션을 실행하여 결과값을 확인하기 위해서는 입력값, d에 4bit 값을 하나씩 넣어봐야 한다.
  • 그러나, 이는 굉장히 귀찮고, 만약 32bit 데이터를 넣어야 한다면, 얼마나 힘들까?
  • 이러한 상황에서 사용할 수 있는 것이 Test Bench 이다.
  • Test Bench는 시뮬레이션 환경 구성, 자동화 테스트 지원을 소스 코드로 정의할 수 있는 도구이다.

 

 

4.1. Test Bench는 어떻게 만들 수 있는가?

  • add Source 버튼 클릭
  • Add or create simulation source 클릭 
  • Test bench 명 설정후, 파일 생성 (일반적으로 tb 키워드를 붙어 test bench임을 표시한다.)

 

 

4.2. Test Bench의 내부 구성은 어떻게 만들 수 있는가?

  • 1단계) module의 입출력의 자료형을 정의한다.
               (입력 변수의 자료형 : reg, 출력 변수의 자료형 : wire)
  • 2단계) module의 instance를 생성
  • 3단계) Initialization of Simulation 를 정의
  • 4단계) always문을 이용하여 Clock Pulse를 정의
  • 5단계) Processing of Simulation (시뮬레이션의 과정)을 정의

 

 

4.3. 4bit SISO Shift Register를 Test Bench로 구현

 

< Source >

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/07/15 20:24:35
// Design Name: 
// Module Name: tb_SISO_4bit_Shift_Register
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module tb_SISO_4bit_Shift_Register( );
    // module의 Input / Ouput 자료형 선언 
    // module의 input = reg
    // module 의 output  = wire
    reg clk, enable, reset_p, d;
    wire q;
    
    // Register에 저장할 값,데이터
    parameter data = 4'b1101;
    
    // Create instance
    // DUT (Design Under Test) 라고 주로 Instance 명을 설정
    SISO_Shift_Register_By_Using_Parameter #(.N(4)) DUT(
    clk, enable, reset_p, d, q );
    
    // Initializtaion of simulation 
    initial begin 
        clk = 0; enable = 1; reset_p = 1; d = 0;
     end
    
    // Set clock pulse
    always #5 clk = ~clk;
    
    // 변수 선언 
    integer i;
    
    // Processing of simulation 
    initial begin
        #10;
        
        reset_p = 0; 
        
        // insert data in register.
        for(i = 0; i <4; i = i+1) begin
            d = data[i]; #10;
        end
        
        // Delay time
        #40;
        
        // End of Simulation
        $finish;
    end

endmodule

 

< Simulation >

  • clk (Clock Pulse)가 Positive edge일 때, 레지스터에 저장하고자 하는 데이터인 data로 부터 입력단자,d를 통해 하나씩 데이터를 입력 받아서 저장한다.
  • 입력과 동시에 출력이 발생한다. 
  • Q) 왜 데이터 입력과 동시에 출력이 발생하는가?
    A) assign q = siso_reg[0]; 명령문은 어떤 조건 없이 무조건 실행되기 때문에 모듈이 실행될 때마다 siso_reg[0]을 출력한다.

 

 

4.4 Analysis of source code 

  • `timescale 1ns / 1ps : 시뮬레이션의 타입 비율을 정의
                                          (1ns은 클럭 신호의 단위, 1ps은 시뮬레이션 시간 단위)

    → 1ns / 1ps 라고 정의하고, #5 는 5ns를 의미, #15 는 15ns를 의미
    2ns / 1ps 라고 정의하고, #5 는 10ns를 의미, #15 는 30ns를 의미

  • Q) 모듈의 입력값은 reg 형, 출력값은 wire 형으로 정의하였는가?
    A) 모듈의 입력값은 SISO Register내에서 데이터 값으로 활용되지만, 출력값은 Register의 출력값으로 활용하기 때문에 모듈의 입력값은 reg 형, 출력값은 wire 형으로 정의하였다.

  • 상수 값은 parameter 키워드, int형 변수는 integer 키워드를 사용하여 정의한다.
    - 단, 상수 및 변수 선언은 module 안에서 선언 되지만, initial 안에서는 선언할 수 없다.
    - 즉, 상수 및 변수 선언은 module과 initial 사이에서 선언해야 한다.

  • Instance 명은 DUT (Design Under Timer)이라고 주로 명명한다.

  • 초기 시뮬레이션 설정 (Initialization of Simulation)은 변수 값을 설정한다.
    - Q) 초기 시뮬레이션 설정내에서 설정 후, delay time을 주면 안되는가?
    - A) 일반적으로 초기 시뮬레이션 설정 범위내에서는 변수 값을 정의하고, 나중에 시뮬레이션 진행
          과정에서 delay time을 주는 것이 일반적이다.


    ★★★★★★
  • 아래 Clock  Pulse 설정은 어떤 의미를 담고, 어떻게 동작하는가?
     always #5 clk = ~ clk;

    - always 문은 C언어의 while 문과 동일한 역활을 수행
    - #5 clk = ~ clk;  :  클럭을 5ns 주기로 클럭의 상태 값을 Toggle 시키라는 의미
                                   따라서 위 코드를 통해 시뮬레이션이 진행되는 동안 주기 10ns인 clock pulse
                                   신호가 생성된다.

  • Q) 두 번째 initial 문은 첫 번째 initial 문과 어떤 차이점을 갖는가?
    A) 첫 번째 initial 문은 시뮬레이션 실행전 초기 세팅값 설정을 하게 되고,
        두 번째 initial 문은 시물레이션이 동작되는 동안의 상태 및 관리를 하게 된다.


  • Q) 두 번째 initial 문 내에서 초기 #10 (= 10ns)을 주었는가?
    A) 시뮬레이션 초기 세팅에 대한 여유 시간을 갖기 위해 10ns 만큼의 delay time을 주는 것이다.

  • $ finish;  : 시뮬레이션의 종료시키는 키워드