《The UVM Primer》——Chapter1: Introduction and DUT

1. 引言

《The UVM Primer》 是 一本UVM 的入门书籍,由Ray Salemi编写出版,并且是免费开源的项目,代码可以在GitHub上下载。这本书只有一百多页的篇幅,围绕TinyALU进行一步步的深入学习,非常适合初学UVM的新手。当然也需要有SystemVerilog的相关基础。

本书的UVM需要了解以下几个基本概念:

  • SystemVerilog 的面向对象OOP

  • 动态生成的对象,可在不重新编译的情况下指定tests和testbench架构

  • 由 Agent, Driver, Monitor 和 BFM 构成的分层的testbench

  • 对象之间的事务级通信(TLM)

  • Testbench激励(UVM Sequences)与testbench结构的分离


2. DUT

本项目将通过一个简单的DUT即TinyALU,来对UVM展开学习,重点是在验证平台上,而不是设计本身。TinyALU是 VHDL 编写的一个简单的 ALU,它接收两个 8 位数据(A 和 B),并产生 16 位的输出 result。这里是 TinyALU 的顶层:

ALU在时钟上升沿采样,当 start 信号有效时, TinyALU将从 A、B总线上读取操作数, 从 op 总线上读取指令, 然后根据指令生成结果。指令可以是任意长度时钟周期的。TinyALU 在指令完成的时,拉高 done 信号。

reset_n 信号是低有效, 同步的复位信号。

TinyALU有5个指令: NOP、ADD、AND、XOR和MULT。在计算的时候,需对 3 位总线 op 进行编码,编码表如下:

Here is the waveform for the TinyALU:

start 信号需要保持为高, 操作码和操作数在 TinyALU 拉高 done 信号之前,都要保持稳定。done 信号只拉高一个时钟周期。NOP 指令没有 done 信号。在 NOP 中, requester 在 start 信号为高一个周期后将其拉低。


后续章节将依次搭建验证环境平台,每章都会做些改动,使得它变得越来越UVM!

附上Tiny ALU的一些源代码:

// tinyalu.v
module tinyalu(A, B, clk, op, reset_n, start, done, result, bus_valid, bus_op, bus_addr, bus_wr_data, bus_rd_data);
   input [7:0]      A;
   input [7:0]      B;
   input            clk;
   input [2:0]      op;
   input            reset_n;
   input            start;
   output           done;
   output [15:0]    result;

   input            bus_valid;
   input            bus_op;
   input [15:0]     bus_addr;
   input [15:0]     bus_wr_data;
   output reg[15:0]    bus_rd_data;
   
   
   wire             done_aax;
   wire             done_mult;
   wire [15:0]      result_aax;
   wire [15:0]      result_mult;
   reg              start_single;
   reg              start_mult;
   reg              done_internal;
   reg[15:0]        result_internal;

   reg [15:0]       ctrl_reg;
   reg [15:0]       status_reg;
  
   //start_demux
   always @(op[2] or start) begin
      case (op[2])
         1'b0 :
            begin
               start_single <= start;
               start_mult <= 1'b0;
            end
         1'b1 :
            begin
               start_single <= 1'b0;
               start_mult <= start;
            end
         default:
            ;
      endcase
   end
   
   //result_mux
   always @(result_aax or result_mult or op) begin
      case (op[2])
         1'b0 :
            result_internal <= result_aax;
         1'b1 :
            result_internal <= result_mult;
         default :
            result_internal <= {16{1'bx}};
      endcase
   end
   
   //done_mux
   always @(done_aax or done_mult or op) begin
      case (op[2])
         1'b0 :
            done_internal <= done_aax;
         1'b1 :
            done_internal <= done_mult;
         default :
            done_internal <= 1'bx;
      endcase
   end

   //bus write
   always @(posedge clk)begin
       if(!reset_n)begin
           ctrl_reg <= 16'h0;
           status_reg <= 16'h0;
       end
       else if(bus_valid && bus_op)begin
           case(bus_addr)
               16'h8:begin
                   ctrl_reg <= bus_wr_data;
               end
               default:;
           endcase
       end

       if(ctrl_reg[1])begin
           if(A == 8'hff)
               status_reg[0] <= 1'b1;
           else
               status_reg[0] <= 1'b0;
           if(B == 8'hff)
               status_reg[1] <= 1'b1;
           else
               status_reg[1] <= 1'b0;
           if(A == 8'h00)
               status_reg[2] <= 1'b1;
           else
               status_reg[2] <= 1'b0;
           if(B == 8'h00)
               status_reg[3] <= 1'b1;
           else
               status_reg[3] <= 1'b0;
       end
   end

   //bus read
   always @(posedge clk)begin
       if(!reset_n)
           bus_rd_data <= 16'h0;
       else if(bus_valid && !bus_op)begin
           case(bus_addr)
               16'h8:begin
                   bus_rd_data <= ctrl_reg;
               end
               16'h9:begin
                   bus_rd_data <= status_reg;
               end
               default:begin
                   bus_rd_data <= 16'h0;
               end
           endcase
       end
   end

   single_cycle add_and_xor(.A(A), .B(B), .clk(clk), .op(op), .reset_n(reset_n), .start(start_single), .done_aax(done_aax), .result_aax(result_aax));
   
   three_cycle mult(.A(A), .B(B), .clk(clk), .reset_n(reset_n), .start(start_mult), .done_mult(done_mult), .result_mult(result_mult));
   
   assign result = (ctrl_reg[0])? ~result_internal : result_internal;
   assign done = done_internal;
   
endmodule

// single_cycle_add_and_xor.v
module single_cycle(A, B, clk, op, reset_n, start, done_aax, result_axx);
	input [7:0]			A;
	input [7:0]			A;
	input 				clk;
	input [2:0]			op;
	input 				reset_n;
	input 				start;
    output reg			done_axx;
    outpit reg[15:0]	result_axx;


   //single_cycle_ops
   always @(posedge clk) begin
       if(!reset_n)
           result_aax <= 16'd0;
       else begin
           if (start == 1'b1)begin
               case (op)
                  3'b001 :
                     result_aax <= ({8'b00000000, A}) + ({8'b00000000, B});
                  3'b010 :
                     result_aax <= (({8'b00000000, A}) & ({8'b00000000, B}));
                  3'b011 :
                     result_aax <= (({8'b00000000, A}) ^ ({8'b00000000, B}));
                  default :
                    ;
               endcase
            end
            else ;
        end
   end

 //set_done
   always @(posedge clk or negedge reset_n) begin
      if (!reset_n)
         done_aax <= 1'b0;
      else begin
         if ((start == 1'b1) && (op != 3'b000) && (done_aax == 1'b0))
            done_aax <= 1'b1;
         else
            done_aax <= 1'b0;
      end
   end
   
endmodule

// three_cycle_mult.v
module three_cycle(A, B, clk, reset_n, start, done_mult, result_mult);
   input [7:0]      A;
   input [7:0]      B;
   input            clk;
   input            reset_n;
   input            start;
   output           done_mult;
   output reg[15:0]    result_mult;
   
   reg [7:0]        a_int;
   reg [7:0]        b_int;
   reg [15:0]       mult1;
   reg [15:0]       mult2;
   reg              done3;
   reg              done2;
   reg              done1;
   reg              done_mult_int;
   
   //multiplier
   always @(posedge clk or negedge reset_n) begin
      if (!reset_n) begin
         done_mult_int <= 1'b0;
         done3 <= 1'b0;
         done2 <= 1'b0;
         done1 <= 1'b0;
         
         a_int <= 8'd0;
         b_int <= 8'd0;
         mult1 <= 16'd0;
         mult2 <= 16'd0;
         result_mult <= 16'd0;
      end
      else begin
         a_int <= A;
         b_int <= B;
         mult1 <= a_int * b_int;
         mult2 <= mult1;
         result_mult <= mult2;
         done3 <= start & ((~done_mult_int));
         done2 <= done3 & ((~done_mult_int));
         done1 <= done2 & ((~done_mult_int));
         done_mult_int <= done1 & ((~done_mult_int));
      end
   end
   assign done_mult = done_mult_int;
   
endmodule

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UVM(Universal Verification Methodology)中,`uvm_type_id::create`是一个静态函数,用于创建指定类型的对象实例。 `uvm_type_id::create`函数的作用是根据给定的类型标识符(type_id)创建该类型的对象实例。它是UVM中的一种工厂模式,通过类型标识符动态地创建对象,而无需显式调用具体类的构造函数。 使用`uvm_type_id::create`函数可以实现以下功能: 1. 动态创建对象:通过使用类型标识符,可以在运行时根据需要创建对象。这样可以实现在运行时决定对象类型的灵活性。 2. 避免直接调用构造函数:通过使用`uvm_type_id::create`函数,可以避免直接调用对象的构造函数,减少代码的依赖性和耦合度。 3. 多态性支持:UVM中的类通常使用继承关系构建,通过创建对象实例,可以实现多态性的支持。即可以通过基类的类型标识符创建派生类的对象实例。 使用`uvm_type_id::create`函数需要注意以下几点: 1. 类型注册:在使用`uvm_type_id::create`函数之前,需要确保要创建的类型已经在UVM系统中注册过。通常可以通过在类声明中使用`uvm_object_utils`宏来注册类型。 2. 类型转换:由于`uvm_type_id::create`函数返回的是一个基类指针,如果需要使用派生类的特定方法或成员,可能需要进行类型转换。 总结来说,`uvm_type_id::create`函数用于根据类型标识符动态创建对象实例,实现了对象的动态创建和多态性支持。它是UVM中一种灵活和方便的对象创建方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值