mirror of
https://github.com/retroSoC/common.git
synced 2026-11-06 15:05:14 +00:00
361 lines
13 KiB
Systemverilog
361 lines
13 KiB
Systemverilog
// Copyright (c) 2023 Beijing Institute of Open Source Chip
|
|
// common is licensed under Mulan PSL v2.
|
|
// You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
// You may obtain a copy of Mulan PSL v2 at:
|
|
// http://license.coscl.org.cn/MulanPSL2
|
|
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
// See the Mulan PSL v2 for more details.
|
|
|
|
`ifndef INC_AXI4_MASTER_SV
|
|
`define INC_AXI4_MASTER_SV
|
|
|
|
`include "setting.sv"
|
|
`include "helper.sv"
|
|
`include "test_base.sv"
|
|
`include "axi4_define.sv"
|
|
|
|
class AXI4Master extends TestBase;
|
|
string name;
|
|
logic [`AXI4_DATA_WIDTH-1:0] rd_data[$];
|
|
logic [`AXI4_DATA_WIDTH-1:0] wr_data[$];
|
|
virtual axi4_if.master axi4;
|
|
|
|
extern function new(string name = "axi4_master", virtual axi4_if.master axi4);
|
|
extern function automatic bit [`AXI4_WSTRB_WIDTH-1:0] calc_strb(
|
|
input bit [`AXI4_WSTRB_WIDTH-1:0] addr, input bit [2:0] size);
|
|
extern function automatic bit [`AXI4_ADDR_WIDTH-1:0] calc_addr(
|
|
input bit [`AXI4_WSTRB_WIDTH-1:0] addr, input bit [2:0] size, input bit [1:0] burst);
|
|
extern task automatic init();
|
|
extern task automatic write(
|
|
input bit [`AXI4_ID_WIDTH-1:0] id, input bit [`AXI4_ADDR_WIDTH-1:0] addr, input bit [7:0] len,
|
|
input bit [2:0] size, input bit [1:0] burst, input bit [`AXI4_DATA_WIDTH-1:0] data[$]);
|
|
|
|
extern task automatic read(input bit [`AXI4_ID_WIDTH-1:0] id,
|
|
input bit [`AXI4_ADDR_WIDTH-1:0] addr, input bit [7:0] len,
|
|
input bit [2:0] size, input bit [1:0] burst);
|
|
// extern task automatic wr_rd_check(input bit [31:0] addr, string name, input bit [63:0] data,
|
|
// input Helper::cmp_t cmp_type,
|
|
// input Helper::log_lev_t log_level = Helper::NORM);
|
|
extern task automatic wr_check(
|
|
input bit [`AXI4_ID_WIDTH-1:0] id, input bit [`AXI4_ADDR_WIDTH-1:0] addr, input bit [7:0] len,
|
|
input bit [2:0] size, input bit [1:0] burst, input bit [`AXI4_DATA_WIDTH-1:0] data[$],
|
|
input bit [`AXI4_DATA_WIDTH-1:0] ref_data[$], input Helper::cmp_t cmp_type,
|
|
input Helper::log_lev_t log_level = Helper::NORM);
|
|
|
|
extern task automatic rd_check(
|
|
input bit [`AXI4_ID_WIDTH-1:0] id, input bit [`AXI4_ADDR_WIDTH-1:0] addr, input bit [7:0] len,
|
|
input bit [2:0] size, input bit [1:0] burst, input bit [`AXI4_DATA_WIDTH-1:0] ref_data[$],
|
|
input Helper::cmp_t cmp_type, input Helper::log_lev_t log_level = Helper::NORM);
|
|
endclass
|
|
|
|
function AXI4Master::new(string name, virtual axi4_if.master axi4);
|
|
super.new();
|
|
this.name = name;
|
|
this.rd_data = {};
|
|
this.wr_data = {};
|
|
this.axi4 = axi4;
|
|
endfunction
|
|
|
|
task automatic AXI4Master::init();
|
|
this.axi4.awid = '0;
|
|
this.axi4.awaddr = 'x;
|
|
this.axi4.awlen = '0;
|
|
this.axi4.awsize = `AXI4_BURST_SIZE_1BYTE;
|
|
this.axi4.awburst = `AXI4_BURST_TYPE_FIXED;
|
|
this.axi4.awlock = `AXI4_LOCK_NORM;
|
|
this.axi4.awcache = `AXI4_CACHE_NO_BUF;
|
|
this.axi4.awprot = `AXI4_PROT_NORMAL;
|
|
this.axi4.awqos = `AXI4_QOS_NORMAL;
|
|
this.axi4.awregion = `AXI4_REGION_NORMAL;
|
|
this.axi4.awvalid = '0;
|
|
|
|
this.axi4.wdata = 'x;
|
|
this.axi4.wstrb = '0;
|
|
this.axi4.wlast = '0;
|
|
this.axi4.wuser = '0;
|
|
this.axi4.wvalid = '0;
|
|
|
|
this.axi4.bready = '0;
|
|
|
|
this.axi4.arid = '0;
|
|
this.axi4.araddr = 'x;
|
|
this.axi4.arlen = '0;
|
|
this.axi4.arsize = `AXI4_BURST_SIZE_1BYTE;
|
|
this.axi4.arburst = `AXI4_BURST_TYPE_FIXED;
|
|
this.axi4.arlock = `AXI4_LOCK_NORM;
|
|
this.axi4.arcache = `AXI4_CACHE_NO_BUF;
|
|
this.axi4.arprot = `AXI4_PROT_NORMAL;
|
|
this.axi4.arqos = `AXI4_QOS_NORMAL;
|
|
this.axi4.arregion = `AXI4_REGION_NORMAL;
|
|
this.axi4.arvalid = '0;
|
|
|
|
this.axi4.rready = '0;
|
|
|
|
Helper::print("axi4 master device init done");
|
|
endtask
|
|
|
|
function automatic bit [`AXI4_WSTRB_WIDTH-1:0] AXI4Master::calc_strb(
|
|
input bit [`AXI4_WSTRB_WIDTH-1:0] addr, input bit [2:0] size);
|
|
|
|
bit [`AXI4_WSTRB_WIDTH-1:0] align_strb = '0;
|
|
int ofset = addr % `AXI4_WSTRB_WIDTH;
|
|
unique case (size)
|
|
`AXI4_BURST_SIZE_1BYTE: align_strb = {1{1'b1}};
|
|
`AXI4_BURST_SIZE_2BYTES: align_strb = {2{1'b1}};
|
|
`AXI4_BURST_SIZE_4BYTES: align_strb = {4{1'b1}};
|
|
`AXI4_BURST_SIZE_8BYTES: align_strb = {8{1'b1}};
|
|
default: align_strb = '0;
|
|
endcase
|
|
|
|
return align_strb << ofset;
|
|
endfunction
|
|
|
|
function automatic bit [`AXI4_ADDR_WIDTH-1:0] AXI4Master::calc_addr(
|
|
input bit [`AXI4_WSTRB_WIDTH-1:0] addr, input bit [2:0] size, input bit [1:0] burst);
|
|
|
|
int ofset;
|
|
unique case (burst)
|
|
`AXI4_BURST_TYPE_FIXED: return addr;
|
|
`AXI4_BURST_TYPE_INCR: begin
|
|
ofset = addr % `AXI4_WSTRB_WIDTH;
|
|
if ((ofset + (1 << size)) <= `AXI4_WSTRB_WIDTH) begin
|
|
return addr + (1 << size);
|
|
end else begin
|
|
return addr + `AXI4_WSTRB_WIDTH - (1 << size); // TODO: right?
|
|
end
|
|
end
|
|
`AXI4_BURST_TYPE_WRAP: begin
|
|
$display("no support now");
|
|
return addr;
|
|
end
|
|
`AXI4_BURST_TYPE_RESV: begin
|
|
$display("error burst type");
|
|
return addr;
|
|
end
|
|
endcase
|
|
|
|
endfunction
|
|
|
|
|
|
task automatic AXI4Master::write(
|
|
input bit [`AXI4_ID_WIDTH-1:0] id, input bit [`AXI4_ADDR_WIDTH-1:0] addr, input bit [7:0] len,
|
|
input bit [2:0] size, input bit [1:0] burst, input bit [`AXI4_DATA_WIDTH-1:0] data[$]);
|
|
|
|
bit [ `AXI4_ADDR_WIDTH-1:0] tmp_addr;
|
|
bit [`AXI4_WSTRB_WIDTH-1:0] tmp_strb;
|
|
|
|
// aw channel
|
|
@(posedge this.axi4.aclk);
|
|
#`REGISTER_DELAY;
|
|
this.axi4.awid = id;
|
|
this.axi4.awaddr = addr;
|
|
this.axi4.awlen = len - 1;
|
|
this.axi4.awsize = size;
|
|
this.axi4.awburst = burst;
|
|
this.axi4.awvalid = 1'b1;
|
|
|
|
@(posedge this.axi4.aclk);
|
|
while (~this.axi4.awready) begin
|
|
@(posedge this.axi4.aclk);
|
|
end
|
|
#`REGISTER_DELAY;
|
|
// $display("%t aw trigger", $time);
|
|
this.axi4.awid = '0;
|
|
this.axi4.awaddr = 'x;
|
|
this.axi4.awlen = '0;
|
|
this.axi4.awsize = `AXI4_BURST_SIZE_1BYTE;
|
|
this.axi4.awburst = `AXI4_BURST_TYPE_FIXED;
|
|
this.axi4.awvalid = '0;
|
|
@(negedge this.axi4.aclk);
|
|
#`REGISTER_DELAY;
|
|
// w burst channel
|
|
tmp_addr = addr;
|
|
for (int i = 0; i < len; i++) begin
|
|
this.axi4.wdata = data.pop_front();
|
|
this.axi4.wstrb = this.calc_strb(tmp_addr, size);
|
|
tmp_addr = this.calc_addr(tmp_addr, size, burst);
|
|
this.axi4.wlast = i == len - 1;
|
|
this.axi4.wvalid = 1'b1;
|
|
@(posedge this.axi4.aclk);
|
|
while (~this.axi4.wready) begin
|
|
@(posedge this.axi4.aclk);
|
|
end
|
|
#`REGISTER_DELAY;
|
|
// $display("%t w burst trigger", $time);
|
|
end
|
|
|
|
this.axi4.wdata = 'x;
|
|
this.axi4.wstrb = '0;
|
|
this.axi4.wlast = '0;
|
|
this.axi4.wvalid = '0;
|
|
@(negedge this.axi4.aclk);
|
|
|
|
// b channel
|
|
this.axi4.bready = 1'b1;
|
|
@(posedge this.axi4.aclk);
|
|
while (~this.axi4.bvalid) begin
|
|
@(posedge this.axi4.aclk);
|
|
end
|
|
|
|
if (this.axi4.bid != id) begin
|
|
$error("%t [wr mismatch id] awid is %d, bid: %d", $time, id, this.axi4.bid);
|
|
end else begin
|
|
unique case (this.axi4.bresp)
|
|
`AXI4_RESP_OKAY: begin
|
|
end
|
|
`AXI4_RESP_EXOKAY: $display("%t EXOKAY", $time);
|
|
`AXI4_RESP_SLAVE_ERROR: $display("%t SLVERR", $time);
|
|
`AXI4_RESP_DECODE_ERROR: $display("%t DECERR", $time);
|
|
endcase
|
|
end
|
|
#`REGISTER_DELAY;
|
|
this.axi4.bready = 1'b0;
|
|
@(negedge this.axi4.aclk);
|
|
endtask
|
|
|
|
task automatic AXI4Master::read(input bit [`AXI4_ID_WIDTH-1:0] id,
|
|
input bit [`AXI4_ADDR_WIDTH-1:0] addr, input bit [7:0] len,
|
|
input bit [2:0] size, input bit [1:0] burst);
|
|
bit [ `AXI4_ADDR_WIDTH-1:0] tmp_addr;
|
|
bit [`AXI4_WSTRB_WIDTH-1:0] tmp_strb;
|
|
bit [ `AXI4_DATA_WIDTH-1:0] tmp_mask;
|
|
this.rd_data = {};
|
|
// ar channel
|
|
@(posedge this.axi4.aclk);
|
|
#`REGISTER_DELAY;
|
|
this.axi4.arid = id;
|
|
this.axi4.araddr = addr;
|
|
this.axi4.arlen = len - 1;
|
|
this.axi4.arsize = size;
|
|
this.axi4.arburst = burst;
|
|
this.axi4.arvalid = 1'b1;
|
|
|
|
@(posedge this.axi4.aclk);
|
|
while (~this.axi4.arready) begin
|
|
@(posedge this.axi4.aclk);
|
|
end
|
|
#`REGISTER_DELAY;
|
|
// $display("%t ar trigger", $time);
|
|
this.axi4.arid = '0;
|
|
this.axi4.araddr = 'x;
|
|
this.axi4.arlen = '0;
|
|
this.axi4.arsize = `AXI4_BURST_SIZE_1BYTE;
|
|
this.axi4.arburst = `AXI4_BURST_TYPE_FIXED;
|
|
this.axi4.arvalid = '0;
|
|
@(negedge this.axi4.aclk);
|
|
|
|
// r burst channel
|
|
tmp_addr = addr;
|
|
@(posedge this.axi4.aclk);
|
|
#`REGISTER_DELAY;
|
|
this.axi4.rready = 1'b1;
|
|
for (int i = 0; i < len; i++) begin
|
|
@(posedge this.axi4.aclk);
|
|
while (~this.axi4.rvalid) begin
|
|
@(posedge this.axi4.aclk);
|
|
end
|
|
tmp_strb = this.calc_strb(tmp_addr, size);
|
|
for (int j = 0; j < `AXI4_WSTRB_WIDTH; j++) begin
|
|
tmp_mask[j*8+:8] = {8{tmp_strb[j]}};
|
|
end
|
|
// $display("%t: this.axi4.rdata: %h", $time, this.axi4.rdata);
|
|
this.rd_data.push_back(this.axi4.rdata & tmp_mask);
|
|
if (this.axi4.rid != id) begin
|
|
$error("%t [rd mismatch id] arid is %d, rid: %d", $time, id, this.axi4.rid);
|
|
end
|
|
|
|
if (i == len - 1 && ~axi4.rlast) begin
|
|
$error("%t [rd error last]", $time, id);
|
|
end else begin
|
|
@(negedge this.axi4.aclk);
|
|
tmp_addr = this.calc_addr(tmp_addr, size, burst);
|
|
end
|
|
#`REGISTER_DELAY;
|
|
end
|
|
|
|
this.axi4.rready = 1'b0;
|
|
@(negedge this.axi4.aclk);
|
|
endtask
|
|
|
|
// task automatic AXI4Master::wr_rd_check(input bit [31:0] addr, string name, input bit [63:0] data,
|
|
// input Helper::cmp_t cmp_type,
|
|
// input Helper::log_lev_t log_level = Helper::NORM);
|
|
// this.wr_data = data;
|
|
// this.write(addr, this.wr_data);
|
|
// this.read(addr);
|
|
// Helper::check(name, this.rd_data, this.wr_data, cmp_type, log_level);
|
|
|
|
// endtask
|
|
|
|
task automatic AXI4Master::wr_check(
|
|
input bit [`AXI4_ID_WIDTH-1:0] id, input bit [`AXI4_ADDR_WIDTH-1:0] addr, input bit [7:0] len,
|
|
input bit [2:0] size, input bit [1:0] burst, input bit [`AXI4_DATA_WIDTH-1:0] data[$],
|
|
input bit [`AXI4_DATA_WIDTH-1:0] ref_data[$], input Helper::cmp_t cmp_type,
|
|
input Helper::log_lev_t log_level = Helper::NORM);
|
|
|
|
this.wr_data = data;
|
|
this.write(id, addr, len, size, burst, this.wr_data);
|
|
Helper::check_queue(name, this.wr_data, ref_data, cmp_type, log_level);
|
|
endtask
|
|
|
|
task automatic AXI4Master::rd_check(
|
|
input bit [`AXI4_ID_WIDTH-1:0] id, input bit [`AXI4_ADDR_WIDTH-1:0] addr, input bit [7:0] len,
|
|
input bit [2:0] size, input bit [1:0] burst, input bit [`AXI4_DATA_WIDTH-1:0] ref_data[$],
|
|
input Helper::cmp_t cmp_type, input Helper::log_lev_t log_level = Helper::NORM);
|
|
bit [ `AXI4_DATA_WIDTH-1:0] filter_ref_data[$] = {};
|
|
bit [ `AXI4_ADDR_WIDTH-1:0] nxt_addr;
|
|
bit [`AXI4_WSTRB_WIDTH-1:0] ofset;
|
|
|
|
this.read(id, addr, len, size, burst);
|
|
|
|
nxt_addr = addr;
|
|
foreach (ref_data[i]) begin
|
|
ofset = nxt_addr % `AXI4_WSTRB_WIDTH;
|
|
|
|
unique case (size)
|
|
`AXI4_BURST_SIZE_1BYTE: begin
|
|
unique case (ofset[2:0])
|
|
3'b000: filter_ref_data[i] = {56'b0, ref_data[i][7:0]};
|
|
3'b001: filter_ref_data[i] = {48'b0, ref_data[i][15:8], 8'b0};
|
|
3'b010: filter_ref_data[i] = {40'b0, ref_data[i][23:16], 16'b0};
|
|
3'b011: filter_ref_data[i] = {32'b0, ref_data[i][31:24], 24'b0};
|
|
3'b100: filter_ref_data[i] = {24'b0, ref_data[i][39:32], 32'b0};
|
|
3'b101: filter_ref_data[i] = {16'b0, ref_data[i][47:40], 40'b0};
|
|
3'b110: filter_ref_data[i] = {8'b0, ref_data[i][55:48], 48'b0};
|
|
3'b111: filter_ref_data[i] = {ref_data[i][63:56], 56'b0};
|
|
endcase
|
|
end
|
|
`AXI4_BURST_SIZE_2BYTES: begin
|
|
unique case (ofset[2:1])
|
|
2'b00: filter_ref_data[i] = {48'b0, ref_data[i][15:0]};
|
|
2'b01: filter_ref_data[i] = {32'b0, ref_data[i][31:16], 16'b0};
|
|
2'b10: filter_ref_data[i] = {16'b0, ref_data[i][47:32], 32'b0};
|
|
2'b11: filter_ref_data[i] = {ref_data[i][63:48], 48'b0};
|
|
endcase
|
|
end
|
|
`AXI4_BURST_SIZE_4BYTES: begin
|
|
unique case (ofset[2])
|
|
1'b0: filter_ref_data[i] = {32'b0, ref_data[i][31:0]};
|
|
1'b1: filter_ref_data[i] = {ref_data[i][63:32], 32'b0};
|
|
endcase
|
|
end
|
|
`AXI4_BURST_SIZE_8BYTES: filter_ref_data[i] = ref_data[i][63:0];
|
|
default: begin
|
|
filter_ref_data[i] = ref_data[i];
|
|
$display("no support now");
|
|
end
|
|
endcase
|
|
|
|
nxt_addr = this.calc_addr(nxt_addr, size, burst);
|
|
end
|
|
|
|
// foreach (this.rd_data[i]) begin
|
|
// $display("rd data: %h", this.rd_data[i]);
|
|
// end
|
|
Helper::check_queue(name, this.rd_data, filter_ref_data, cmp_type, log_level);
|
|
endtask
|
|
|
|
`endif
|