在前几篇文章中,分别介绍了各个模块的设计,本篇文章将会针对k0a_core_top层搭建一个简单的验证环境。
// -------------------------------------------------------------------------------------------------
// Copyright 2024 Kearn Chen, [email protected]
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -------------------------------------------------------------------------------------------------
// Description :
// 1. testbench for simulation
// -------------------------------------------------------------------------------------------------
`timescale 1ns/10ps
module test_top();
reg core_clk ;
reg core_rstn ;
wire bus_avalid ;
wire bus_aready ;
wire bus_write ;
wire [17:0] bus_addr ;
wire [3:0] bus_strb ;
wire [31:0] bus_wdata ;
wire bus_rvalid ;
wire bus_rready ;
wire [31:0] bus_rdata ;
reg [15:0] irq_lines ;
k0a_core_top dut (
.core_clk (core_clk ),
.core_rstn (core_rstn ),
.bus_avalid (bus_avalid ),
.bus_aready (bus_aready ),
.bus_write (bus_write ),
.bus_addr (bus_addr ),
.bus_strb (bus_strb ),
.bus_wdata (bus_wdata ),
.bus_rvalid (bus_rvalid ),
.bus_rready (bus_rready ),
.bus_rdata (bus_rdata ),
.irq_lines (irq_lines )
);
slave_model u_slave (
.clk (core_clk ),
.rstn (core_rstn ),
.avalid (bus_avalid ),
.aready (bus_aready ),
.write (bus_write ),
.addr (bus_addr ),
.strb (bus_strb ),
.wdata (bus_wdata ),
.rvalid (bus_rvalid ),
.rready (bus_rready ),
.rdata (bus_rdata )
);
initial begin
core_clk = 1'b0;
forever #10 core_clk = ~core_clk;
end
initial begin
core_rstn = 1'b0;
irq_lines = 16'd0;
initcase();
#1000;
core_rstn = 1'b1;
testcase();
$finish();
end
`ifdef DUMP_LXT2
initial begin
$dumpfile("test_top.lxt2");
$dumpvars(0, test_top);
end
`endif
`include "testcase.v"
task load_instr(string path);
integer i, fd, ret;
reg [7:0] data;
fd = $fopen($sformatf("../test/%s", path), "rb");
if(fd == 0) begin
$display("Open file : ../test/%s failed!", path);
end
for(i=0; i<131072; i++) begin
u_slave.imem[i] = 32'd0;
end
i = 0;
while($feof(fd) == 0) begin
ret = $fread(data, fd);
u_slave.imem[i/4][(i%4)*8+:8] = data;
i++;
end
$fclose(fd);
endtask
endmodule
在测试顶层代码中,连接了DUT的时钟、复位以及中断信号,总线的丛机模型和内核相连接。同时,提供了一个加载初始化软件代码的任务,验证用例通过load_instr
任务,可加载二进制Bin文件到丛机模型中,在复位结束后,内核开始运行软件代码。
地址 | 寄存器 | 位宽 | 属性 | 描述 |
---|---|---|---|---|
0xC0000 | DATA | 8 | WO | 队列的地址,软件可通过向此地址写入ASCII字符,输出到终端显示,以换行符\n 结尾 |
0xC0004 | FINISH | 32 | WO | 写入数据0x12345678后,仿真运行结束 |
0xC0008 | CLOCK | 32 | RO | 时钟计数器,在复位结束后开始每个时钟周期自增1 |
0xC000C | SPEED | 8 | RW | 模型的总线速度,0:模型会随机拉住总线的ready,1:总线ready一直为1 |
// -------------------------------------------------------------------------------------------------
// Copyright 2024 Kearn Chen, [email protected]
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -------------------------------------------------------------------------------------------------
// Description :
// 1. slave model for simulation
// -------------------------------------------------------------------------------------------------
module slave_model (
input wire clk ,
input wire rstn ,
input wire avalid ,
output wire aready ,
input wire write ,
input wire [17:0] addr ,
input wire [3:0] strb ,
input wire [31:0] wdata ,
output wire rvalid ,
input wire rready ,
output wire [31:0] rdata
);
reg flag;
reg [1:0] cycle;
reg [31:0] dout;
reg [31:0] imem[0:131071];
reg [31:0] dmem[0:65535];
reg [7:0] queue[$];
reg [31:0] clock;
reg speed;
string str;
integer idx;
assign aready = cycle == 3'd0 ? 1'b1 : 1'b0;
assign rvalid = cycle == 3'd0 ? flag : 1'b0;
assign rdata = cycle == 3'd0 ? dout : 32'dx;
always @(posedge clk)
begin
if(avalid & aready & write) begin
if(~addr[17]) begin
if(strb[0]) imem[addr[16:0]][0 +:8] <= wdata[0 +:8];
if(strb[1]) imem[addr[16:0]][8 +:8] <= wdata[8 +:8];
if(strb[2]) imem[addr[16:0]][16+:8] <= wdata[16+:8];
if(strb[3]) imem[addr[16:0]][24+:8] <= wdata[24+:8];
end else if(~addr[16]) begin
if(strb[0]) dmem[addr[15:0]][0 +:8] <= wdata[0 +:8];
if(strb[1]) dmem[addr[15:0]][8 +:8] <= wdata[8 +:8];
if(strb[2]) dmem[addr[15:0]][16+:8] <= wdata[16+:8];
if(strb[3]) dmem[addr[15:0]][24+:8] <= wdata[24+:8];
end
end
end
always @(posedge clk or negedge rstn)
begin
if(!rstn)
flag <= 1'b0;
else if(avalid & aready & ~write)
flag <= 1'b1;
else if(rready & rvalid)
flag <= 1'b0;
end
always @(posedge clk or negedge rstn)
begin
if(!rstn)
cycle <= 2'd0;
else if(|cycle)
cycle <= cycle - 2'd1;
else if(avalid) begin
idx = $urandom_range(0, 99);
if(idx <= 80 || speed == 1'b1)
cycle <= 2'd0;
else if(idx <= 95)
cycle <= 2'd1;
else if(idx <= 97)
cycle <= 2'd2;
else
cycle <= 2'd3;
end
end
always @(posedge clk or negedge rstn)
begin
if(!rstn)
dout <= 32'dx;
else if(avalid & aready & ~write)
if(~addr[17])
dout <= imem[addr[16:0]];
else if(~addr[16])
dout <= dmem[addr[15:0]];
else if(addr[15:0] == 16'h0002)
dout <= clock;
else if(addr[15:0] == 16'h0003)
dout <= {31'd0, speed};
else if(rready & rvalid)
dout <= 32'dx;
end
always @(posedge clk)
begin
if(avalid & aready & write & strb[0] & addr == 18'h30000) begin
if(wdata[7:0] == 8'h0a) begin
str = "";
while(queue.size() > 0) begin
str = $sformatf("%s%c", str, queue.pop_front());
end
$display("[MCU_INFO] : %s", str);
end else begin
queue.push_back(wdata[7:0]);
end
end
end
always @(posedge clk)
begin
if(avalid & aready & write & (&strb) & addr == 18'h30001) begin
if(wdata == 32'h12345678) begin
$finish();
end
end
end
always @(posedge clk or negedge rstn)
begin
if(!rstn)
clock <= 32'd0;
else
clock <= clock + 1'b1;
end
always @(posedge clk or negedge rstn)
begin
if(!rstn)
speed <= 1'b0;
else if(avalid & aready & write & strb[0] & addr == 18'h30003)
speed <= wdata[0];
end
endmodule
脚本支持两套仿真工具,一套为开源的iverilog+gtkwave的组合,另一套为vcs+verdi。
# -------------------------------------------------------------------------------------------------
# Copyright 2024 Kearn Chen, [email protected]
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# -------------------------------------------------------------------------------------------------
# Description :
# 1. Run simulation
# -------------------------------------------------------------------------------------------------
TEST := hello_world_test
vcs:
cp ../case/$(TEST).v testcase.v
make -C ../test/$(TEST)
vcs -full64 -sverilog +vcs+fsdbon -f ../list/filelist.vc
./simv +ntb_random_seed_automatic -l simv.log
iverilog:
cp ../case/$(TEST).v testcase.v
make -C ../test/$(TEST)
iverilog -o simv -f ../list/filelist.vc -g2012 -DDUMP_LXT2
vvp simv -lxt2 -fst
verdi:
verdi -sverilog -f ../list/filelist.vc &
gtkwave:
gtkwave &
clean:
rm -rf simv *.log csrc simv.daidir verdiLog ucli.key novas* *.v
本文介绍了一个简单的验证环境,包括测试顶层及模型、脚本等代码,在后续的文章中会基于此验证环境给出具体的验证用例。