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