Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ sources:
- ref_model_csim/rtl/fpu_refmodel_pkg.sv
- env/fpu_env_pkg.sv
- tests/fpu_test_pkg.sv
- top/rtl/cvfpu_rsp_arb_sva.sv
- top/rtl/cvfpu_sva.sv
- top/rtl/tb_top.sv


Expand Down
7 changes: 7 additions & 0 deletions env/fpu_env.svh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ class fpu_env extends uvm_env;
pulse_gen_driver m_flush_driver;
pulse_gen_cfg m_flush_cfg;

bp_agent m_req_bp_agent;
bp_virtual_sequence m_req_bp_seq;

fpu_top_cfg m_fpu_top_cfg;

reset_driver_c #(1'b1,50,0) m_reset_driver;
Expand Down Expand Up @@ -69,6 +72,9 @@ class fpu_env extends uvm_env;
m_flush_driver = pulse_gen_driver::type_id::create("flush_driver", this );
m_flush_cfg = pulse_gen_cfg::type_id::create("flush_cfg", this );

m_req_bp_agent = bp_agent::type_id::create("req_bp_agent", this);
m_req_bp_seq = bp_virtual_sequence::type_id::create("req_bp_sequence", this);

m_fpu_top_cfg = fpu_top_cfg::type_id::create("fpu_top_cfg", this );

`uvm_info(get_full_name(), "Build phase complete", UVM_DEBUG)
Expand All @@ -86,6 +92,7 @@ class fpu_env extends uvm_env;

m_flush_driver.m_pulse_cfg = m_flush_cfg;
m_fpu_sb.m_sequencer = m_fpu_agent.m_sequencer;

m_fpu_sb.num_txn = m_fpu_top_cfg.get_num_txn();
`uvm_info(get_full_name( ), "Connect phase complete.", UVM_DEBUG)
endfunction: connect_phase
Expand Down
1 change: 1 addition & 0 deletions env/fpu_env_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ package fpu_env_pkg;
import fpu_agent_pkg::*;
import clock_driver_pkg::*;
import reset_driver_pkg::*;
import bp_driver_pkg::*;
import watchdog_pkg::*;
import pulse_gen_pkg::*;

Expand Down
2 changes: 1 addition & 1 deletion env/fpu_top_cfg.svh
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class fpu_top_cfg extends uvm_object;
endfunction: convert2string

constraint reset_on_the_fly_c {m_reset_on_the_fly dist {1 := 10, 0 := 90};} // insert reset on the fly 10% of the time
constraint flush_on_the_fly_c {m_flush_on_the_fly dist {1 := 10, 0 := 90};} // insert flush on the fly 10% of the time
constraint flush_on_the_fly_c {m_flush_on_the_fly dist {1 := 50, 0 := 50};} // insert flush on the fly 50% of the time

endclass : fpu_top_cfg

16 changes: 16 additions & 0 deletions fpu_agent/fpu_agent.svh
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ class fpu_agent extends uvm_agent;
fpu_driver m_driver;
fpu_monitor m_monitor;

// Coverage models
fpu_agent_cov_model m_cov_model;
fpu_conv_cov m_conv_cov;
fpu_comput_cov m_comput_cov;
fpu_noncomput_cov m_noncomput_cov;

virtual fpu_if fpu_vif;
virtual pulse_if flush_vif;

Expand All @@ -65,6 +71,10 @@ class fpu_agent extends uvm_agent;
m_monitor.set_is_active();
end

m_cov_model = fpu_agent_cov_model::type_id::create("cov_model", this);
m_conv_cov = fpu_conv_cov::type_id::create("conv_cov", this);
m_comput_cov = fpu_comput_cov::type_id::create("comput_cov", this);
m_noncomput_cov = fpu_noncomput_cov::type_id::create("noncomput_cov", this);
if (!uvm_config_db #( virtual fpu_if)::get(this, "", "fpu_vif", fpu_vif )) begin
`uvm_fatal("BUILD_PHASE", $psprintf("Unable to get fpu_vif for %s from configuration database", get_name() ) );
end
Expand All @@ -87,6 +97,12 @@ class fpu_agent extends uvm_agent;
m_driver.set_flush_vif(flush_vif);
m_monitor.m_sequencer = m_sequencer;
end
m_monitor.ap_fpu_req.connect(m_cov_model.ap_cov_req);
m_monitor.ap_fpu_rsp.connect(m_cov_model.ap_cov_rsp);
m_monitor.ap_flush.connect(m_cov_model.ap_cov_flush);
m_monitor.ap_conv_obs.connect(m_conv_cov.analysis_export);
m_monitor.ap_conv_obs.connect(m_comput_cov.analysis_export);
m_monitor.ap_conv_obs.connect(m_noncomput_cov.analysis_export);
m_monitor.set_fpu_vif(fpu_vif);
m_monitor.set_flush_vif(flush_vif);
`uvm_info(get_full_name( ), "Connect stage complete.", UVM_LOW)
Expand Down
8 changes: 7 additions & 1 deletion fpu_agent/fpu_agent_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,17 @@ package fpu_agent_pkg;
import fpu_common_pkg::*;
import ariane_pkg::*;
`include "uvm_macros.svh"

`include "fpu_txn.svh"
`include "fpu_txn_conv.svh"
`include "fpu_cov_model/fpu_obs_txn.svh"
`include "fpu_driver.svh"
`include "fpu_sequencer.svh"
`include "fpu_monitor.svh"
`include "fpu_sequences.svh"
`include "fpu_conv_seq.svh"
`include "fpu_cov_model/fpu_conv_cov.svh"
`include "fpu_cov_model/fpu_comput_cov.svh"
`include "fpu_cov_model/fpu_noncomput_cov.svh"
`include "fpu_cov_model/fpu_agent_cov_model.svh"
`include "fpu_agent.svh"
endpackage
54 changes: 54 additions & 0 deletions fpu_agent/fpu_conv_seq.svh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright (c) 2026 OpenHW Foundation
*
* SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
*
* Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
* may not use this file except in compliance with the License, or, at your
* option, the Apache License version 2.0. You may obtain a copy of the
* License at
*
* https://solderpad.org/licenses/SHL-2.1/
*
* Unless required by applicable law or agreed to in writing, any work
* 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.
*/
/*
* Authors : Ihsane TAHIR
* Creation Date : 2026
* Description :
* History :
*/

// ============================================================================
// fpu_conv_seq — weighted random across every conversion stimulus
// ============================================================================
class fpu_conv_seq extends fpu_base_sequence;
`uvm_object_utils(fpu_conv_seq)

fpu_txn_conv item;

function new(string name = "fpu_conv_seq");
super.new(name);
endfunction : new

virtual task body();
super.body();
item = fpu_txn_conv::type_id::create("fpu_conv_req");

for (int i = 0; i < num_txn; i++) begin
item.q_inflight_tid = my_sequencer.q_inflight_tid;

if (!item.randomize())
`uvm_fatal("fpu_conv_seq::body", "Randomization failed");

my_sequencer.q_inflight_tid[item.m_trans_id] = item.m_trans_id;
start_item(item);
finish_item(item);
end
endtask : body

endclass : fpu_conv_seq
233 changes: 233 additions & 0 deletions fpu_agent/fpu_cov_model/fpu_agent_cov_model.svh
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
/*
* Copyright (c) 2026 OpenHW Foundation
*
* SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1
*
* Licensed under the Solderpad Hardware License v 2.1 (the “License”); you
* may not use this file except in compliance with the License, or, at your
* option, the Apache License version 2.0. You may obtain a copy of the
* License at
*
* https://solderpad.org/licenses/SHL-2.1/
*
* Unless required by applicable law or agreed to in writing, any work
* 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.
*/
/*
* Authors : Ihsane TAHIR
* Creation Date : April, 2026
* Description :
* History :
*/

`uvm_analysis_imp_decl(_req)
`uvm_analysis_imp_decl(_rsp)
`uvm_analysis_imp_decl(_cov_flush)

class fpu_agent_cov_model extends uvm_component;

`uvm_component_utils(fpu_agent_cov_model)

uvm_analysis_imp_req #(fpu_req_t, fpu_agent_cov_model) ap_cov_req;
uvm_analysis_imp_rsp #(fpu_rsp_t, fpu_agent_cov_model) ap_cov_rsp;
uvm_analysis_imp_cov_flush #(bit, fpu_agent_cov_model) ap_cov_flush;

fpu_req_t req;
fpu_rsp_t rsp;

// Covergroups
covergroup cg_fpu_request(string name = "cg_fpu_request");
type_option.merge_instances = 1;
option.get_inst_coverage = 1;
option.per_instance = 1;

// Coverage for floating-point operations
cp_operation: coverpoint req.data.operation {
bins fadd = { FADD };
bins fsub = { FSUB };
bins fmul = { FMUL };
bins fdiv = { FDIV };
bins fsqrt = { FSQRT };
bins fmadd = { FMADD };
bins fmsub = { FMSUB };
bins fnmsub = { FNMSUB };
bins fnmadd = { FNMADD };
bins fcmp = { FCMP };
bins fmin_max = { FMIN_MAX };
bins fsgnj = { FSGNJ };
bins fcvt_f2f = { FCVT_F2F };
bins fcvt_f2i = { FCVT_F2I };
bins fcvt_i2f = { FCVT_I2F };
bins fmv_f2x = { FMV_F2X };
bins fmv_x2f = { FMV_X2F };
bins fclass = { FCLASS };
}

cp_fmt: coverpoint req.fmt {
bins FP32 = {2'b00};
bins FP64 = {2'b01};
ignore_bins UNSUPPORTED_FMTS = {[2:3]}; // FP16, FP8, and FP16ALT formats are not covered for now
}

cp_rm: coverpoint req.rm {
bins RNE = {3'b000}; // Round to Nearest, ties to Even
bins RTZ = {3'b001}; // Round towards Zero
bins RDN = {3'b010}; // Round Down
bins RUP = {3'b011}; // Round Up
// FIXME: The RMM rounding mode is currently not supported by the reference model
ignore_bins RMM = {3'b100}; // Round to Nearest, ties away from zero
}

cp_operand_a_class: coverpoint classify_operand(req.data.operand_a, req.data.operation == FCVT_F2F ? req.data.imm[1:0] : req.fmt){
bins OP_A_ZERO = {ZERO};
bins OP_A_SUBNORMAL = {SUBNORMAL};
bins OP_A_NORMAL = {NORMAL};
bins OP_A_INF = {INF};
bins OP_A_QNAN = {QNAN};
bins OP_A_SNAN = {SNAN};
}

cp_operand_b_class: coverpoint classify_operand(req.data.operand_b, req.data.operation == FCVT_F2F ? req.data.imm[1:0] : req.fmt){
bins OP_B_ZERO = {ZERO};
bins OP_B_SUBNORMAL = {SUBNORMAL};
bins OP_B_NORMAL = {NORMAL};
bins OP_B_INF = {INF};
bins OP_B_QNAN = {QNAN};
bins OP_B_SNAN = {SNAN};
}

cp_operand_c_class: coverpoint classify_operand(req.data.imm, req.data.operation == FCVT_F2F ? req.data.imm[1:0] : req.fmt){
bins OP_C_ZERO = {ZERO};
bins OP_C_SUBNORMAL = {SUBNORMAL};
bins OP_C_NORMAL = {NORMAL};
bins OP_C_INF = {INF};
bins OP_C_QNAN = {QNAN};
bins OP_C_SNAN = {SNAN};
}

cp_operand_a_sign: coverpoint get_operand_sign(req.data.operand_a, req.data.operation == FCVT_F2F ? req.data.imm[1:0] : req.fmt) {
bins OP_A_POS = {1'b0};
bins OP_A_NEG = {1'b1};
}

cp_operand_b_sign: coverpoint get_operand_sign(req.data.operand_b, req.data.operation == FCVT_F2F ? req.data.imm[1:0] : req.fmt) {
bins OP_B_POS = {1'b0};
bins OP_B_NEG = {1'b1};
}

cp_operand_c_sign: coverpoint get_operand_sign(req.data.imm, req.data.operation == FCVT_F2F ? req.data.imm[1:0] : req.fmt) {
bins OP_C_POS = {1'b0};
bins OP_C_NEG = {1'b1};
}

// Cross-coverage: Operation x Format
cp_op_x_fmt: cross cp_operation, cp_fmt;

cp_opA_x_sign: cross cp_operand_a_class, cp_operand_a_sign;
cp_opB_x_sign: cross cp_operand_b_class, cp_operand_b_sign;
cp_opC_x_sign: cross cp_operand_c_class, cp_operand_c_sign;
endgroup

covergroup cg_fpu_response(string name = "cg_fpu_response");
type_option.merge_instances = 1;
option.get_inst_coverage = 1;
option.per_instance = 1;
// option.name = name;

cp_fp_exception: coverpoint rsp.exception.cause {
// Exact match for 0 (no bits set)
bins NO_EXCEPTION = {'h0};
wildcard bins NX = {5'b????1}; // Bit 0: Inexact
wildcard bins UF = {5'b???1?}; // Bit 1: Underflow
wildcard bins OF = {5'b??1??}; // Bit 2: Overflow
wildcard bins DZ = {5'b?1???}; // Bit 3: Divide by Zero
wildcard bins NV = {5'b1????}; // Bit 4: Invalid Operation
}
endgroup

covergroup cg_fpu_flush(string name = "cg_fpu_flush") with function sample(bit flush);
type_option.merge_instances = 1;
option.get_inst_coverage = 1;
option.per_instance = 1;
// option.name = name;

cp_flush: coverpoint flush {
bins flush_detected = {1'b1};
}
endgroup

function new(string name = "fpu_agent_cov_model", uvm_component parent = null);
super.new(name, parent);
cg_fpu_request = new("cg_fpu_request");
cg_fpu_response = new("cg_fpu_response");
cg_fpu_flush = new("cg_fpu_flush");
endfunction

function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Create analysis ports
ap_cov_req = new("ap_cov_req", this);
ap_cov_rsp = new("ap_cov_rsp", this);
ap_cov_flush = new("ap_cov_flush", this);
endfunction

function void write_req(fpu_req_t req);
this.req = req;
cg_fpu_request.sample();
endfunction

function void write_rsp(fpu_rsp_t rsp);
this.rsp = rsp;
cg_fpu_response.sample();
endfunction

function void write_cov_flush(bit flush);
cg_fpu_flush.sample(flush);
endfunction

// ----------------------------------------------------------------
// API FUNCTIONS
// ----------------------------------------------------------------
/**
* Classify floating point operand
*/
function fp_op_type_e classify_operand(logic [CVA6Cfg.FLen-1:0] value, logic [1:0] fmt);
logic [10:0] exponent;
logic [51:0] mantissa;
logic mantissa_msb;

case (fmt)
2'h0: begin // FP32
mantissa = value[fpnew_pkg::man_bits(fpnew_pkg::FP32)-1:0];
exponent = value[fpnew_pkg::man_bits(fpnew_pkg::FP32) +: fpnew_pkg::exp_bits(fpnew_pkg::FP32)];
mantissa_msb = mantissa[fpnew_pkg::man_bits(fpnew_pkg::FP32)-1];
end
2'h1: begin // FP64
mantissa = value[fpnew_pkg::man_bits(fpnew_pkg::FP64)-1:0];
exponent = value[fpnew_pkg::man_bits(fpnew_pkg::FP64) +: fpnew_pkg::exp_bits(fpnew_pkg::FP64)];
mantissa_msb = mantissa[fpnew_pkg::man_bits(fpnew_pkg::FP64)-1];
end
endcase

if (exponent == '0 && mantissa == '0) return ZERO;
if (exponent == '0 && mantissa != '0) return SUBNORMAL;
if (exponent != '0 && exponent != '1) return NORMAL;
if (exponent == '1 && mantissa == '0) return INF;
if (exponent == '1 && mantissa != '0 && mantissa_msb == 1'b0) return SNAN;
return QNAN;
endfunction: classify_operand

/**
* Extract sign bit from operand
*/
function bit get_operand_sign(logic [CVA6Cfg.FLen-1:0] value, logic [1:0] fmt);
case (fmt)
2'h0: return value[fpnew_pkg::fp_width(fpnew_pkg::FP32)-1];
2'h1: return value[fpnew_pkg::fp_width(fpnew_pkg::FP64)-1];
endcase
endfunction: get_operand_sign

endclass //fpu_agent_cov_model
Loading