《The UVM Primer》——Chapter1: Introduction and DUT
1. 引言《The UVM Primer》 是 一本UVM 的入门书籍,由Ray Salemi编写出版,并且是免费开源的项目,代码可以在GitHub上下载。这本书只有一百多页的篇幅,围绕TinyALU进行一步步的深入学习,非常适合初学UVM的新手。当然也需要有SystemVerilog的相关基础。本书的UVM需要了解以下几个基本概念:SystemVerilog 的面向对象OOP动态生成的对象,可在
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
更多推荐
所有评论(0)