![[VerilogHDL] Verification(32bit register, BRAM)](https://img1.daumcdn.net/thumb/R750x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcth4gF%2FbtsHyFT7n8o%2Fbx3CKSjb1AsbzQMKmy9YnK%2Fimg.png)
1. SystemVerilog - 32bit Register
32bit Register 구현
`timescale 1ns / 1ps
module register(
input clk,
input reset,
input [31:0] D,
output [31:0] Q
);
reg [31:0] Q_reg;
// reg [31:0] Q_next;
assign Q = Q_reg;
always @(posedge clk, posedge reset) begin
if(reset) begin
Q_reg <= 0;
end else begin
Q_reg <= D;
end
end
// always @(*) begin
// Q_next = D;
// end
endmodule
1-2. Verification
위와 같이 생성자를 반복문 안에 넣었을 때 문제가 생기지 않을까?
trans = new()
할 때마다 메모리에 공간을 할당하면 Memory leak이 발생하지 않을까??
→ Garbage collection이 동작해서 메모리에 trasaction class instance Data가 자동으로 정리된다.
environment 만들기
class environment; // OOP AP_main같은 느낌...(각 class 인스턴스화, 초기화, task 실행)
generator gen;
driver drv;
monitor mon;
scoreboard scb;
mailbox #(transaction) gen2drv_mbox;
mailbox #(transaction) mon2scb_mbox;
event gen_next_event;
function new(virtual reg_interface reg_intf);
gen2drv_mbox = new();
mon2scb_mbox = new();
gen = new(gen2drv_mbox, gen_next_event);
drv = new(gen2drv_mbox, reg_intf);
mon = new(mon2scb_mbox, reg_intf);
scb = new(mon2scb_mbox, gen_next_event);
endfunction
task report();
$display("=============================");
$display("== Final Report ==");
$display("=============================");
$display("Total Test : %d", scb.total_cnt);
$display("Pass Count : %d", scb.pass_cnt);
$display("Fail Count : %d", scb.fail_cnt);
$display("=============================");
$display("== test bench is finished! ==");
$display("=============================");
endtask
task pre_run();
drv.reset();
endtask
task run();
fork
gen.run(20);
drv.run();
mon.run();
scb.run();
join_any
report();
#10 $finish;
endtask
task run_test();
pre_run();
run();
endtask
endclass
구현 : driver, monitor 입출력 클럭 동기화(posedge사용)
`timescale 1ns / 1ps
interface reg_interface;
logic clk;
logic reset;
logic [31:0] D;
logic [31:0] Q;
endinterface
class transaction;
rand logic [31:0] data;
logic [31:0] out;
task display(string name);
$display("[%s] data: %x, out: %x", name, data, out);
endtask
endclass
class generator;
transaction trans;
mailbox #(transaction) gen2drv_mbox;
event gen_next_event;
// 생성자에 mailbox, event 초기화 추가
function new(mailbox#(transaction) gen2drv_mbox, event gen_next_event);
this.gen2drv_mbox = gen2drv_mbox;
this.gen_next_event = gen_next_event;
endfunction
task run(int count); // 매개변수로 몇번 반복할지
repeat (count) begin
trans = new(); // transaction 인스턴스화를 run task 안에서 진행
// Garbage collection이 동작해서 메모리에 trasaction class instance Data가 자동으로 정리된다.
assert (trans.randomize())
else $error("[GEN] trans.randomize() error!");
gen2drv_mbox.put(trans);
trans.display("GEN");
@(gen_next_event);
end
endtask
endclass
class driver;
transaction trans;
mailbox #(transaction) gen2drv_mbox;
virtual reg_interface reg_intf;
function new(mailbox#(transaction) gen2drv_mbox,
virtual reg_interface reg_intf);
this.gen2drv_mbox = gen2drv_mbox;
this.reg_intf = reg_intf;
endfunction
task reset();
reg_intf.D <= 0;
reg_intf.reset <= 1'b1;
repeat (5) @(posedge reg_intf.clk);
reg_intf.reset <= 1'b0;
endtask
task run();
forever begin
// @(posedge reg_intf.clk);
gen2drv_mbox.get(trans);
reg_intf.D <= trans.data; // input
trans.display("DRV");
@(posedge reg_intf.clk); // 여기 지나면 출력
// driver와 monitor 입, 출력 동기화
end
endtask
endclass
class monitor;
transaction trans;
mailbox #(transaction) mon2scb_mbox;
virtual reg_interface reg_intf;
function new(mailbox#(transaction) mon2scb_mbox,
virtual reg_interface reg_intf);
this.mon2scb_mbox = mon2scb_mbox;
this.reg_intf = reg_intf;
endfunction
task run();
forever begin
trans = new();
@(posedge reg_intf.clk);
trans.data = reg_intf.D;
@(posedge reg_intf.clk);
trans.out = reg_intf.Q;
mon2scb_mbox.put(trans);
trans.display("MON");
end
endtask
endclass
class scoreboard;
transaction trans;
mailbox #(transaction) mon2scb_mbox;
event gen_next_event;
int total_cnt, pass_cnt, fail_cnt;
function new(mailbox#(transaction) mon2scb_mbox, event gen_next_event);
this.mon2scb_mbox = mon2scb_mbox;
this.gen_next_event = gen_next_event;
total_cnt = 0;
pass_cnt = 0;
fail_cnt = 0;
endfunction
task run();
forever begin
mon2scb_mbox.get(trans);
trans.display("SCB");
if (trans.data == trans.out) begin
$display(" ---> PASS! %x == %x", trans.data, trans.out);
pass_cnt++;
end else begin
$display(" ---> FAIL! %x != %x", trans.data, trans.out);
fail_cnt++;
end
total_cnt++;
->gen_next_event;
end
endtask
endclass
class environment; // OOP AP_main같은 느낌...(각 class 인스턴스화, 초기화, task 실행)
generator gen;
driver drv;
monitor mon;
scoreboard scb;
mailbox #(transaction) gen2drv_mbox;
mailbox #(transaction) mon2scb_mbox;
event gen_next_event;
function new(virtual reg_interface reg_intf);
gen2drv_mbox = new();
mon2scb_mbox = new();
gen = new(gen2drv_mbox, gen_next_event);
drv = new(gen2drv_mbox, reg_intf);
mon = new(mon2scb_mbox, reg_intf);
scb = new(mon2scb_mbox, gen_next_event);
endfunction
task report();
$display("=============================");
$display("== Final Report ==");
$display("=============================");
$display("Total Test : %d", scb.total_cnt);
$display("Pass Count : %d", scb.pass_cnt);
$display("Fail Count : %d", scb.fail_cnt);
$display("=============================");
$display("== test bench is finished! ==");
$display("=============================");
endtask
task pre_run();
drv.reset();
endtask
task run();
fork
gen.run(100);
drv.run();
mon.run();
scb.run();
join_any
report();
#10 $finish;
endtask
task run_test();
pre_run();
run();
endtask
endclass
module tb_register ();
environment env;
reg_interface reg_intf (); // interface instantiation
register dut (
.clk(reg_intf.clk),
.reset(reg_intf.reset),
.D(reg_intf.D),
.Q(reg_intf.Q)
);
always #5 reg_intf.clk = ~reg_intf.clk;
initial begin
reg_intf.clk = 0;
end
initial begin
env = new(reg_intf);
env.run_test();
end
endmodule
결과
구현2 : driver, monitor 입출력 클럭 동기화(event 사용)
`timescale 1ns / 1ps
interface reg_interface;
logic clk;
logic reset;
logic [31:0] D;
logic [31:0] Q;
endinterface
class transaction;
rand logic [31:0] data;
logic [31:0] out;
task display(string name);
$display("[%s] data: %x, out: %x", name, data, out);
endtask
endclass
class generator;
transaction trans;
mailbox #(transaction) gen2drv_mbox;
event gen_next_event;
// 생성자에 mailbox, event 초기화 추가
function new(mailbox#(transaction) gen2drv_mbox, event gen_next_event);
this.gen2drv_mbox = gen2drv_mbox;
this.gen_next_event = gen_next_event;
endfunction
task run(int count); // 매개변수로 몇번 반복할지
repeat (count) begin
trans = new(); // transaction 인스턴스화를 run task 안에서 진행
// Garbage collection이 동작해서 메모리에 trasaction class instance Data가 자동으로 정리된다.
assert (trans.randomize())
else $error("[GEN] trans.randomize() error!");
gen2drv_mbox.put(trans);
trans.display("GEN");
@(gen_next_event);
end
endtask
endclass
class driver;
transaction trans;
mailbox #(transaction) gen2drv_mbox;
event drv_next_event;
virtual reg_interface reg_intf;
function new(mailbox#(transaction) gen2drv_mbox,
virtual reg_interface reg_intf, event drv_next_event);
this.gen2drv_mbox = gen2drv_mbox;
this.reg_intf = reg_intf;
this.drv_next_event = drv_next_event;
endfunction
task reset();
reg_intf.D <= 0;
reg_intf.reset <= 1'b1;
repeat (5) @(posedge reg_intf.clk);
reg_intf.reset <= 1'b0;
endtask
task run();
forever begin
// @(posedge reg_intf.clk);
gen2drv_mbox.get(trans);
reg_intf.D <= trans.data; // input
trans.display("DRV");
@(posedge reg_intf.clk); // 여기 지나면 출력
// driver와 monitor 입, 출력 동기화
-> drv_next_event; // event를 이용한 입출력 동기화
end
endtask
endclass
class monitor;
transaction trans;
mailbox #(transaction) mon2scb_mbox;
event drv_next_event;
virtual reg_interface reg_intf;
function new(mailbox#(transaction) mon2scb_mbox,
virtual reg_interface reg_intf, event drv_next_event);
this.mon2scb_mbox = mon2scb_mbox;
this.reg_intf = reg_intf;
this.drv_next_event = drv_next_event;
endfunction
task run();
forever begin
@(drv_next_event);
trans = new();
// @(posedge reg_intf.clk);
trans.data = reg_intf.D;
@(posedge reg_intf.clk);
trans.out = reg_intf.Q;
mon2scb_mbox.put(trans);
trans.display("MON");
end
endtask
endclass
class scoreboard;
transaction trans;
mailbox #(transaction) mon2scb_mbox;
event gen_next_event;
int total_cnt, pass_cnt, fail_cnt;
function new(mailbox#(transaction) mon2scb_mbox, event gen_next_event);
this.mon2scb_mbox = mon2scb_mbox;
this.gen_next_event = gen_next_event;
total_cnt = 0;
pass_cnt = 0;
fail_cnt = 0;
endfunction
task run();
forever begin
mon2scb_mbox.get(trans);
trans.display("SCB");
if (trans.data == trans.out) begin
$display(" ---> PASS! %x == %x", trans.data, trans.out);
pass_cnt++;
end else begin
$display(" ---> FAIL! %x != %x", trans.data, trans.out);
fail_cnt++;
end
total_cnt++;
->gen_next_event;
end
endtask
endclass
class environment; // OOP AP_main같은 느낌...(각 class 인스턴스화, 초기화, task 실행)
generator gen;
driver drv;
monitor mon;
scoreboard scb;
mailbox #(transaction) gen2drv_mbox;
mailbox #(transaction) mon2scb_mbox;
event gen_next_event;
event drv_next_event;
function new(virtual reg_interface reg_intf);
gen2drv_mbox = new();
mon2scb_mbox = new();
gen = new(gen2drv_mbox, gen_next_event);
drv = new(gen2drv_mbox, reg_intf, drv_next_event);
mon = new(mon2scb_mbox, reg_intf, drv_next_event);
scb = new(mon2scb_mbox, gen_next_event);
endfunction
task report();
$display("=============================");
$display("== Final Report ==");
$display("=============================");
$display("Total Test : %d", scb.total_cnt);
$display("Pass Count : %d", scb.pass_cnt);
$display("Fail Count : %d", scb.fail_cnt);
$display("=============================");
$display("== test bench is finished! ==");
$display("=============================");
endtask
task pre_run();
drv.reset();
endtask
task run();
fork
gen.run(20);
drv.run();
mon.run();
scb.run();
join_any
report();
#10 $finish;
endtask
task run_test();
pre_run();
run();
endtask
endclass
module tb_register ();
environment env;
reg_interface reg_intf (); // interface instantiation
register dut (
.clk(reg_intf.clk),
.reset(reg_intf.reset),
.D(reg_intf.D),
.Q(reg_intf.Q)
);
always #5 reg_intf.clk = ~reg_intf.clk;
initial begin
reg_intf.clk = 0;
end
initial begin
env = new(reg_intf);
env.run_test();
end
endmodule
GitHub - k1minseok/SystemVerilog_BRAM_0522: BRAM Verification
BRAM Verification. Contribute to k1minseok/SystemVerilog_BRAM_0522 development by creating an account on GitHub.
github.com
결과
2. SystemVerilog - Block RAM(≈ SRAM)
- RW = 0일때, write mode이며 입력한 wrie data가 입력한 address에 저장된다.
- RW = 1일때, read mode이며 입력한 address의 저장된 값이 read data로 출력된다.
구현
`timescale 1ns / 1ps
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]; // 8bit짜리 메모리 공간 n개
integer i;
initial begin // 메모리 값 초기화
for (i = 0; i < 2 ** 10 - 1; i = i + 1) begin
mem[i] = 0;
end
end
always @(posedge clk) begin
if (!wr_en) begin
mem[address] <= wdata;
end
end
assign rdata = mem[address];
endmodule
2-1. Verification
`timescale 1ns / 1ps
interface ram_interface;
logic clk;
logic wr_en;
logic [9:0] addr;
logic [7:0] wdata;
logic [7:0] rdata;
endinterface
class transaction;
rand bit wr_en; // bit : 2state type
rand bit [9:0] addr;
rand bit [7:0] wdata;
bit [7:0] rdata;
task display(string name);
$display("[%s] wr_en: %x, addr: %x, wdata: %x, rdata: %x", name, wr_en,
addr, wdata, rdata);
endtask
// 제약사항 설정 -> 랜덤 값 범위 설정 가능 : 이름은 상관 없음
// constraint c_addr {addr < 10;}
constraint c_addr {addr inside {[10:19]};} // addr = 10 ~ 19 범위
constraint c_wdata1 {wdata < 100;}
constraint c_wdata2 {wdata > 10;}
// constraint c_wr_en {wr_en dist {0:=100, 1:=110};} // 0~100까지 0, 101~110까지 1
constraint c_wr_en {wr_en dist {0:/80, 1:/20};} // wr_en 0,1비율 60%, 40%
endclass
class generator;
transaction trans;
mailbox #(transaction) gen2drv_mbox;
event gen_next_event;
function new(mailbox#(transaction) gen2drv_mbox, event gen_next_event);
this.gen2drv_mbox = gen2drv_mbox;
this.gen_next_event = gen_next_event;
endfunction
task run(int count);
repeat(count) begin // count만큼 계속 반복되면 transaction이 메모리에
// count 숫자만큼 할당될 거 같지만 garbage collection이 동작해서
// 메모리에 있는 transaction class instance data가 자동으로
// 정리된다
trans = new();
assert (trans.randomize())
else $error("[GEN] trans.radomize() error!");
gen2drv_mbox.put(trans);
trans.display("GEN");
@(gen_next_event); //
end
endtask
endclass
class driver;
transaction trans;
mailbox #(transaction) gen2drv_mbox;
virtual ram_interface ram_intf;
function new(virtual ram_interface ram_intf,
mailbox#(transaction) gen2drv_mbox);
this.ram_intf = ram_intf;
this.gen2drv_mbox = gen2drv_mbox;
endfunction
task reset (); // task reset -> driver에는 보통 초기화 해주는 코드가 들어감
ram_intf.wr_en <= 0;
ram_intf.addr <= 0;
ram_intf.wdata <= 0;
repeat (5) @(posedge ram_intf.clk);
endtask
task run();
forever begin
gen2drv_mbox.get(trans);
ram_intf.wr_en <= trans.wr_en;
ram_intf.addr <= trans.addr;
ram_intf.wdata <= trans.wdata;
// if (trans.wr_en) begin // read
// ram_intf.wr_en <= trans.wr_en;
// ram_intf.addr <= trans.addr;
// end else begin // write
// ram_intf.wr_en <= trans.wr_en;
// ram_intf.addr <= trans.addr;
// ram_intf.wdata <= trans.wdata;
// end
trans.display("DRV");
@(posedge ram_intf.clk);
end
endtask
endclass
class monitor;
transaction trans;
mailbox #(transaction) mon2scb_mbox;
virtual ram_interface ram_intf;
function new(virtual ram_interface ram_intf,
mailbox#(transaction) mon2scb_mbox);
this.ram_intf = ram_intf;
this.mon2scb_mbox = mon2scb_mbox;
endfunction
task run();
forever begin
trans = new();
@(posedge ram_intf.clk);
trans.wr_en = ram_intf.wr_en;
trans.addr = ram_intf.addr;
trans.wdata = ram_intf.wdata;
trans.rdata = ram_intf.rdata;
mon2scb_mbox.put(trans);
trans.display("MON");
end
endtask
endclass
class scoreboard;
transaction trans;
mailbox #(transaction) mon2scb_mbox;
event gen_next_event;
int total_cnt, pass_cnt, fail_cnt, write_cnt;
logic [7:0] mem[0:2**10-1]; // test용 메모리
function new(mailbox#(transaction) mon2scb_mbox, event gen_next_event);
this.mon2scb_mbox = mon2scb_mbox;
this.gen_next_event = gen_next_event;
total_cnt = 0;
pass_cnt = 0;
fail_cnt = 0;
write_cnt = 0;
for (int i = 0; i < 2 ** 10 - 1; i++) begin
mem[i] = 0;
end
endfunction
task run();
forever begin
mon2scb_mbox.get(trans);
trans.display("SCB");
if (trans.wr_en) begin //read
if (mem[trans.addr] == trans.rdata) begin
$display(" --> READ PASS! mem[%x] == %x", mem[trans.addr],
trans.rdata);
pass_cnt++;
end else begin
$display(" --> READ FAIL! mem[%x] == %x", mem[trans.addr],
trans.rdata);
fail_cnt++;
end
// 초기 logic mem값에는 데이터가 저장되어 있지 않음(초기화했기때문에 값 모두 0)
// dut의 reg mem도 같음 -> write하기 전까지 0 == 0 으로 비교되고
// write한 후 제대로 저장 값과 출력 값이 보임
end else begin //write
mem[trans.addr] = trans.wdata;
$display(" --> WRITE! mem[%x] == %x", trans.addr, trans.wdata);
write_cnt++;
end
total_cnt++;
->gen_next_event;
end
endtask
endclass
class environment;
generator gen;
driver drv;
monitor mon;
scoreboard scb;
event gen_next_event;
mailbox #(transaction) gen2drv_mbox;
mailbox #(transaction) mon2scb_mbox;
function new(virtual ram_interface ram_intf);
gen2drv_mbox = new();
mon2scb_mbox = new();
gen = new(gen2drv_mbox, gen_next_event);
drv = new(ram_intf, gen2drv_mbox);
mon = new(ram_intf, mon2scb_mbox);
scb = new(mon2scb_mbox, gen_next_event);
endfunction
task pre_run();
drv.reset();
endtask
task report();
$display("================================");
$display(" Final Report ");
$display("================================");
$display("Total Test : %d", scb.total_cnt);
$display("READ Pass Count : %d", scb.pass_cnt);
$display("READ Fail Count : %d", scb.fail_cnt);
$display("WRITE Count : %d", scb.write_cnt);
$display("================================");
$display(" test bench is finished! ");
$display("================================");
endtask
task run();
fork
gen.run(10000);
drv.run();
mon.run();
scb.run();
join_any
report();
#10 $finish;
endtask
task run_test();
pre_run();
run();
endtask
endclass
// test bench
// 초기화, 동작 코드
module tb_ram ();
environment env;
ram_interface ram_intf ();
ram dut (
.clk(ram_intf.clk),
.address(ram_intf.addr),
.wdata(ram_intf.wdata),
.wr_en(ram_intf.wr_en), // write enable
.rdata(ram_intf.rdata)
);
always #5 ram_intf.clk = ~ram_intf.clk;
initial begin
ram_intf.clk = 0;
end
initial begin
env = new(ram_intf);
env.run_test();
end
endmodule
GitHub - k1minseok/SystemVerilog_16bitRegister_0521: Register Verification
Register Verification. Contribute to k1minseok/SystemVerilog_16bitRegister_0521 development by creating an account on GitHub.
github.com
결과
위 시뮬레이션 결과의 콘솔창에서 generator와 driver의 rdata
는 따로 설정을 안했기 때문에 0이 나오고
monitor와 scoreboard에서는 DUT의 출력(read data)가 나온다.
class transaction;
rand bit wr_en;
rand bit [9:0] addr;
rand bit [7:0] wdata;
bit [7:0] rdata;
task display(string name);
$display("[%s] wr_en: %x, addr: %x, wdata: %x, rdata: %x", name, wr_en,
addr, wdata, rdata);
endtask
// 제약사항 설정 -> 랜덤 값 범위 설정 가능 : 이름은 상관 없음
// constraint c_addr {addr < 10;}
constraint c_addr {addr inside {[10:19]};} // addr = 10 ~ 19 범위
constraint c_wdata1 {wdata < 100;}
constraint c_wdata2 {wdata > 10;}
// constraint c_wr_en {wr_en dist {0:=100, 1:=110};} // 0~100까지 0, 101~110까지 1
constraint c_wr_en {wr_en dist {0:/80, 1:/20};} // wr_en 0,1비율 60%, 40%
endclass
constraint <name> {제약 조건}
: 제약 사항 설정
- 은 아무거나 넣어도 상관없다.
- 제약 조건은 랜덤 값의 조건(범위)를 설정한다. → 같은 변수에 대한 제약조건은 AND
rand
& randc
- rand : 무작위 랜덤
- randc : 순환 랜덤 ← 경우의 수에 중복이 없음(같은 값이 나오지 않음)
Made By Minseok KIM
'VerilogHDL > Study' 카테고리의 다른 글
[VerilogHDL] CPU 기본 구조, 메모리, Counter 설계(Control Unit, Data Path) (1) | 2024.06.03 |
---|---|
[VerilogHDL] FIFO, UART&FIFO (0) | 2024.05.30 |
[VerilogHDL] System Verilog 기초 (0) | 2024.05.22 |
[VerilogHDL] UART Rx, Tx 최종(Oversampling) (0) | 2024.05.22 |
[VerilogHDL] UART Tx(2) (0) | 2024.05.22 |
Let's Be Happy!
도움이 되었으면 좋겠어요 :)