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

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐