From 4eae3f4cda95f1f35f40f66ae4e4445bd692fd80 Mon Sep 17 00:00:00 2001 From: Ryan Wolk Date: Thu, 30 Apr 2026 19:33:12 -0700 Subject: [PATCH 1/4] Correct Subnormal Exponent Tracking from No-Rounding Cases --- src/c/coverfloat.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/c/coverfloat.cpp b/src/c/coverfloat.cpp index 98adc6c..34ac64b 100644 --- a/src/c/coverfloat.cpp +++ b/src/c/coverfloat.cpp @@ -2205,11 +2205,16 @@ std::pair reference_model( if (exp != 0) { sig |= (1 << 23); - intermResult.sig = mp::cpp_int(sig) << (INTERM_SIG_LENGTH - 2 - 23); } + intermResult.sig = mp::cpp_int(sig) << (INTERM_SIG_LENGTH - 2 - 23); + intermResult.exp = exp; + if (exp == 0 && sig != 0) { + intermResult.exp = -(INTERM_SIG_LENGTH - 2 - safe_msb(intermResult.sig)); + } + intermResult.sign = signF32UI(result); break; } From 8fd052f2c1f9904fb710c53a65bb9a11e70af0cb Mon Sep 17 00:00:00 2001 From: Ryan Wolk Date: Thu, 30 Apr 2026 19:35:08 -0700 Subject: [PATCH 2/4] Add B13 Covergroup --- config.svh | 6 +- coverage/coverfloat_pkg.sv | 2 +- coverage/covergroups/B13.svh | 234 +++++++++++++++++++++++++++++++++++ 3 files changed, 238 insertions(+), 4 deletions(-) create mode 100644 coverage/covergroups/B13.svh diff --git a/config.svh b/config.svh index 346556f..723c49f 100644 --- a/config.svh +++ b/config.svh @@ -26,11 +26,11 @@ // `define COVER_B8 `define COVER_B9 `define COVER_B10 -// `define COVER_B11 +`define COVER_B11 `define COVER_B12 -// `define COVER_B13 +`define COVER_B13 `define COVER_B14 -// `define COVER_B15 +`define COVER_B15 `define COVER_B16 // `define COVER_B17 // `define COVER_B18 diff --git a/coverage/coverfloat_pkg.sv b/coverage/coverfloat_pkg.sv index e416068..1ea9477 100644 --- a/coverage/coverfloat_pkg.sv +++ b/coverage/coverfloat_pkg.sv @@ -860,7 +860,7 @@ function automatic int effective_exponent ( break; end - return min_norm_exp - shift - 1; + return - shift - 1 - bias; endfunction diff --git a/coverage/covergroups/B13.svh b/coverage/covergroups/B13.svh new file mode 100644 index 0000000..8432c4f --- /dev/null +++ b/coverage/covergroups/B13.svh @@ -0,0 +1,234 @@ +// Copyright (C) 2025-26 Harvey Mudd College, Ryan Wolk (rwolk@hmc.edu) +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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, 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. + +covergroup B13_cg (virtual coverfloat_interface CFI); + option.per_instance = 0; + + // Source Format Helpers + + F16_src_fmt: coverpoint (CFI.operandFmt == FMT_HALF) { + type_option.weight = 0; + bins f16 = {1}; + } + + BF16_src_fmt: coverpoint (CFI.operandFmt == FMT_BF16) { + type_option.weight = 0; + bins bf16 = {1}; + } + + F32_src_fmt: coverpoint (CFI.operandFmt == FMT_SINGLE) { + type_option.weight = 0; + bins f32 = {1}; + } + + F64_src_fmt: coverpoint (CFI.operandFmt == FMT_DOUBLE) { + type_option.weight = 0; + bins f64 = {1}; + } + + F128_src_fmt: coverpoint (CFI.operandFmt == FMT_QUAD) { + type_option.weight = 0; + bins f128 = {1}; + } + + // RFI Instruction + + ADD_SUB_op: coverpoint CFI.op { + type_option.weight = 0; + bins add = { OP_ADD }; + bins sub = { OP_SUB }; + } + + // Cancellation Coverpoints + + F32_cancellation: coverpoint + ( + ( + (CFI.intermX == 0 && CFI.intermM != 0) + ? -count_leading_zeros(CFI.intermM[INTERM_M_BITS-1:INTERM_M_BITS-256], 256) + : -F32_M_BITS + ) + - + ( + (effective_exponent(CFI.a, FMT_SINGLE) > effective_exponent(CFI.b, FMT_SINGLE)) + ? effective_exponent(CFI.a, FMT_SINGLE) + F32_EXP_BIAS + : effective_exponent(CFI.b, FMT_SINGLE) + F32_EXP_BIAS + ) + ) + { + type_option.weight = 0; + bins cancel[] = {[-F32_P: 1]}; + } + + F64_cancellation: coverpoint + ( + ( + (CFI.intermX == 0 && CFI.intermM != 0) + ? -count_leading_zeros(CFI.intermM[INTERM_M_BITS-1:INTERM_M_BITS-256], 256) + : -F64_M_BITS + ) + - + ( + (effective_exponent(CFI.a, FMT_DOUBLE) > effective_exponent(CFI.b, FMT_DOUBLE)) + ? effective_exponent(CFI.a, FMT_DOUBLE) + F64_EXP_BIAS + : effective_exponent(CFI.b, FMT_DOUBLE) + F64_EXP_BIAS + ) + ) + { + type_option.weight = 0; + bins cancel[] = {[-F64_P: 1]}; + } + + F128_cancellation: coverpoint + ( + ( + (CFI.intermX == 0 && CFI.intermM != 0) + ? -count_leading_zeros(CFI.intermM[INTERM_M_BITS-1:INTERM_M_BITS-256], 256) + : -F128_M_BITS + ) + - + ( + (effective_exponent(CFI.a, FMT_QUAD) > effective_exponent(CFI.b, FMT_QUAD)) + ? effective_exponent(CFI.a, FMT_QUAD) + F128_EXP_BIAS + : effective_exponent(CFI.b, FMT_QUAD) + F128_EXP_BIAS + ) + ) + { + type_option.weight = 0; + bins cancel[] = {[-F128_P: 1]}; + } + + F16_cancellation: coverpoint + ( + ( + (CFI.intermX == 0 && CFI.intermM != 0) + ? -count_leading_zeros(CFI.intermM[INTERM_M_BITS-1:INTERM_M_BITS-256], 256) + : -F16_M_BITS + ) + - + ( + (effective_exponent(CFI.a, FMT_HALF) > effective_exponent(CFI.b, FMT_HALF)) + ? effective_exponent(CFI.a, FMT_HALF) + F16_EXP_BIAS + : effective_exponent(CFI.b, FMT_HALF) + F16_EXP_BIAS + ) + ) + { + type_option.weight = 0; + bins cancel[] = {[-F16_P: 1]}; + } + + BF16_cancellation: coverpoint + ( + ( + (CFI.intermX == 0 && CFI.intermM != 0) + ? -count_leading_zeros(CFI.intermM[INTERM_M_BITS-1:INTERM_M_BITS-256], 256) + : -BF16_M_BITS + ) + - + ( + (effective_exponent(CFI.a, FMT_BF16) > effective_exponent(CFI.b, FMT_BF16)) + ? effective_exponent(CFI.a, FMT_BF16) + BF16_EXP_BIAS + : effective_exponent(CFI.b, FMT_BF16) + BF16_EXP_BIAS + ) + ) + { + type_option.weight = 0; + bins cancel[] = {[-BF16_P: 1]}; + } + + // Subnormal Exponent Coverpoint + F32_subnorm_exp: coverpoint(count_leading_zeros(CFI.result[F32_M_UPPER:0], F32_M_BITS)) { + type_option.weight = 0; + bins negative_exp[] = {[F32_M_BITS-1:0]}; + } + F32_subnormal: coverpoint(|CFI.result[F32_E_UPPER:F32_E_LOWER]) { + type_option.weight = 0; + bins is_subnormal = { 0 }; + } + + F64_subnorm_exp: coverpoint(count_leading_zeros(CFI.result[F64_M_UPPER:0], F64_M_BITS)) { + type_option.weight = 0; + bins negative_exp[] = {[F64_M_BITS-1:0]}; + } + F64_subnormal: coverpoint(|CFI.result[F64_E_UPPER:F64_E_LOWER]) { + type_option.weight = 0; + bins is_subnormal = { 0 }; + } + + F128_subnorm_exp: coverpoint(count_leading_zeros(CFI.result[F128_M_UPPER:0], F128_M_BITS)) { + type_option.weight = 0; + bins negative_exp[] = {[F128_M_BITS-1:0]}; + } + F128_subnormal: coverpoint(|CFI.result[F128_E_UPPER:F128_E_LOWER]) { + type_option.weight = 0; + bins is_subnormal = { 0 }; + } + + F16_subnorm_exp: coverpoint(count_leading_zeros(CFI.result[F16_M_UPPER:0], F16_M_BITS)) { + type_option.weight = 0; + bins negative_exp[] = {[F16_M_BITS-1:0]}; + } + F16_subnormal: coverpoint(|CFI.result[F16_E_UPPER:F16_E_LOWER]) { + type_option.weight = 0; + bins is_subnormal = { 0 }; + } + + BF16_subnorm_exp: coverpoint(count_leading_zeros(CFI.result[BF16_M_UPPER:0], BF16_M_BITS)) { + type_option.weight = 0; + bins negative_exp[] = {[BF16_M_BITS-1:0]}; + } + BF16_subnormal: coverpoint(|CFI.result[BF16_E_UPPER:BF16_E_LOWER]) { + type_option.weight = 0; + bins is_subnormal = { 0 }; + } + + // Main Crosses + `ifdef COVER_F32 + B13_F32_subnormal_cancel: cross ADD_SUB_op, F32_src_fmt, F32_cancellation, F32_subnorm_exp, F32_subnormal { + ignore_bins min_subnorm_carry = binsof(F32_subnorm_exp.negative_exp) intersect {F32_M_BITS-1} && binsof(F32_cancellation.cancel) intersect {1}; + ignore_bins min_subnorm_no_cancel = binsof(F32_subnorm_exp.negative_exp) intersect {F32_M_BITS-1} && binsof(F32_cancellation.cancel) intersect {0}; + } + `endif + + `ifdef COVER_F64 + B13_F64_subnormal_cancel: cross ADD_SUB_op, F64_src_fmt, F64_cancellation, F64_subnorm_exp, F64_subnormal { + ignore_bins min_subnorm_carry = binsof(F64_subnorm_exp.negative_exp) intersect {F64_M_BITS-1} && binsof(F64_cancellation.cancel) intersect {1}; + ignore_bins min_subnorm_no_cancel = binsof(F64_subnorm_exp.negative_exp) intersect {F64_M_BITS-1} && binsof(F64_cancellation.cancel) intersect {0}; + } + `endif + + `ifdef COVER_F128 + B13_F128_subnormal_cancel: cross ADD_SUB_op, F128_src_fmt, F128_cancellation, F128_subnorm_exp, F128_subnormal { + ignore_bins min_subnorm_carry = binsof(F128_subnorm_exp.negative_exp) intersect {F128_M_BITS-1} && binsof(F128_cancellation.cancel) intersect {1}; + ignore_bins min_subnorm_no_cancel = binsof(F128_subnorm_exp.negative_exp) intersect {F128_M_BITS-1} && binsof(F128_cancellation.cancel) intersect {0}; + } + `endif + + `ifdef COVER_F16 + B13_F16_subnormal_cancel: cross ADD_SUB_op, F16_src_fmt, F16_cancellation, F16_subnorm_exp, F16_subnormal { + ignore_bins min_subnorm_carry = binsof(F16_subnorm_exp.negative_exp) intersect {F16_M_BITS-1} && binsof(F16_cancellation.cancel) intersect {1}; + ignore_bins min_subnorm_no_cancel = binsof(F16_subnorm_exp.negative_exp) intersect {F16_M_BITS-1} && binsof(F16_cancellation.cancel) intersect {0}; + } + `endif + + `ifdef COVER_BF16 + B13_BF16_subnormal_cancel: cross ADD_SUB_op, BF16_src_fmt, BF16_cancellation, BF16_subnorm_exp, BF16_subnormal { + ignore_bins min_subnorm_carry = binsof(BF16_subnorm_exp.negative_exp) intersect {BF16_M_BITS-1} && binsof(BF16_cancellation.cancel) intersect {1}; + ignore_bins min_subnorm_no_cancel = binsof(BF16_subnorm_exp.negative_exp) intersect {BF16_M_BITS-1} && binsof(BF16_cancellation.cancel) intersect {0}; + } + `endif + + +endgroup From f50bec8a968098844a129597e3747e1f95d043e1 Mon Sep 17 00:00:00 2001 From: Ryan Wolk Date: Thu, 30 Apr 2026 20:06:30 -0700 Subject: [PATCH 3/4] Return coverfloat_pkg to correct effective exponent calculation --- coverage/coverfloat_pkg.sv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage/coverfloat_pkg.sv b/coverage/coverfloat_pkg.sv index 1ea9477..e416068 100644 --- a/coverage/coverfloat_pkg.sv +++ b/coverage/coverfloat_pkg.sv @@ -860,7 +860,7 @@ function automatic int effective_exponent ( break; end - return - shift - 1 - bias; + return min_norm_exp - shift - 1; endfunction From 4c67d6f74ecd73791dda82b2e8b82e3b4638d17d Mon Sep 17 00:00:00 2001 From: Ryan Wolk Date: Thu, 30 Apr 2026 20:13:50 -0700 Subject: [PATCH 4/4] Handle Subnormal Exponents Correctly across all precisions --- src/c/coverfloat.cpp | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/c/coverfloat.cpp b/src/c/coverfloat.cpp index 34ac64b..a9742e4 100644 --- a/src/c/coverfloat.cpp +++ b/src/c/coverfloat.cpp @@ -2180,6 +2180,12 @@ std::pair reference_model( intermResult.exp = exp; + if (exp == 0 && sig != 0) { + // In theory, we place the leading one (zero for subnormals) in the INTERM_SIG - 2 position + // so an effective exponent of 0 means that the leading digit is in the INTERM_SIG - 3 position + intermResult.exp = -(INTERM_SIG_LENGTH - 3 - safe_msb(intermResult.sig)); + } + intermResult.sign = signBF16UI(result); break; } @@ -2196,6 +2202,10 @@ std::pair reference_model( intermResult.exp = exp; + if (exp == 0 && sig != 0) { + intermResult.exp = -(INTERM_SIG_LENGTH - 3 - safe_msb(intermResult.sig)); + } + intermResult.sign = signF16UI(result); break; } @@ -2212,7 +2222,7 @@ std::pair reference_model( intermResult.exp = exp; if (exp == 0 && sig != 0) { - intermResult.exp = -(INTERM_SIG_LENGTH - 2 - safe_msb(intermResult.sig)); + intermResult.exp = -(INTERM_SIG_LENGTH - 3 - safe_msb(intermResult.sig)); } intermResult.sign = signF32UI(result); @@ -2230,6 +2240,10 @@ std::pair reference_model( intermResult.exp = exp; + if (exp == 0 && sig != 0) { + intermResult.exp = -(INTERM_SIG_LENGTH - 3 - safe_msb(intermResult.sig)); + } + intermResult.sign = signF64UI(result); break; } @@ -2249,6 +2263,10 @@ std::pair reference_model( intermResult.exp = exp; + if (exp == 0 && sig != 0) { + intermResult.exp = -(INTERM_SIG_LENGTH - 3 - safe_msb(intermResult.sig)); + } + intermResult.sign = signF128UI64(result >> 64); break; } @@ -2403,7 +2421,8 @@ std::pair reference_model( // 2. Subnormal Handling if (intermResult.exp <= 0 && !int_format) { - // See s_roundPackToF32.c for why we add 1. Our exp is +1 theirs + // We add 1 here to account for there being no leading one. Consider intermResult.exp=0, + // in this case, we need to shift the result over by one. int32_t shift_dist = -intermResult.exp + 1; // Lets shift right jam ourselves now!