blob: 16d4617340d060c738e15140ff06eee871ba9ac3 [file] [log] [blame]
// This test read all trace CSV, and collect functional coverage from the instruction trace
class riscv_instr_cov_test extends uvm_test;
typedef uvm_enum_wrapper#(riscv_instr_name_t) instr_enum;
typedef uvm_enum_wrapper#(riscv_reg_t) gpr_enum;
typedef uvm_enum_wrapper#(privileged_reg_t) preg_enum;
riscv_instr_gen_config cfg;
riscv_instr_cover_group instr_cg;
riscv_instr_cov_item instr;
string trace_csv[$];
string trace[string];
int unsigned entry_cnt;
int unsigned total_entry_cnt;
int unsigned skipped_cnt;
int unsigned illegal_instr_cnt;
`uvm_component_utils(riscv_instr_cov_test)
`uvm_component_new
task run_phase(uvm_phase phase);
int i;
string args;
string csv;
string line;
string header[$];
string entry[$];
int fd;
while(1) begin
args = {$sformatf("trace_csv_%0d", i), "=%s"};
if ($value$plusargs(args, csv)) begin
trace_csv.push_back(csv);
end else begin
break;
end
i++;
end
cfg = riscv_instr_gen_config::type_id::create("cfg");
cfg.build_instruction_template(.skip_instr_exclusion(1));
instr = riscv_instr_cov_item::type_id::create("instr");
instr.rand_mode(0);
instr.no_hint_illegal_instr_c.constraint_mode(0);
instr.imm_val_c.constraint_mode(0);
instr_cg = new(cfg);
`uvm_info(`gfn, $sformatf("%0d CSV trace files to be processed", trace_csv.size()), UVM_LOW)
foreach (trace_csv[i]) begin
entry_cnt = 0;
instr_cg.reset();
`uvm_info(`gfn, $sformatf("Processing CSV trace[%0d]: %s", i, trace_csv[i]), UVM_LOW)
fd = $fopen(trace_csv[i], "r");
if (fd) begin
// Get the header line
if ($fgets(line, fd)) begin
split_string(line, ",", header);
`uvm_info(`gfn, $sformatf("Header: %0s", line), UVM_HIGH);
end else begin
`uvm_info(`gfn, $sformatf("Skipping empty trace file: %0s", trace_csv[i]), UVM_LOW)
continue;
end
while ($fgets(line, fd)) begin
split_string(line, ",", entry);
if (entry.size() != header.size()) begin
`uvm_info(`gfn, $sformatf("Skipping malformed entry[%0d] : %0s", entry_cnt, line), UVM_LOW)
skipped_cnt += 1;
end else begin
trace["csv_entry"] = line;
foreach (header[j]) begin
trace[header[j]] = entry[j];
end
post_process_trace();
if (trace["instr"] inside {"li", "ret", "la"}) continue;
if (uvm_is_match("amo*",trace["instr"]) ||
uvm_is_match("lr*" ,trace["instr"]) ||
uvm_is_match("sc*" ,trace["instr"])) begin
// TODO: Enable functional coverage for AMO test
continue;
end
if (!sample()) begin
`uvm_info(`gfn, $sformatf("Found illegal instr: %0s [%0s]",
trace["instr"], line), UVM_LOW)
end
end
entry_cnt += 1;
end
end else begin
`uvm_error(`gfn, $sformatf("%0s cannot be openned", trace_csv[i]))
end
`uvm_info(`gfn, $sformatf("[%s] : %0d instructions processed",
trace_csv[i], entry_cnt), UVM_LOW)
total_entry_cnt += entry_cnt;
end
`uvm_info(`gfn, $sformatf("Finished processing %0d trace CSV, %0d instructions",
trace_csv.size(), total_entry_cnt), UVM_LOW)
if ((skipped_cnt > 0) || (illegal_instr_cnt > 0)) begin
`uvm_error(`gfn, $sformatf("%0d instructions skipped, %0d illegal instruction",
skipped_cnt, illegal_instr_cnt))
end else begin
`uvm_info(`gfn, "TEST PASSED", UVM_NONE);
end
endtask
virtual function void post_process_trace();
endfunction
function bit sample();
riscv_instr_name_t instr_name;
bit [XLEN-1:0] val;
if (instr_enum::from_name(process_instr_name(trace["instr"]), instr_name)) begin
if (cfg.instr_template.exists(instr_name)) begin
instr.copy_base_instr(cfg.instr_template[instr_name]);
assign_trace_info_to_instr(instr);
instr.pre_sample();
instr_cg.sample(instr);
return 1'b1;
end
end
get_val(trace["binary"], val);
if ((val[1:0] != 2'b11) && (RV32C inside {supported_isa})) begin
instr_cg.compressed_opcode_cg.sample(val[15:0]);
instr_cg.illegal_compressed_instr_cg.sample(val);
end
if (val[1:0] == 2'b11) begin
`uvm_info("DBG", $sformatf("Sample illegal opcode: %0x [%0s]",
val[6:2], trace["instr_str"]), UVM_LOW)
instr_cg.opcode_cg.sample(val[6:2]);
end
illegal_instr_cnt++;
return 1'b0;
endfunction
virtual function void assign_trace_info_to_instr(riscv_instr_cov_item instr);
riscv_reg_t gpr;
privileged_reg_t preg;
get_val(trace["addr"], instr.pc);
get_val(trace["binary"], instr.binary);
instr.trace = trace["str"];
if (instr.instr_name inside {ECALL, EBREAK, FENCE, FENCE_I, NOP,
C_NOP, WFI, MRET, C_EBREAK}) begin
return;
end
if (instr.has_rs2) begin
if (get_gpr(trace["rs2"], gpr)) begin
instr.rs2 = gpr;
get_val(trace["rs2_val"], instr.rs2_value);
end else begin
`uvm_error(`gfn, $sformatf("Unrecoganized rs2: [%0s] (%0s)",
trace["rs2"], trace["csv_entry"]))
end
end
if (instr.has_rd) begin
if (get_gpr(trace["rd"], gpr)) begin
instr.rd = gpr;
get_val(trace["rd_val"], instr.rd_value);
end else begin
`uvm_error(`gfn, $sformatf("Unrecoganized rd: [%0s] (%0s)",
trace["rd"], trace["csv_entry"]))
end
end
if (instr.has_rs1) begin
if (instr.format inside {CI_FORMAT, CR_FORMAT} &&
!(instr.instr_name inside {C_JR, C_JALR})) begin
instr.rs1 = instr.rd;
end else begin
if (get_gpr(trace["rs1"], gpr)) begin
instr.rs1 = gpr;
get_val(trace["rs1_val"], instr.rs1_value);
end else begin
`uvm_error(`gfn, $sformatf("Unrecoganized rs1: [%0s] (%0s)",
trace["rs1"], trace["csv_entry"]))
end
end
end
if (instr.has_imm) begin
get_val(trace["imm"], instr.imm);
end
if (instr.category == CSR) begin
if (preg_enum::from_name(trace["csr"].toupper(), preg)) begin
instr.csr = preg;
end else begin
get_val(trace["csr"], instr.csr);
end
end
if (instr.category inside {LOAD, STORE}) begin
if (XLEN == 32) begin
instr.mem_addr = instr.rs1_value + instr.imm;
end else begin
bit [XLEN-32-1:0] padding;
if (instr.imm[31]) begin
padding = '1;
end else begin
padding = '0;
end
instr.mem_addr = instr.rs1_value + {padding, instr.imm};
end
end
endfunction
function bit get_gpr(input string str, output riscv_reg_t gpr);
str = str.toupper();
if (gpr_enum::from_name(str, gpr)) begin
return 1'b1;
end else begin
return 1'b0;
end
endfunction
function void get_val(input string str, output bit [XLEN-1:0] val);
val = str.atohex();
endfunction
function string process_instr_name(string instr_name);
instr_name = instr_name.toupper();
foreach (instr_name[i]) begin
if (instr_name[i] == ".") begin
instr_name[i] = "_";
end
end
return instr_name;
endfunction
function void split_string(string str, byte step, ref string result[$]);
string tmp_str;
int i;
bit in_quote;
result = {};
while (i < str.len()) begin
if (str[i] == "\"") begin
in_quote = ~in_quote;
end else if ((str[i] == step) && !in_quote) begin
result.push_back(tmp_str);
tmp_str = "";
end else begin
tmp_str = {tmp_str, str[i]};
end
if (i == str.len()-1) begin
result.push_back(tmp_str);
end
i++;
end
endfunction
endclass