본문 바로가기

Verilog HDL 설계

테스트벤치 작성 예시

테스트 벤치는 설계한 회로가 제대로 동작하는지 확인하기 위해서 쓴다. 

 

시뮬레이션을 위해서 일반적으로 필요한 신호는 clock과 reset이다. 그 외 제어신호는 각자 하기 나름이지만, clock과 reset은 sequential circuit에서 무조건적으로 필요하다.

 

`timescale 1ns / 1ps

module tb_cla();

reg clk, rst;
reg [3:0] in_a, in_b;
reg carry_in;
wire [4:0] cla_result;

top TEST(.in_a(in_a), .in_b(in_b), .carry_in(carry_in),
    .clk(clk), .rst(rst), .out(cla_result)
);

initial begin
    clk <= 1;
    rst <= 1;
    in_a <= 4'b0;
    in_b <= 4'b0;
    carry_in <= 1'b0;
    #10
    rst <= 0;
    in_a <= 4'd2;
    in_b <= 4'd3;
    carry_in <= 1'b0;
    #10
    in_a <= 4'd8;
    in_b <= 4'd9;
    carry_in <= 1'b1;
    #100
    $finish;
end

always #5 clk <= ~clk;

endmodule

 

위와 같은 코드에서 알아야 할 것들은

 

1. reg 변수로 탑 레벨 모듈에 입력을 넣어주는 것

2. initial 혹은 always 구문으로 변수 값을 지정

3. clk의 경우 따로 driven 할 것

4. 출력은 wire로 할 것

5. timescale 설정

이다. 

 

1: reg 변수의 특징인 값을 유지한다는 것을 이용해 회로에 넣는 가상의 신호를 만든다는 의미다. 실제 회로가 물리적으로 있다면 당연히 전기 신호로 동작할 텐데, 이 전기 신호를 시뮬레이션에서는 테스트 벤치의 reg 변수로 구현한다. non-blocking(<=)이나 blocking(=) 중에 어떤 걸 쓰느냐는 그렇게 큰 차이가 없지만 가끔 민감한 회로의 경우 차이가 날 때가 있다. 

 

2: initial은 시뮬레이션 시작을 기준으로 내부에 있는 구문을 실행하겠다는 의미다. always는 시뮬레이션 시작부터 항상 내부에 있는 구문을 실행하겠다는 의미다. initial이나 always 전부 $finish를 넣을 수는 있긴 한데, always는 반복적인 테스트를 위함이므로 always에서는 $finish로 시뮬레이션 종료 지점을 설정하지 않는다.

  내부에 delay 구문을 통해 일정 시간동안 신호가 어떻게 변화하는지를 기술하는데, for문 같은 반복문을 안에 넣을 수도 있어서 테스트 벡터 파일 또한 사용이 가능하다.

 

3: initial에서 초기값을 지정하고, 다른 always에서 주기의 2분의 1만큼의 delay마다 반전되도록 설정한다. 합성하는 코드가 아니기 때문에 여러 곳에서 driven해도 괜찮다. initial은 얼마든지 많이 써도 상관은 없다. 여러 변수를 굳이 한 initial에 넣을 필요는 없다.

 

4: 포트의 출력은 항상 wire다. reg로 연결하면 문법 오류다.

 

5: 요새 cpu는 GHz 주파수, 즉 1ns 단위의 주파수를 가진다. timescale 설정이 없으면 ps 단위로 시뮬레이션이 돌아가기 때문에 실제로 회로가 돌아갈 시간과는 다르게 되는 경우가 있다. 

1ns/1ps의 의미는 모든 시간 단위(time control) 구문을 1ns로 하고, 최대 해상도는 1ps라는 의미다. 해상도라는 것은 1.001x에서 x는 0.1ps라서 무시되는 것을 의미한다. 앞에 1ns는 아는데 1ps는 왜 쓰이는지 몰랐었는데 저런 의미라고 한다. 숫자도 1 10 100만 쓸 수 있다.

#10의 딜레이가 있다면 이 경우에는 10ns가 되는 것이다.

나는 보통 시뮬레이션에서 테스트벤치에만 이런 설정을 하는데, 가끔 가다 외부에서 받은 코드에 timescale이 다르면 시뮬레이션 에러가 나곤 한다.

 

모듈 불러오는 건 문법에서 다룬 적이 있어서 넘어간다.

 

reg  [8:0] data [0:2048-1];

initial begin
    $readmemh("./vector.txt", data);
    #(30);
    for (j=0;j<3;j=j+1) begin
        for (i=0;i<2048;i=i+1) 
        begin
            in_data <= data[i];
            #(20);
        end
    end
end

 

만약에 너무 많이 값을 지정해야한다면 테스트 벡터를 쓰는 걸 추천한다. $readmemh는 16진수를 텍스트 파일의 줄 단위로 인식한다. 물론 해당 텍스트 파일을 만드는 걸 손으로 하는 사람은 없을 거라 생각한다. 해당 벡터는 Deinterleaver때 쓴 벡터 파일이라 데이터가 2048줄 있어서 array 선언을 2048개로 해줬다. $readmemb 도 있는데 b라는 말 그대로 binary text data를 인식한다. 텍스트 데이터를 인식하는 명령어라 비트스트림을 읽지는 못한다. 비트스트림은 $fscanf를 써야 한다. $fscanf는 나도 아직 써본 적이 없어서 나중에 한번 써봐야겠다.

아 참고로 for문 안에서 delay는 괄호를 써주고 세미콜론을 넣어줘야 문법 에러가 나지 않는 점 알아두기 바란다.

'Verilog HDL 설계' 카테고리의 다른 글

파라미터를 이용한 모듈 선언  (0) 2022.04.07
Simple Finite State Machine implementation  (0) 2021.04.29
Countdown control & display  (0) 2021.04.23
BCD to 7 segment  (0) 2021.04.19
간단한 Deinterleaver  (0) 2021.04.18