The UVM Primer-Chapter1: Introduction and DUT

Posted by nick5449 on Sat, 05 Feb 2022 03:48:15 +0100

1. Introduction

The UVM Primer is an introductory book on UVM, written and published by Ray Salemi. It is a free and open source project. The code can be downloaded from GitHub. This book is only more than 100 pages long. It is a step-by-step in-depth study around TinyALU. It is very suitable for beginners of UVM. Of course, it also needs to have the relevant foundation of SystemVerilog.

The UVM in this book needs to understand the following basic concepts:

  • Object oriented OOP of SystemVerilog

  • For dynamically generated objects, you can specify the tests and testbench architecture without recompiling

  • Layered testbench composed of Agent, Driver, Monitor and BFM

  • Transaction level communication (TLM) between objects

  • Separation of testbench sequences and testbench structure

2. DUT

This project will learn UVM through a simple DUT, TinyALU, focusing on the verification platform rather than the design itself. TinyALU is a simple ALU written in VHDL. It receives two 8-bit data (A and B) and generates a 16 bit output result. Here is the top layer of TinyALU:

ALU samples at the rising edge of the clock. When the start signal is valid, TinyALU will read the operands from the A and B buses, read the instructions from the op bus, and then generate the results according to the instructions. Instructions can be clock cycles of any length. TinyALU pulls up the done signal when the instruction is completed.

reset_ The N signal is a low effective, synchronous reset signal.

TinyALU has five instructions: NOP, ADD, AND, XOR AND MULT. During calculation, the 3-bit bus op needs to be coded. The coding table is as follows:

Here is the waveform for the TinyALU:

The start signal needs to be kept high, and the opcode and operand should be kept stable before TinyALU pulls up the done signal. The done signal only pulls up one clock cycle. NOP command has no done signal. In NOP, the requester pulls down the start signal after it is high for one cycle.

The subsequent chapters will build the verification environment platform in turn. Each chapter will make some changes to make it more and more UVM!

Some source codes of Tiny ALU are attached:

// 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

 

 

Topics: microchip SOC uvm