Skip to content

Commit ab9bca0

Browse files
[fcov] Add fcov for the interaction of pmp with debug module accesses
Accesses to the debug module in debug mode should never be denied by the PMP unit. This commit implements fcov to confirm we have stimulated this particular behaviour in relevant related states. Illegal bins are used for incorrect behaviour (e.g. denied access in debug mode) Other behaviours such as debug module accesses outside of debug mode are left as ignore_bins for now. This is not explicitly disallowed by the specification, and our implementation does not have any opinion about its validity, but external debug modules opine that it should not be allowed. We could possibly expand the stimulus in the future to test this condition, but it is low priority.
1 parent fa84591 commit ab9bca0

File tree

3 files changed

+141
-11
lines changed

3 files changed

+141
-11
lines changed

dv/uvm/core_ibex/fcov/core_ibex_pmp_fcov_if.sv

Lines changed: 118 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
2020
input logic pmp_req_err [3],
2121
input pmp_mseccfg_t csr_pmp_mseccfg,
2222

23+
input logic debug_mode,
24+
2325
input logic data_req_out,
2426

2527
input fcov_csr_write
@@ -136,6 +138,14 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
136138
assign pmp_dside_boundary_cross = |(pmp_dside_match ^ pmp_dside_match_last) &
137139
load_store_unit_i.fcov_ls_second_req;
138140

141+
logic pmp_iside_access_fault_check;
142+
logic pmp_iside2_access_fault_check;
143+
logic pmp_dside_access_fault_check;
144+
145+
assign pmp_iside_access_fault_check = g_pmp.pmp_i.access_fault_check_res[PMP_I];
146+
assign pmp_iside2_access_fault_check = g_pmp.pmp_i.access_fault_check_res[PMP_I2];
147+
assign pmp_dside_access_fault_check = g_pmp.pmp_i.access_fault_check_res[PMP_D];
148+
139149
for (genvar i_region = 0; i_region < PMPNumRegions; i_region += 1) begin : g_pmp_region_fcov
140150
pmp_priv_bits_e pmp_region_priv_bits;
141151
pmp_priv_bits_e pmp_region_priv_bits_wr;
@@ -237,7 +247,8 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
237247
cross cp_region_priv_bits, cp_req_type_iside, cp_priv_lvl_iside, pmp_iside_req_err
238248
iff (g_pmp_fcov_signals.g_pmp_region_fcov[i_region].fcov_pmp_region_ichan_access &&
239249
g_pmp_fcov_signals.fcov_pmp_region_ichan_priority[i_region] &&
240-
csr_pmp_cfg[i_region].mode != PMP_MODE_OFF) {
250+
csr_pmp_cfg[i_region].mode != PMP_MODE_OFF &&
251+
!(g_pmp_fcov_signals.fcov_access_attempted_into_dm[PMP_I])) {
241252

242253
// Will never see a succesful exec access when execute is disallowed
243254
illegal_bins illegal_allow_exec =
@@ -294,7 +305,8 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
294305
cross cp_region_priv_bits, cp_req_type_iside2, cp_priv_lvl_iside2, pmp_iside2_req_err
295306
iff (g_pmp_fcov_signals.g_pmp_region_fcov[i_region].fcov_pmp_region_ichan2_access &&
296307
g_pmp_fcov_signals.fcov_pmp_region_ichan2_priority[i_region] &&
297-
csr_pmp_cfg[i_region].mode != PMP_MODE_OFF) {
308+
csr_pmp_cfg[i_region].mode != PMP_MODE_OFF &&
309+
!(g_pmp_fcov_signals.fcov_access_attempted_into_dm[PMP_I2])) {
298310

299311
// Will never see a succesful exec access when execute is disallowed
300312
illegal_bins illegal_allow_exec =
@@ -351,7 +363,8 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
351363
cross cp_region_priv_bits, cp_req_type_dside, cp_priv_lvl_dside, pmp_dside_req_err
352364
iff (g_pmp_fcov_signals.g_pmp_region_fcov[i_region].fcov_pmp_region_dchan_access &&
353365
g_pmp_fcov_signals.fcov_pmp_region_dchan_priority[i_region] &&
354-
csr_pmp_cfg[i_region].mode != PMP_MODE_OFF) {
366+
csr_pmp_cfg[i_region].mode != PMP_MODE_OFF &&
367+
!(g_pmp_fcov_signals.fcov_access_attempted_into_dm[PMP_D])) {
355368

356369
// Will never see a succesful read access when read is disallowed
357370
illegal_bins illegal_allow_read =
@@ -709,6 +722,108 @@ interface core_ibex_pmp_fcov_if import ibex_pkg::*; #(
709722
misaligned_lsu_access_cross: cross misaligned_pmp_err_last,
710723
load_store_unit_i.fcov_ls_mis_pmp_err_2
711724
iff (pmp_dside_boundary_cross);
725+
726+
// Debug Module Accesses
727+
728+
// Coverpoints that we have attempted to access the debug module address range
729+
//
730+
// The riscv-dbg module states the following:
731+
// > riscv-dbg/doc/debug-system.md (# Debug Memory)
732+
// > The Debug Memory should only be accessible from the CPU if it is in debug mode.
733+
// However, The RISC-V Debug Specification does not mandate this. Bins for accesses in normal
734+
// mode are ignored for the purpose of this coverage.
735+
736+
dm_fetch_iside_debug_mode_cp: coverpoint debug_mode
737+
iff (g_pmp_fcov_signals.fcov_access_attempted_into_dm[PMP_I])
738+
{
739+
bins dm_debug_mode_fetch = {1'b1};
740+
// We shouldn't be fetching from the debug module unless in debug mode.
741+
ignore_bins dm_normal_mode_fetch = {1'b0};
742+
}
743+
dm_fetch_iside2_debug_mode_cp: coverpoint debug_mode
744+
iff (g_pmp_fcov_signals.fcov_access_attempted_into_dm[PMP_I2])
745+
{
746+
bins dm_debug_mode_fetch = {1'b1};
747+
// We shouldn't be fetching from the debug module unless in debug mode.
748+
ignore_bins dm_normal_mode_fetch = {1'b0};
749+
}
750+
dm_load_store_debug_mode_cp: coverpoint debug_mode
751+
iff (g_pmp_fcov_signals.fcov_access_attempted_into_dm[PMP_D])
752+
{
753+
bins dm_debug_mode_load_store = {1'b1};
754+
// We shouldn't be loading/storing from/to the debug module unless in debug mode.
755+
ignore_bins dm_normal_mode_load_store = {1'b0};
756+
}
757+
758+
cp_ls_pmp_exception: coverpoint load_store_unit_i.fcov_ls_pmp_exception;
759+
760+
// Cross access attempts with the result of the access check
761+
762+
dm_fetch_access_iside_cross: cross
763+
dm_fetch_iside_debug_mode_cp,
764+
pmp_iside_req_err,
765+
pmp_iside_access_fault_check
766+
{
767+
// Fetches should never fail the access check in debug mode
768+
illegal_bins dm_debug_mode_disallowed_fetch =
769+
binsof(pmp_iside_req_err) intersect {1'b1} &&
770+
binsof(dm_fetch_iside_debug_mode_cp) intersect {1'b1};
771+
772+
// Allowed fetches in debug mode may or may not override a denying PMP region.
773+
// Create a bin for each possibility.
774+
bins dm_debug_mode_pmp_allow_fetch_allowed =
775+
binsof(dm_fetch_iside_debug_mode_cp) intersect {1'b1} &&
776+
binsof(pmp_iside_access_fault_check) intersect {1'b0} &&
777+
binsof(pmp_iside_req_err) intersect {1'b0};
778+
bins dm_debug_mode_pmp_deny_fetch_allowed =
779+
binsof(dm_fetch_iside_debug_mode_cp) intersect {1'b1} &&
780+
binsof(pmp_iside_access_fault_check) intersect {1'b1} &&
781+
binsof(pmp_iside_req_err) intersect {1'b0};
782+
}
783+
784+
dm_fetch_access_iside2_cross: cross
785+
dm_fetch_iside2_debug_mode_cp,
786+
pmp_iside2_req_err,
787+
pmp_iside2_access_fault_check
788+
{
789+
// Fetches should never fail the access check in debug mode
790+
illegal_bins dm_debug_mode_disallowed_fetch =
791+
binsof(pmp_iside2_req_err) intersect {1'b1} &&
792+
binsof(dm_fetch_iside2_debug_mode_cp) intersect {1'b1};
793+
794+
// Allowed fetches in debug mode may or may not override a denying PMP region.
795+
// Create a bin for each possibility.
796+
bins dm_debug_mode_pmp_allow_fetch_allowed =
797+
binsof(dm_fetch_iside2_debug_mode_cp) intersect {1'b1} &&
798+
binsof(pmp_iside2_access_fault_check) intersect {1'b0} &&
799+
binsof(pmp_iside2_req_err) intersect {1'b0};
800+
bins dm_debug_mode_pmp_deny_fetch_allowed =
801+
binsof(dm_fetch_iside2_debug_mode_cp) intersect {1'b1} &&
802+
binsof(pmp_iside2_access_fault_check) intersect {1'b1} &&
803+
binsof(pmp_iside2_req_err) intersect {1'b0};
804+
}
805+
806+
dm_load_store_access_cross: cross
807+
dm_load_store_debug_mode_cp,
808+
cp_ls_pmp_exception,
809+
pmp_dside_access_fault_check
810+
{
811+
// Loads/Stores should never fail the access check in debug mode
812+
illegal_bins dm_debug_mode_disallowed_load_store =
813+
binsof(cp_ls_pmp_exception) intersect {1'b1} &&
814+
binsof(dm_load_store_debug_mode_cp) intersect {1'b1};
815+
816+
// Allowed loads/stores in debug mode may or may not override a denying PMP region.
817+
// Create a bin for each possibility.
818+
bins dm_debug_mode_pmp_allow_allowed_load_store =
819+
binsof(dm_load_store_debug_mode_cp) intersect {1'b1} &&
820+
binsof(pmp_dside_access_fault_check) intersect {1'b1} &&
821+
binsof(cp_ls_pmp_exception) intersect {1'b0};
822+
bins dm_debug_mode_pmp_deny_allowed_load_store =
823+
binsof(dm_load_store_debug_mode_cp) intersect {1'b1} &&
824+
binsof(pmp_dside_access_fault_check) intersect {1'b0} &&
825+
binsof(cp_ls_pmp_exception) intersect {1'b0};
826+
}
712827
endgroup
713828

714829
`DV_FCOV_INSTANTIATE_CG(pmp_top_cg, en_pmp_fcov)

rtl/ibex_core.sv

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1944,6 +1944,20 @@ module ibex_core import ibex_pkg::*; #(
19441944
g_pmp.pmp_i.region_match_all[PMP_D][i_region];
19451945
end
19461946
end
1947+
1948+
// Create a signal 'fcov_access_attempted_into_dm' for each PMP channel, which becomes true at
1949+
// the point when a memory access into the debug_module address space via that channel is
1950+
// accesses-checked by it's PMP unit. If true we know that the current instruction at least
1951+
// attempted to make an access, and we can then cross this signal with the result of the pmp
1952+
// check and the current debug_mode state to capture all appropriate coverage.
1953+
logic [PMPNumChan-1:0] access_check_into_dm;
1954+
for (genvar c = 0; c < PMPNumChan; c++) begin : g_pmp_channel_access_check
1955+
assign access_check_into_dm[c] = (g_pmp.pmp_req_addr[c][31:0] & ~DmAddrMask) == DmBaseAddr;
1956+
end
1957+
`DV_FCOV_SIGNAL(logic [PMPNumChan-1:0], access_attempted_into_dm,
1958+
{access_check_into_dm[PMP_D] & data_req_out, // [2]
1959+
access_check_into_dm[PMP_I2] & if_stage_i.if_id_pipe_reg_we, // [1]
1960+
access_check_into_dm[PMP_I] & if_stage_i.if_id_pipe_reg_we}) // [0]
19471961
end
19481962
`endif
19491963

rtl/ibex_pmp.sv

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ module ibex_pmp #(
4141
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_match_all;
4242
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_basic_perm_check;
4343
logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_perm_check;
44+
logic [PMPNumChan-1:0] access_fault_check_res;
4445
logic [PMPNumChan-1:0] debug_mode_allowed_access;
4546

4647
///////////////////////
@@ -241,14 +242,14 @@ module ibex_pmp #(
241242

242243
// Once the permission checks of the regions are done, decide if the access is
243244
// denied by figuring out the matching region and its permission check.
244-
// No error is raised if the access is allowed as Debug Module access (first term).
245-
assign pmp_req_err_o[c] = ~debug_mode_allowed_access[c] &
246-
access_fault_check(csr_pmp_mseccfg_i.mmwp,
247-
csr_pmp_mseccfg_i.mml,
248-
pmp_req_type_i[c],
249-
region_match_all[c],
250-
priv_mode_i[c],
251-
region_perm_check[c]);
245+
// No error is raised if the access is allowed via the Debug Module in Debug Mode exception.
246+
assign access_fault_check_res[c] = access_fault_check(csr_pmp_mseccfg_i.mmwp,
247+
csr_pmp_mseccfg_i.mml,
248+
pmp_req_type_i[c],
249+
region_match_all[c],
250+
priv_mode_i[c],
251+
region_perm_check[c]);
252+
assign pmp_req_err_o[c] = ~debug_mode_allowed_access[c] & access_fault_check_res[c];
252253

253254
// Access fails check against one region but access allowed due to another higher-priority
254255
// region.

0 commit comments

Comments
 (0)