UVMPrimer中的DUT改写成Verilog

内容:

讲UVMPrimer书给的代码中的DUT改写成了Verilog形式,原来时VHDL形式,用VCS仿真的时候总是出错,花了大半天改写了。

DUT介绍

这本书中的DUT是个ALU,模块管脚如下图,

A,B是两个八位的输入,start是启动信号,op是命令信号,包括加,乘,与等,result是计算结果,done是DUT的应答信号,当dut计算结束,done为1。ALU中的计算有单周期,也有多周期的乘法指令。

op命令的编码如下:

ALU的时序图如下,这就是管脚的协议,设计工程师根据协议来design,验证师根据协议来验证设计是否满足协议。

start有效时,ALU读取op,A,B信号,ALU计算中start一直是高,当ALU计算结束,ALU拉高done信号,此时start可以拉低了,done为高只持续一个周期。

ALU代码:

单周期的指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
module single_alu(
input clk,
input rst_n,
input [7:0] A,
input [7:0] B,
input [2:0] op,
input start,
output done,
output [15:0] result
);

reg[15:0] result_r;
reg done_r;

assign result = result_r;
assign done = done_r;

always@(posedge clk ) begin //synchronize
if(rst_n == 1'b0) begin
result_r <= 'b0;
end
else begin
if(start == 1'b1) begin
case(op)
3'b001:result_r <= A+B;
3'b010:result_r <= A&B;
3'b011:result_r <= A^B;
default: result_r <= 'b0;
endcase
end
end
$display("@ %0t : [single alu] result is %d !!!",$time,result_r);
end

always @(posedge clk or negedge rst_n) begin
if(rst_n == 1'b0) done_r <= 1'b0;
else begin
if(start==1'b1 && op != 3'b000) begin
done_r <= 1'b1;
end
else done_r <= 1'b0;
end
end
endmodule

多周期乘法指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
module mul_alu(
input clk,
input rst_n,
input [7:0] A,
input [7:0] B,
input start,
output done,
output [15:0] result
// 这里没有op信号
);

reg done_r,done1,done2,done3;
reg [15:0] result_r,mul1,mul2;
reg [7:0] a,b;

assign done = done_r;
assign result = result_r;

always@(posedge clk or negedge rst_n) begin
if(rst_n <= 1'b0) begin
done_r <= 'b0;
done1 <= 'b0 ;
done2 <= 'b0 ;
done3 <= 'b0 ;

mul1 <= 'b0;
mul2 <= 'b0;
result_r <= 'b0;
a<=0;
b<=0;
end
else begin
a <= A;
b <= B;
mul1 <= a*b;
mul2 <= mul1;
result_r <= mul2;
done3 <= start && (~done_r);
done2 <= done3 && (~done_r);
done1 <= done2 && (~done_r);
done_r <= done1 &&(~done_r);
end
// $display("@ %0t : [mul alu] result is %d !!!",$time,result_r);
end

endmodule

顶层模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
module tinyalu(
input clk,
input reset_n,
input [7:0] A,
input [7:0] B,
input [2:0] op,
input start,
output done,
output [15:0] result
);

wire [15:0] result_sgl,result_mul;
reg start_sgl,start_mul;
wire done_sgl,done_mul;
reg done_r;
reg [15:0] result_r;

assign done = done_r;
assign result = result_r;

single_alu sgl_u(.clk(clk),.rst_n(reset_n),.A(A),.B(B),.op(op),
.start(start_sgl),.done(done_sgl),.result(result_sgl));

mul_alu mul_u(.clk(clk),.rst_n(reset_n),.A(A),.B(B),.start(start_mul),
.done(done_mul),.result(result_mul));
// 根据op的最高位决定是单周期指令还是多周期指令
always@(op[2],start) begin
case(op[2])
1: begin
start_mul = start;
start_sgl = 0;
end
0: begin
start_mul =0;
start_sgl = start;
end
endcase
end

always@(op[2],result_mul,result_sgl) begin
case(op[2])
1: begin
result_r = result_mul;
end
0: begin
result_r = result_sgl;
end
default result_r = 0;
endcase
end
always@(op[2],done_mul,done_sgl) begin
case(op[2])
1: begin
done_r = done_mul;
end
0: begin
done_r = done_sgl;
end
default done_r = 0;
endcase
end
endmodule

所有文件获取

把DUT和验证环境放在了GitHub上,地址

原来

错误:

编码时候出现了一些错误。

  1. 定向思维错误。

    在连续写多个always过程语句时候,把相似的敏感列表写成了一样的了。这种错误常常发生,就是脑子木了,惯性的敲代码,没过脑子。

  2. 给信号result增加寄存器形式result_r,result_r用在过程语句中。result_r的宽度跟result不一致,经常忘了定义宽度。

  3. 修改了信号,但是没有改全。

  4. 在tb上例化DUT时,连线信号变了,但是例化DUT部分没有改变这个信号。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!