반도체 설계에서 검증(Verification)이 중요한 이유는 한번 칩을 만들기 위해 엄청난 시간과 돈과 자원이 필요하다. 만약 잘못 설계된 칩을 알아차리지 못하고 생산하게 된다면 매우 큰 자원을 낭비하게 되는것이다. 이를 방지하기 위해 설계단계에서 simulation을 통해 검증하여 미리 결함과 오류를 체크하고 개선할 수 있다.
System Verilog는 복잡한 테스트벤치 구조와 random stimulus를 사용하여 설계를 검증하도록하는 Verilog의 확장 언어다.
검증은 testbench화경에서 DUT(Design Under Test)를 인스턴스하고, 입력과 출력포트가 적절한 testbench구성요소와 연결된다. 다양한 stimulus로 testbench를 구동하고, 출력 결과와 예상값을 비교하여 제대로 작동하는지 확인한다.
위 그림은 system verilog를 이용한 testbench환경을 간략하게 표현한 것이다.
generator, driver, monitor, scoreboard는 각각의 객체이며 class를 통해 선언된다.
- Generator : 테스트를 위한 data를 생성하고, 생성된 data를 transaction으로 저장
- Driver : transaction값을 interface로 전달, sw-data를 hw-신호로 전달하는 느낌
- Monitor : interface값을 transaction으로 변환, hw-신호를 sw-data로 전달하는 느낌
- Scoreboard : transaction값을 받아와 출력과 비교하여 pass, fail 판단
transaction은 테스트를 위한 data묶음으로 mailbox(Queue)를 통해 공유된다.
interface는 hw신호의 묶음이다.
설계의 검증을 위해 System Verilog를 사용하는 이유는 크게 2가지가 있다.
1. randomized stimulus
2. OOP (캡슐화, 상속, 다형성)
module adder (
input clk,
input reset,
input valid,
input [3:0] a,
input [3:0] b,
output [3:0] sum,
output carry
);
reg [3:0] sum_reg, sum_next;
reg carry_reg, carry_next;
assign sum = sum_reg;
assign carry = carry_reg;
// register (f/f)
always @(posedge clk, posedge reset) begin
if (reset) begin
sum_reg <= 0;
carry_reg <= 1'b0;
end else begin
sum_reg <= sum_next;
carry_reg <= carry_next;
end
end
// combinational logic circuit
always @(*) begin
carry_next = carry_reg;
sum_next = sum_reg;
if (valid) begin
{carry_next, sum_next} = a + b;
end
end
endmodule
valid신호가 왔을 때 a와 b를 더하여 {carry_next, sum_next}로 저장
task run();
forever begin
mon2scb_mbox.get(trans);
trans.display("SCB");
if ((trans.a + trans.b) == {trans.carry, trans.sum}) begin // (trans.a + trans.b) <- golden reference, reference model
$display(" --> PASS ! %d + %d = %d", trans.a, trans.b, {
trans.carry, trans.sum});
pass_cnt++;
end else begin
$display(" --> FAIL ! %d + %d = %d", trans.a, trans.b, {
trans.carry, trans.sum});
fail_cnt++;
end
total_cnt++;
->genNextEvent; // generator trigger
end
endtask
a와 b를 더한 값과 출력결과인 sum과 carry를 비트결합 연산자로 합친 값을 비교하여 Pass, Fail 판단
module register (
input clk,
input reset,
input [31:0] d,
output [31:0] q
);
reg [31:0] q_reg;
assign q = q_reg;
always @(posedge clk, posedge reset) begin
if (reset) begin
q_reg <= 0;
end else begin
q_reg <= d;
end
end
endmodule
module ram (
input clk,
input [9:0] address,
input [7:0] wdata,
input wr_en,
output [7:0] rdata
);
reg [7:0] mem[0:2**10-1]; // width:8, depth:1024
integer i;
initial begin
for (i = 0; i < 2 ** 10; i = i + 1) begin
mem[i] = 0; // default 0으로 초기화
end
end
always @(posedge clk) begin
if (!wr_en) begin
mem[address] <= wdata;
end
end
assign rdata = mem[address];
endmodule
wr_en에 따라 0은 write동작, 1은 read동작을 수행한다.