공부방/Verilog_노진호교수님_서울기술교육센터_필기

240521_ TB_ADDER.SV

맘스터치보단파파이스 2024. 5. 21. 16:43

Device Under Test  DUT

interface = H/W신호들 (port list)의 묶음. ==> 케이블같다.

TB : testbench

UVM : universal verification methodology

uvm에서 제동해주는 프레임 ==> env , agent 안에 모니터, 드라이버 등등 드라이버 인터페이스를 통해 모니터를 거쳐 스코어보드로 간다.

transction : test를 위한 데이터의 묶음. 

검증용으로 왜 SV를 사용하나.

--> ramdomized stimulus.

--> class 사용 객체지향언어 C++ 사용. VS verilog _  C 절차지향.

객체지향: OOP

객체지향언어의 3대기능 : 캡슐화(추상화), 상속, 다형 

1. 캡슐화 : 모듈화한다. 

2. 상속  : 재사용성. 새 기능을 사용할 때 기존에 만들어 놓은 것을 사용하는 것.

               확장성 : 기존기능 + 새로운기능 

3. 다형성 : 같은 객체를 다르게 정의할 수 있다. --> 포인터? 같다 객체들을 상속받는 느낌. 

 

interface : 신호들의 묶음. 케이블처럼.

여태껏 연결을 직접해줬어야했는데,

interface를 선언해놓고 ( 신호선들이 묶여있다.)

interface는 system verilog의 문법이고 합성도 가능한 문법이다. 문제는 베릴로그에서 interface는 지원이 안된다. 베릴로그도 일반적으로는 디자인도 많이 하는데 이로 인해 만들어진 DUT는 사용할 수 없고 SV 에서 사용되는 애들한테만 사용이 가능하다.

자료형은 c언어와 비슷비슷.

verilog 에서 추가된 reg 있고 SV에만 있는 logic이 있다 reg, wire 둘다 사용가능한 logic.

간단한 신호전달. C로따지면 Flag --> Set, true 등등 표현. 

신호를 줘서 트리거링을 하는 역할을 한다.

@ 와 wait은 같은 뜻.

 

객체 1, 2 대기+ 실행 --> 순서를 정한다.

동기화방식으로 된다.

트리거링하는 동기화방식이다.

 

4bit  + 4bit ADDER.

 

출력이 8bit --> 어떤건 빨리나가고 어떤건 느리게나가는 상황.

이 상황 다음 next에 도착후에 동시에 clk에 출력해주는 작업이 필요하다.

계산은 combination 에서 하고 ( F/F) 순차회로.

출력을 다시 해주는 이런 회로를 주로 사용한다.

 

아직은 tb_adder.v랑 비슷하다.

`timescale 1ns / 1ps


interface adder_intf;

    logic       clk;
    logic       reset;
    logic       valid;
    logic [3:0] a;
    logic [3:0] b;
    logic [3:0] sum;
    logic       carry;

endinterface  //adder_intf


class transaction;
    rand logic [3:0] a;
    rand logic [3:0] b;
    // rand logic valid;

    task display(string name);
        $display("[%s] a:%d, b:%d", name, a, b);
        //$display("[%s] a:%d, b:%d, vaild:%d", name, a, b, vaild);

    endtask
endclass  //transaction


class generator;
    transaction tr;

    function new();
        tr = new();
    endfunction  //new()

    task run();
        repeat (10) begin
            assert (tr.randomize())
            else $error("tr.randomize() error!");
            tr.display("GEN");
        end
    endtask  //run
endclass  //generator


module tb_adder ();

    // adder_intf adder_if;
    generator  gen;



    // adder dut (
    //     .clk  (adder_if.clk),
    //     .reset(adder_if.reset),
    //     .valid(adder_if.valid),
    //     .a    (adder_if.a),
    //     .b    (adder_if.b),
    //     .sum  (adder_if.sum),
    //     .carry(adder_if.carry)
    // );

    // always #5 adder_if.clk = ~adder_if.clk;

    // initial begin
    //     adder_if.clk   = 1'b0;
    //     adder_if.reset = 1'b1;
    // end

    initial begin
        gen = new();
        gen.run();
    end


endmodule

 일단 돌려본거. interface 부분을 일단 주석처리 해둠 GEN 까지 완성.

다음은 DRIVER

GEN -> driver로 보내기 위해 mailbox(queue)를 사용한다.

clk 1번과 2번 사이가 driver의 task run() 앞부분.

virtual 을 넣으면 가상의 인터페이스. 이게 없으면 하드웨어 묶음이 실제로 생겨버린다.

우리는 ta_adder에 실제 묶음을 만들기위해 virtual 쓴다.

new의 reference 값이 넘어간다.

member.adder_if : reference 값.

virtual 과 물리적 interface를 잘 구분해야함.

new()는 class driver만큼 영역을 heap 영역에 생성.

mailbox가 따로있으니까 공유한다는 느낌이 들지 않는다.

쓰레드 사용하면  원하는대로 동작할 것.

folk_join을 통해 따로 따로 한줄 씩 실행.

`timescale 1ns / 1ps


interface adder_intf;

    logic       clk;
    logic       reset;
    logic       valid;
    logic [3:0] a;
    logic [3:0] b;
    logic [3:0] sum;
    logic       carry;

endinterface  //adder_intf


class transaction;
    rand logic [3:0] a;
    rand logic [3:0] b;
    // rand logic valid;

    task display(string name);
        $display("[%s] a:%d, b:%d", name, a, b);
        //$display("[%s] a:%d, b:%d, valid:%d", name, a, b, valid);

    endtask
endclass  //transaction


class generator;
    transaction tr;
    mailbox #(transaction) gen2drv_mbox;
    event genNextEvent1;
    
    function new();
        tr = new();
    endfunction  //new()

    task run();
        repeat (10) begin
            assert (tr.randomize())
            else $error("tr.randomize() error!");
            gen2drv_mbox.put(tr);
            tr.display("GEN");
            @(genNextEvent1);
        end
    endtask  //run
endclass  //generator

class driver;
    virtual adder_intf adder_if1;
    mailbox #(transaction) gen2drv_mbox;
    transaction trans;
    event genNextEvent2;

    function new(virtual adder_intf adder_if2);
        this.adder_if1 = adder_if2;
    endfunction//new()

    task reset();
        adder_if1.a <= 0;
        adder_if1.b <= 0;
        adder_if1.valid <= 1'b0;
        adder_if1.reset <= 1'b1;
        repeat(5) @(adder_if1.clk);
        adder_if1.reset <= 1'b0;
    endtask //

    task run();
        forever begin
            gen2drv_mbox.get(trans);
            adder_if1.a = trans.a;
            adder_if1.b = trans.b;
            adder_if1.valid = 1'b1;
            trans.display("DRV");
            @(posedge adder_if1.clk);
            adder_if1.valid = 1'b0;
            @(posedge adder_if1.clk);
            ->genNextEvent2;
        end
    endtask
endclass//driver

module tb_adder ();

    adder_intf adder_interface();
    generator  gen;
    driver drv;
    event genNextEvent;
    mailbox #(transaction) gen2drv_mbox;
    



    adder dut (
        .clk  (adder_interface.clk),
        .reset(adder_interface.reset),
        .valid(adder_interface.valid),
        .a    (adder_interface.a),
        .b    (adder_interface.b),
        .sum  (adder_interface.sum),
        .carry(adder_interface.carry)
    );

    always #5 adder_interface.clk = ~adder_interface.clk;

    initial begin
        adder_interface.clk   = 1'b0;
        adder_interface.reset = 1'b1;
    end

    initial begin
        gen2drv_mbox = new();

        gen = new();
        gen.genNextEvent1 = genNextEvent;
        drv = new(adder_interface);
        drv.genNextEvent2 = genNextEvent;

        gen.gen2drv_mbox = gen2drv_mbox;
        drv.gen2drv_mbox = gen2drv_mbox;

        drv.reset();
        fork
            drv.run();
            gen.run();
        join_any
        $display("testbench is finished");
        #10 $finish;
    end


endmodule

 

이젠 monitor 와 scoreboard가 남아있다.

trans.a + trans.b <- reference model. 

해당 값을 golden reference.

`timescale 1ns / 1ps


interface adder_intf;

    logic       clk;
    logic       reset;
    logic       valid;
    logic [3:0] a;
    logic [3:0] b;
    logic [3:0] sum;
    logic       carry;

endinterface  //adder_intf


class transaction;
    rand logic [3:0] a;
    rand logic [3:0] b;
    logic      [3:0] sum;
    logic            carry;
    // rand logic valid;

    task display(string name);
        $display("[%s] a:%d, b:%d, carry:%d, sum:%d", name, a, b, carry, sum);
    endtask
endclass  //transaction


class generator;
    transaction tr;
    mailbox #(transaction) gen2drv_mbox;
    event genNextEvent1;
    
    function new();
        tr = new();
    endfunction  //new()

    task run();
        repeat (10) begin
            assert (tr.randomize())
            else $error("tr.randomize() error!");
            gen2drv_mbox.put(tr);
            tr.display("GEN");
            @(genNextEvent1);
        end
    endtask  //run
endclass  //generator

class driver;
    virtual adder_intf adder_if1;
    mailbox #(transaction) gen2drv_mbox;
    transaction trans;
    event genNextEvent2;
    event monNextEvent2;

    function new(virtual adder_intf adder_if2);
        this.adder_if1 = adder_if2;
    endfunction//new()

    task reset();
        adder_if1.a <= 0;
        adder_if1.b <= 0;
        adder_if1.valid <= 1'b0;
        adder_if1.reset <= 1'b1;
        repeat(5) @(adder_if1.clk);
        adder_if1.reset <= 1'b0;
    endtask //

    task run();
        forever begin
            gen2drv_mbox.get(trans);
            adder_if1.a = trans.a;
            adder_if1.b = trans.b;
            adder_if1.valid = 1'b1;
            trans.display("DRV");
            @(posedge adder_if1.clk);
            adder_if1.valid = 1'b0;          
            @(posedge adder_if1.clk);
            ->monNextEvent2;
            ->genNextEvent2; 
        end
    endtask
endclass//driver

class monitor;
    virtual adder_intf adder_if3;
    mailbox #(transaction) mon2scb_mbox;
    transaction trans;
    event monNextEvent1;

    function new(virtual adder_intf adder_if2);
        this.adder_if3 = adder_if2;
        trans = new();
    endfunction// new()

    task run();
        forever begin
            @(monNextEvent1);
            trans.a = adder_if3.a;
            trans.b = adder_if3.b;
            trans.sum = adder_if3.sum;
            trans.carry = adder_if3.carry;
            mon2scb_mbox.put(trans);
            trans.display("MON");
        end
    endtask //run()
endclass //monitor

class scoreboard;
    mailbox #(transaction) mon2scb_mbox;
    transaction trans;
    
    function new();      
    endfunction //new()

    task run();
        forever begin
            mon2scb_mbox.get(trans);
            trans.display("SCB");
            if(trans.a + trans.b == {trans.carry, trans.sum})begin
                $display(" --> PASS! %d + %d = %d", trans.a, trans.b, {trans.carry, trans.sum});
            end 
            else begin
                $display(" --> FAIL! %d + %d = %d", trans.a, trans.b, {trans.carry, trans.sum});
            end
        end 
    endtask 
endclass //scoreboard

module tb_adder ();

    adder_intf adder_interface();
    generator  gen;
    driver drv;
    monitor mon;
    scoreboard scb;

    event genNextEvent;
    event monNextEvent;
    mailbox #(transaction) gen2drv_mbox;
    mailbox #(transaction) mon2scb_mbox;
    
    adder dut (
        .clk  (adder_interface.clk),
        .reset(adder_interface.reset),
        .valid(adder_interface.valid),
        .a    (adder_interface.a),
        .b    (adder_interface.b),
        .sum  (adder_interface.sum),
        .carry(adder_interface.carry)
    );

    always #5 adder_interface.clk = ~adder_interface.clk;

    initial begin
        adder_interface.clk   = 1'b0;
        adder_interface.reset = 1'b1;
    end

    initial begin
        gen2drv_mbox = new();
        mon2scb_mbox = new();

        gen = new();
        drv = new(adder_interface);
        mon = new(adder_interface);
        scb = new();

        gen.genNextEvent1 = genNextEvent;
        drv.genNextEvent2 = genNextEvent;
        mon.monNextEvent1 = monNextEvent;
        drv.monNextEvent2 = monNextEvent;

        gen.gen2drv_mbox = gen2drv_mbox;
        drv.gen2drv_mbox = gen2drv_mbox;
        mon.mon2scb_mbox = mon2scb_mbox;
        scb.mon2scb_mbox = mon2scb_mbox;

        drv.reset();

        fork
            drv.run();
            gen.run();
            mon.run();
            scb.run();
        join_any

        $display("testbench is finished");
        #10 $finish;
    end
endmodule

 

`timescale 1ns / 1ps


interface adder_intf;

    logic       clk;
    logic       reset;
    logic       valid;
    logic [3:0] a;
    logic [3:0] b;
    logic [3:0] sum;
    logic       carry;

endinterface  //adder_intf


class transaction;
    rand logic [3:0] a;
    rand logic [3:0] b;
    logic      [3:0] sum;
    logic            carry;
    // rand logic valid;

    task display(string name);
        $display("[%s] a:%d, b:%d, carry:%d, sum:%d", name, a, b, carry, sum);
    endtask
endclass  //transaction


class generator;
    transaction tr;
    mailbox #(transaction) gen2drv_mbox;
    event genNextEvent1;
    
    function new();
        tr = new();
    endfunction  //new()

    task run();
        repeat (10) begin
            assert (tr.randomize())
            else $error("tr.randomize() error!");
            gen2drv_mbox.put(tr);
            tr.display("GEN");
            @(genNextEvent1);
        end
    endtask  //run
endclass  //generator

class driver;
    virtual adder_intf adder_if1;
    mailbox #(transaction) gen2drv_mbox;
    transaction trans;
    //event genNextEvent2;
    event monNextEvent2;

    function new(virtual adder_intf adder_if2);
        this.adder_if1 = adder_if2;
    endfunction//new()

    task reset();
        adder_if1.a <= 0;
        adder_if1.b <= 0;
        adder_if1.valid <= 1'b0;
        adder_if1.reset <= 1'b1;
        repeat(5) @(adder_if1.clk);
        adder_if1.reset <= 1'b0;
    endtask //

    task run();
        forever begin
            gen2drv_mbox.get(trans);
            adder_if1.a = trans.a;
            adder_if1.b = trans.b;
            adder_if1.valid = 1'b1;
            trans.display("DRV");
            @(posedge adder_if1.clk);
            adder_if1.valid = 1'b0;          
            @(posedge adder_if1.clk);
            ->monNextEvent2;
            //->genNextEvent2; 
        end
    endtask
endclass//driver

class monitor;
    virtual adder_intf adder_if3;
    mailbox #(transaction) mon2scb_mbox;
    transaction trans;
    event monNextEvent1;

    function new(virtual adder_intf adder_if2);
        this.adder_if3 = adder_if2;
        trans = new();
    endfunction// new()

    task run();
        forever begin
            @(monNextEvent1);
            trans.a = adder_if3.a;
            trans.b = adder_if3.b;
            trans.sum = adder_if3.sum;
            trans.carry = adder_if3.carry;
            mon2scb_mbox.put(trans);
            trans.display("MON");
        end
    endtask //run()
endclass //monitor

class scoreboard;
    mailbox #(transaction) mon2scb_mbox;
    transaction trans;
    event genNextEvent2;

    int total_cnt, pass_cnt, fail_cnt;

    function new();      
    endfunction //new()

    task run();
        forever begin
            mon2scb_mbox.get(trans);
            trans.display("SCB");
            if(trans.a + trans.b == {trans.carry, trans.sum})begin
                $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++;
            ->genNextEvent2;
        end 
    endtask 
endclass //scoreboard

module tb_adder ();

    adder_intf adder_interface();
    generator  gen;
    driver drv;
    monitor mon;
    scoreboard scb;

    event genNextEvent;
    event monNextEvent;
    mailbox #(transaction) gen2drv_mbox;
    mailbox #(transaction) mon2scb_mbox;
    
    adder dut (
        .clk  (adder_interface.clk),
        .reset(adder_interface.reset),
        .valid(adder_interface.valid),
        .a    (adder_interface.a),
        .b    (adder_interface.b),
        .sum  (adder_interface.sum),
        .carry(adder_interface.carry)
    );

    always #5 adder_interface.clk = ~adder_interface.clk;

    initial begin
        adder_interface.clk   = 1'b0;
        adder_interface.reset = 1'b1;
    end

    initial begin
        gen2drv_mbox = new();
        mon2scb_mbox = new();

        gen = new();
        drv = new(adder_interface);
        mon = new(adder_interface);
        scb = new();

        gen.genNextEvent1 = genNextEvent;
        scb.genNextEvent2 = genNextEvent;
        mon.monNextEvent1 = monNextEvent;
        drv.monNextEvent2 = monNextEvent;

        gen.gen2drv_mbox = gen2drv_mbox;
        drv.gen2drv_mbox = gen2drv_mbox;
        mon.mon2scb_mbox = mon2scb_mbox;
        scb.mon2scb_mbox = mon2scb_mbox;

        drv.reset();

        fork
            drv.run();
            gen.run();
            mon.run();
            scb.run();
        join_any
        $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("========testbench is finished=======");
        $display("====================================");
        #10 $finish;
    end
endmodule

 

'공부방 > Verilog_노진호교수님_서울기술교육센터_필기' 카테고리의 다른 글

240522_ BRAM  (0) 2024.05.22
240522_32bit register  (0) 2024.05.22
240520 UART  (0) 2024.05.20
240517 UART  (0) 2024.05.17
240516 FSM_COUNTER , ILA , UART  (0) 2024.05.16