本文主要是介绍《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_demuxalways @(op[2] or start) begincase (op[2])1'b0 :beginstart_single <= start;start_mult <= 1'b0;end1'b1 :beginstart_single <= 1'b0;start_mult <= start;enddefault:;endcaseend//result_muxalways @(result_aax or result_mult or op) begincase (op[2])1'b0 :result_internal <= result_aax;1'b1 :result_internal <= result_mult;default :result_internal <= {16{1'bx}};endcaseend//done_muxalways @(done_aax or done_mult or op) begincase (op[2])1'b0 :done_internal <= done_aax;1'b1 :done_internal <= done_mult;default :done_internal <= 1'bx;endcaseend//bus writealways @(posedge clk)beginif(!reset_n)beginctrl_reg <= 16'h0;status_reg <= 16'h0;endelse if(bus_valid && bus_op)begincase(bus_addr)16'h8:beginctrl_reg <= bus_wr_data;enddefault:;endcaseendif(ctrl_reg[1])beginif(A == 8'hff)status_reg[0] <= 1'b1;elsestatus_reg[0] <= 1'b0;if(B == 8'hff)status_reg[1] <= 1'b1;elsestatus_reg[1] <= 1'b0;if(A == 8'h00)status_reg[2] <= 1'b1;elsestatus_reg[2] <= 1'b0;if(B == 8'h00)status_reg[3] <= 1'b1;elsestatus_reg[3] <= 1'b0;endend//bus readalways @(posedge clk)beginif(!reset_n)bus_rd_data <= 16'h0;else if(bus_valid && !bus_op)begincase(bus_addr)16'h8:beginbus_rd_data <= ctrl_reg;end16'h9:beginbus_rd_data <= status_reg;enddefault:beginbus_rd_data <= 16'h0;endendcaseendendsingle_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_opsalways @(posedge clk) beginif(!reset_n)result_aax <= 16'd0;else beginif (start == 1'b1)begincase (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 :;endcaseendelse ;endend//set_donealways @(posedge clk or negedge reset_n) beginif (!reset_n)done_aax <= 1'b0;else beginif ((start == 1'b1) && (op != 3'b000) && (done_aax == 1'b0))done_aax <= 1'b1;elsedone_aax <= 1'b0;endendendmodule
// 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;//multiplieralways @(posedge clk or negedge reset_n) beginif (!reset_n) begindone_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;endelse begina_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));endendassign done_mult = done_mult_int;endmodule
这篇关于《The UVM Primer》——Chapter1: Introduction and DUT的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!