From 74abf8e51c8ae05cb4ac2ce665a14ca7b2c904e8 Mon Sep 17 00:00:00 2001 From: Blebowski <34539154+Blebowski@users.noreply.github.com> Date: Thu, 24 Nov 2022 20:03:23 +0100 Subject: [PATCH] Toggle coverage improvements (#576) --- nvc.1 | 107 +++++++++++++-- src/lower.c | 85 +++++++++--- src/nvc.c | 94 ++++++++------ src/rt/cover.c | 244 +++++++++++++++++++++++++++-------- src/rt/cover.h | 23 ++-- src/rt/model.c | 5 + src/rt/model.h | 1 + test/regress/cover1.sh | 3 +- test/regress/cover1.vhd | 113 ++++++++++++++-- test/regress/cover2.sh | 16 +++ test/regress/cover2.vhd | 186 ++++++++++++++++++++++++++ test/regress/cover3.sh | 16 +++ test/regress/cover3.vhd | 136 +++++++++++++++++++ test/regress/cover4.sh | 16 +++ test/regress/cover4.vhd | 55 ++++++++ test/regress/cover5.sh | 30 +++++ test/regress/cover5.vhd | 64 +++++++++ test/regress/cover6.sh | 24 ++++ test/regress/cover6.vhd | 53 ++++++++ test/regress/gold/cover1.txt | 4 +- test/regress/gold/cover2.txt | 4 + test/regress/gold/cover3.txt | 4 + test/regress/gold/cover4.txt | 4 + test/regress/gold/cover5.txt | 20 +++ test/regress/gold/cover6.txt | 12 ++ test/regress/testlist.txt | 5 + test/test_lower.c | 2 +- 27 files changed, 1177 insertions(+), 149 deletions(-) create mode 100644 test/regress/cover2.sh create mode 100644 test/regress/cover2.vhd create mode 100644 test/regress/cover3.sh create mode 100644 test/regress/cover3.vhd create mode 100644 test/regress/cover4.sh create mode 100644 test/regress/cover4.vhd create mode 100644 test/regress/cover5.sh create mode 100644 test/regress/cover5.vhd create mode 100644 test/regress/cover6.sh create mode 100644 test/regress/cover6.vhd create mode 100644 test/regress/gold/cover2.txt create mode 100644 test/regress/gold/cover3.txt create mode 100644 test/regress/gold/cover4.txt create mode 100644 test/regress/gold/cover5.txt create mode 100644 test/regress/gold/cover6.txt diff --git a/nvc.1 b/nvc.1 index 4a68065d..83c6342e 100644 --- a/nvc.1 +++ b/nvc.1 @@ -372,6 +372,7 @@ code coverage database. Generate HTML code coverage report to .Ar DIR directory. +.El .\" ------------------------------------------------------------ .\" Make options .\" ------------------------------------------------------------ @@ -470,27 +471,31 @@ NVC supports collecting of code coverage. Following coverage types are supported: .Bl -bullet .It -Statement - -.Cm s +.Cm statement - For each statement, NVC creates coverage bin. When statement is executed, it is covered. .It -Branch - -.Cm b +.Cm branch - For each point where code diverges (if/else, case, when/else, with/select statements), NVC creates coverage bin. If branch can be evaluated to both true and false, NVC creates two coverage bins for such branch (one for each of true/false) .It -Toggle - -.Cm t +.Cm toggle - Each signal of type derived from std_logic -(including nested arrays) creates two coverage bins (to track 0->1 and -1->0 transitions). +(including nested arrays) creates two coverage bins (to track +.Cm 0 +-> +.Cm 1 +and +.Cm 1 +-> +.Cm 0 +transitions). .El Collecting each coverage type can be enabled separately at elaboration time: .Bd -literal -offset indent -$ nvc -e --cover=s,b,t +$ nvc -e --cover=statement,branch,toggle .Ed If no coverage type is specified as argument of .Fl -cover @@ -499,8 +504,90 @@ simulation is executed, NVC dumps coverage data into coverage database file (*.covdb). To merge coverage databases from multiple simulations, and generate hierarchy coverage report in HTML format, run: .Bd -literal -offset indent -$nvc -c --merge=merged.covdb --report= first.covdb second.covdb third.covdb ... +$nvc -c --merge=merged.covdb --report= \\ + first.covdb second.covdb third.covdb ... +.Ed +.Pp +NVC supports following additional options to control coverage collection: +.Bl -bullet +.It +.Cm count-from-undefined +- When set, NVC also counts toggles +.Cm U +-> +.Cm 1 +as +.Cm 0 +-> +.Cm 1 +and toggles +.Cm U +-> +.Cm 0 +as +.Cm 1 +-> +.Cm 0 +during toggle coverage collection. +.It +.Cm count-from-to-z +- When set, NVC also counts toggles from/to +.Cm Z +to either of +.Cm 0/1 +as valid +.Cm 0 +-> +.Cm 1 +or +.Cm 1 +-> +.Cm 0 +transitions. +.It +.Cm ignore-mems +- When set, NVC does not collect toggle coverage on multidimensional arrays or +nested arrays (array of array). +.It +.Cm ignore-arrays-from- +- When set, NVC does not collect toggle coverage for any array which is equal to +or larger than +.Cm +.El +.Pp +All of the options above are passed comma separated to +.Fl -cover +elaboration option, e.g.: +.Bd -literal -offset indent +$ nvc -e --cover=all,ignore_mems,count-from-undefined .Ed +Coverage collection on parts of the code can be ignored via dedicated pragma, e.g.: +.Bd -literal -offset indent +case (sel) is +when "00" => ... +when "01" => ... +when "10" => ... +when "11" => ... +-- coverage off +when others => report "ERROR" severity failure; +-- coverage on +end case; +.Ed +.Pp +in the example above, statement coverage for +.Cm report +statement and branch coverage for +.Cm others +choice will not be collected. + +Toggle coverage collection on specific signals can be also disabled, e.g.: +.Bd -literal -offset indent +-- coverage off +signal cnt : std_logic_vector(3 downto 0); +-- coverage on +.Ed +.Pp + .\" ------------------------------------------------------------ .\" Relaxed rules .\" ------------------------------------------------------------ diff --git a/src/lower.c b/src/lower.c index 7f897eaa..49cbbe9c 100644 --- a/src/lower.c +++ b/src/lower.c @@ -1506,14 +1506,15 @@ static void lower_branch_coverage(tree_t b, unsigned int flags, emit_cover_branch(hit_reg, tag->tag); } -static int32_t lower_toggle_tag_for(type_t type, tree_t where, ident_t prefix, int dims) +static int32_t lower_toggle_tag_for(type_t type, tree_t where, ident_t prefix, + int curr_dim) { type_t root = type; // Gets well known type for scalar and vectorized version of // standard types (std_[u]logic[_vector], signed, unsigned) - if (type_base_kind(type) == T_ARRAY) - root = type_elem(type); + while (type_base_kind(root) == T_ARRAY) + root = type_elem(root); root = type_base_recur(root); well_known_t known = is_well_known(type_ident(root)); @@ -1527,35 +1528,79 @@ static int32_t lower_toggle_tag_for(type_t type, tree_t where, ident_t prefix, i flags = COV_FLAG_TOGGLE_PORT; if (type_is_array(type)) { - tree_t r = range_of(type, dims - 1); - int32_t first_tag = 0; + int t_dims = dimension_of(type); + tree_t r = range_of(type, t_dims - curr_dim); + int32_t first_tag = -1; int64_t low, high; if (folded_bounds(r, &low, &high)) { assert(low <= high); - for (int64_t i = low; i <= high; i++) { + + int64_t first, last, i; + int inc; + + if (cover_skip_array_toggle(cover_tags, high - low + 1)) + return -1; + cover_inc_array_depth(cover_tags); + + switch (tree_subkind(r)) { + case RANGE_DOWNTO: + i = high; + first = high; + last = low; + inc = -1; + break; + case RANGE_TO: + i = low; + first = low; + last = high; + inc = +1; + break; + default: + fatal("Invalid subkind for range: %d", tree_subkind(r)); + } + + while (1) { char arr_index[16] = {0}; - int32_t tmp; + int32_t tmp = -1; checked_sprintf(arr_index, sizeof(arr_index), "(%lu)", i); ident_t arr_suffix = ident_prefix(prefix, ident_new(arr_index), '\0'); - if (dims == 1) { - tmp = cover_add_tag(where, arr_suffix, cover_tags, TAG_TOGGLE, flags)->tag; - if (i == low) - first_tag = tmp; - } - else { - tmp = lower_toggle_tag_for(type, where, arr_suffix, dims - 1); - if (i == low) - first_tag = tmp; + // On lowest dimension walk through elements, if elements are arrays, + // then start new (nested) recursion. + if (curr_dim == 1) { + type_t e_type = type_elem(type); + if (type_is_array(e_type)) + tmp = lower_toggle_tag_for(e_type, where, arr_suffix, dimension_of(e_type)); + else { + cover_tag_t *tag = cover_add_tag(where, arr_suffix, cover_tags, TAG_TOGGLE, flags); + if (tag) + tmp = tag->tag; + } } + + // Recurse to lower dimension + else + tmp = lower_toggle_tag_for(type, where, arr_suffix, curr_dim - 1); + + if (i == first) + first_tag = tmp; + if (i == last) + break; + + i += inc; } - } + cover_dec_array_depth(cover_tags); + } return first_tag; } - else - return cover_add_tag(where, NULL, cover_tags, TAG_TOGGLE, flags)->tag; + else { + cover_tag_t *tag = cover_add_tag(where, NULL, cover_tags, TAG_TOGGLE, flags); + if (tag == NULL) + return -1; + return tag->tag; + } } static void lower_toggle_coverage_cb(tree_t field, vcode_reg_t field_ptr, @@ -7621,7 +7666,7 @@ static void lower_hier_decl(tree_t decl) top_scope->hier = decl; if (cover_tags != NULL) - cover_exclude_from_pragmas(cover_tags, tree_ref(decl)); + cover_ignore_from_pragmas(cover_tags, tree_ref(decl)); } static void lower_decl(tree_t decl, vcode_unit_t context) diff --git a/src/nvc.c b/src/nvc.c index 3aa67afa..a12156dd 100644 --- a/src/nvc.c +++ b/src/nvc.c @@ -219,43 +219,56 @@ static void set_top_level(char **argv, int next_cmd) } } -static cover_mask_t parse_cover_mask(const char *str) +static void parse_cover_options(const char *str, cover_mask_t *mask, + int *array_limit) { - char prev = 0; - int n_chars = 0; - const char *full_cov_opt = str; - cover_mask_t rv = 0; - while (1) { + static const struct { + const char *name; + cover_mask_t mask; + } options[] = { + { "statement", COVER_MASK_STMT }, + { "toggle", COVER_MASK_TOGGLE }, + { "branch", COVER_MASK_BRANCH }, + { "all", COVER_MASK_ALL }, + { "count-from-undefined", COVER_MASK_TOGGLE_COUNT_FROM_UNDEFINED }, + { "count-from-to-z", COVER_MASK_TOGGLE_COUNT_FROM_TO_Z }, + { "ignore-mems", COVER_MASK_TOGGLE_IGNORE_MEMS }, + }; + + for (const char *start = str; ; str++) { if (*str == ',' || *str == '\0') { - if (prev == 's') - rv |= COVER_MASK_STMT; - else if (prev == 't') - rv |= COVER_MASK_TOGGLE; - else if (prev == 'b') - rv |= COVER_MASK_BRANCH; + if (strncmp(start, "ignore-arrays-from-", 19) == 0) + *array_limit = atoi(start + 19); else { - diag_t *d = diag_new(DIAG_FATAL, NULL); - diag_printf(d, "unknown coverage type '%c'", *str); - diag_hint(d, NULL, "valid coverage types are: \n" - " s (statement)\n" - " t (toggle)\n" - " b (branch)"); - diag_hint(d, NULL, "selected coverage types shall be " - "comma separated e.g $bold$--cover=s,t,b$$"); - diag_emit(d); - fatal_exit(EXIT_FAILURE); + int pos = 0; + for (; pos < ARRAY_LEN(options); pos++) { + if (!strncmp(options[pos].name, start, str - start)) + break; + } + + if (pos == ARRAY_LEN(options)) { + diag_t *d = diag_new(DIAG_FATAL, NULL); + diag_printf(d, "unknown coverage type '%.*s'", + (int)(str - start), start); + diag_hint(d, NULL, "valid coverage types are: \n" + " statement\n" + " toggle\n" + " branch"); + diag_hint(d, NULL, "selected coverage types shall be " + "comma separated e.g $bold$--cover=toggle,branch$$"); + diag_emit(d); + fatal_exit(EXIT_FAILURE); + } + else + *mask |= options[pos].mask; } - n_chars = 0; + if (*str == '\0') break; + + start = str + 1; } - n_chars++; - if (n_chars >= 3) - fatal("Invalid coverage type: '%s'.", full_cov_opt); - prev = *str; - str++; } - return rv; } static int elaborate(int argc, char **argv) @@ -270,6 +283,7 @@ static int elaborate(int argc, char **argv) }; cover_mask_t cover_mask = 0; + int cover_array_limit = 0; const int next_cmd = scan_cmd(2, argc, argv); int c, index = 0; const char *spec = "Vg:O:"; @@ -292,7 +306,7 @@ static int elaborate(int argc, char **argv) break; case 'c': if (optarg) - cover_mask = parse_cover_mask(optarg); + parse_cover_options(optarg, &(cover_mask), &(cover_array_limit)); else cover_mask = COVER_MASK_ALL; break; @@ -334,7 +348,7 @@ static int elaborate(int argc, char **argv) cover_tagging_t *cover = NULL; if (cover_mask != 0) - cover = cover_tags_init(cover_mask); + cover = cover_tags_init(cover_mask, cover_array_limit); vcode_unit_t vu = lower_unit(top, cover); progress("generating intermediate code"); @@ -953,13 +967,17 @@ static int coverage(int argc, char **argv) // Rest of inputs are coverage input files for (int i = optind; i < argc; i++) { - progress("Loading input coverage database: %s", argv[i]); fbuf_t *f = fbuf_open(argv[i], FBUF_IN, FBUF_CS_NONE); - if (i == optind) - cover = cover_read_tags(f); + if (f != NULL) { + progress("Loading input coverage database: %s", argv[i]); + if (i == optind) + cover = cover_read_tags(f); + else + cover_merge_tags(f, cover); + } else - cover_merge_tags(f, cover); + fatal("Could not open coverage database: %s", argv[i]); fbuf_close(f, NULL); } @@ -1019,9 +1037,9 @@ static void usage(void) " --cover=\tEnable code coverage collection.\n" " \t is comma separated list\n" " \tof coverage types to collect:\n" - " \t s - Statement coverage\n" - " \t t - Toggle coverage\n" - " \t b - Branch coverage\n" + " \t statement\n" + " \t toggle\n" + " \t branch\n" " \t Ommiting '=' collects all\n" " \t coverage types.\n" " --dump-llvm\tDump generated LLVM IR\n" diff --git a/src/rt/cover.c b/src/rt/cover.c index b910ba02..2d8977d3 100644 --- a/src/rt/cover.c +++ b/src/rt/cover.c @@ -57,7 +57,7 @@ typedef struct _cover_scope { int branch_label; int stmt_label; cover_scope_t *parent; - range_array_t exclude_lines; + range_array_t ignore_lines; } cover_scope_t; struct _cover_tagging { @@ -68,7 +68,10 @@ struct _cover_tagging { ident_t hier; tag_array_t tags; cover_mask_t mask; + int array_limit; + int array_depth; cover_scope_t *top_scope; + int level; }; typedef struct { @@ -162,6 +165,22 @@ bool cover_is_stmt(tree_t t) } } +bool cover_skip_array_toggle(cover_tagging_t *tagging, int a_size) +{ + assert (tagging); + + // Array is equal to or than configured limit + if (tagging->array_limit != 0 && a_size >= tagging->array_limit) + return true; + + // Array is multi-dimensional or nested + if (cover_enabled(tagging, COVER_MASK_TOGGLE_IGNORE_MEMS) && + tagging->array_depth > 0) + return true; + + return false; +} + fbuf_t *cover_open_lib_file(tree_t top, fbuf_mode_t mode, bool check_null) { char *dbname LOCAL = xasprintf("_%s.covdb", istr(tree_ident(top))); @@ -180,14 +199,14 @@ cover_tag_t *cover_add_tag(tree_t t, ident_t suffix, cover_tagging_t *ctx, assert (ctx != NULL); // TODO: need a better way to determine if a scope comes from an instance - cover_scope_t *excl_scope = ctx->top_scope; - for (; excl_scope->exclude_lines.count == 0 && excl_scope->parent; - excl_scope = excl_scope->parent) + cover_scope_t *ignore_scope = ctx->top_scope; + for (; ignore_scope->ignore_lines.count == 0 && ignore_scope->parent; + ignore_scope = ignore_scope->parent) ; const loc_t *loc = tree_loc(t); - for (int i = 0; i < excl_scope->exclude_lines.count; i++) { - line_range_t *lr = &(excl_scope->exclude_lines.items[i]); + for (int i = 0; i < ignore_scope->ignore_lines.count; i++) { + line_range_t *lr = &(ignore_scope->ignore_lines.items[i]); if (loc->first_line > lr->start && loc->first_line <= lr->end) return NULL; } @@ -196,7 +215,13 @@ cover_tag_t *cover_add_tag(tree_t t, ident_t suffix, cover_tagging_t *ctx, case TAG_STMT: cnt = &(ctx->next_stmt_tag); break; case TAG_BRANCH: cnt = &(ctx->next_branch_tag); break; case TAG_TOGGLE: cnt = &(ctx->next_toggle_tag); break; - case TAG_HIER: cnt = &(ctx->next_hier_tag); break; + case TAG_HIER: + cnt = &(ctx->next_hier_tag); + if (flags & COV_FLAG_HIER_DOWN) + ctx->level++; + else + ctx->level--; + break; default: fatal("Unknown coverage type: %d", kind); } @@ -223,7 +248,8 @@ cover_tag_t *cover_add_tag(tree_t t, ident_t suffix, cover_tagging_t *ctx, .data = 0, .flags = flags, .loc = *tree_loc(t), - .hier = hier + .hier = hier, + .level = ctx->level }; APUSH(ctx->tags, new); @@ -246,6 +272,8 @@ void cover_dump_tags(cover_tagging_t *ctx, fbuf_t *f, cover_dump_t dt, printf("Total tag count: %d\n", ctx->tags.count); #endif + write_u32(ctx->mask, f); + write_u32(ctx->array_limit, f); write_u32(ctx->next_stmt_tag, f); write_u32(ctx->next_branch_tag, f); write_u32(ctx->next_toggle_tag, f); @@ -286,6 +314,7 @@ void cover_dump_tags(cover_tagging_t *ctx, fbuf_t *f, cover_dump_t dt, #endif } write_u32(tag->flags, f); + write_u32(tag->level, f); loc_write(&(tag->loc), loc_wr); ident_write(tag->hier, ident_ctx); } @@ -296,10 +325,12 @@ void cover_dump_tags(cover_tagging_t *ctx, fbuf_t *f, cover_dump_t dt, ident_write_end(ident_ctx); } -cover_tagging_t *cover_tags_init(cover_mask_t mask) +cover_tagging_t *cover_tags_init(cover_mask_t mask, int array_limit) { cover_tagging_t *ctx = xcalloc(sizeof(cover_tagging_t)); ctx->mask = mask; + ctx->array_limit = array_limit; + ctx->level = 1; return ctx; } @@ -382,7 +413,7 @@ void cover_pop_scope(cover_tagging_t *tagging) assert(tagging->top_scope != NULL); - ACLEAR(tagging->top_scope->exclude_lines); + ACLEAR(tagging->top_scope->ignore_lines); cover_scope_t *tmp = tagging->top_scope->parent; free(tagging->top_scope); @@ -396,11 +427,11 @@ void cover_pop_scope(cover_tagging_t *tagging) assert(tagging->hier != NULL); } -void cover_exclude_from_pragmas(cover_tagging_t *tagging, tree_t unit) +void cover_ignore_from_pragmas(cover_tagging_t *tagging, tree_t unit) { assert(tagging->top_scope != NULL); - range_array_t *excl = &(tagging->top_scope->exclude_lines); + range_array_t *excl = &(tagging->top_scope->ignore_lines); bool state = true; const int npragmas = tree_pragmas(unit); for (int i = 0; i < npragmas; i++) { @@ -423,10 +454,31 @@ void cover_exclude_from_pragmas(cover_tagging_t *tagging, tree_t unit) } } -void cover_read_header(fbuf_t *f, cover_tagging_t *tagging) +void cover_inc_array_depth(cover_tagging_t *tagging) +{ + assert(tagging != NULL); + tagging->array_depth++; +#ifdef COVER_DEBUG + printf("Adding dimension: %d\n", tagging->array_depth); +#endif +} + +void cover_dec_array_depth(cover_tagging_t *tagging) { assert(tagging != NULL); + assert(tagging->array_depth > 0); + tagging->array_depth--; +#ifdef COVER_DEBUG + printf("Subtracting dimension: %d\n", tagging->array_depth); +#endif +} +static void cover_read_header(fbuf_t *f, cover_tagging_t *tagging) +{ + assert(tagging != NULL); + + tagging->mask = read_u32(f); + tagging->array_limit = read_u32(f); tagging->next_stmt_tag = read_u32(f); tagging->next_branch_tag = read_u32(f); tagging->next_toggle_tag = read_u32(f); @@ -443,6 +495,7 @@ void cover_read_one_tag(fbuf_t *f, loc_rd_ctx_t *loc_rd, tag->tag = read_u32(f); tag->data = read_u32(f); tag->flags = read_u32(f); + tag->level = read_u32(f); loc_read(&(tag->loc), loc_rd); tag->hier = ident_read(ident_ctx); @@ -543,53 +596,129 @@ void cover_count_tags(cover_tagging_t *tagging, int32_t *n_stmts, } } -void cover_toggle_event_cb(uint64_t now, rt_signal_t *s, rt_watch_t *w, - void *user) -{ +/////////////////////////////////////////////////////////////////////////////// +// Runtime handling +/////////////////////////////////////////////////////////////////////////////// -#ifdef COVER_DEBUG - printf("Time: %lu Callback on signal: %s\n", - now, istr(tree_ident(s->where))); -#endif +static inline void cover_toggle_check_0_1(uint8_t old, uint8_t new, + int32_t *toggle_mask) +{ + if (old == _0 && new == _1) + *toggle_mask |= 0x1; + if (old == _1 && new == _0) + *toggle_mask |= 0x2; +} - uint32_t sig_size = s->shared.size; - int32_t *toggle_mask = ((int32_t *)user) + sig_size - 1; +static inline void cover_toggle_check_u(uint8_t old, uint8_t new, + int32_t *toggle_mask) +{ + if (old == _U && new == _1) + *toggle_mask |= 0x1; + if (old == _U && new == _0) + *toggle_mask |= 0x2; +} - for (int i = 0; i < sig_size; i++) { - uint8_t new = ((uint8_t*)signal_value(s))[i]; - uint8_t old = ((uint8_t*)signal_last_value(s))[i]; +static inline void cover_toggle_check_z(uint8_t old, uint8_t new, + int32_t *toggle_mask) +{ + if (old == _0 && new == _Z) + *toggle_mask |= 0x1; + if (old == _Z && new == _1) + *toggle_mask |= 0x1; + + if (old == _1 && new == _Z) + *toggle_mask |= 0x2; + if (old == _Z && new == _0) + *toggle_mask |= 0x2; +} - // 0->1 - if (old == _0 && new == _1) - *toggle_mask |= 0x1; +static inline void cover_toggle_check_0_1_u(uint8_t old, uint8_t new, + int32_t *toggle_mask) +{ + cover_toggle_check_0_1(old, new, toggle_mask); + cover_toggle_check_u(old, new, toggle_mask); +} - // 1->0 - if (old == _1 && new == _0) - *toggle_mask |= 0x2; +static inline void cover_toggle_check_0_1_z(uint8_t old, uint8_t new, + int32_t *toggle_mask) +{ + cover_toggle_check_0_1(old, new, toggle_mask); + cover_toggle_check_z(old, new, toggle_mask); +} - toggle_mask--; - } +static inline void cover_toggle_check_0_1_u_z(uint8_t old, uint8_t new, + int32_t *toggle_mask) +{ + cover_toggle_check_0_1(old, new, toggle_mask); + cover_toggle_check_u(old, new, toggle_mask); + cover_toggle_check_z(old, new, toggle_mask); +} #ifdef COVER_DEBUG - printf("New signal value:\n"); - for (int i = 0; i < sig_size; i++) - printf("0x%x ", ((uint8_t*)signal_value(s))[i]); - printf("\n"); - - printf("Old signal value:\n"); - for (int i = 0; i < sig_size; i++) { - printf("0x%x ", ((const uint8_t *)signal_last_value(s))[i]); - } - printf("\n\n"); +#define COVER_TGL_CB_MSG(signal) \ + do { \ + printf("Time: %lu Callback on signal: %s\n", \ + now, istr(tree_ident(signal->where))); \ + } while (0); + +#define COVER_TGL_SIGNAL_DETAILS(signal, size) \ + do { \ + printf("New signal value:\n"); \ + for (int i = 0; i < size; i++) \ + printf("0x%x ", ((uint8_t*)signal_value(signal))[i]); \ + printf("\n"); \ + printf("Old signal value:\n"); \ + for (int i = 0; i < size; i++) \ + printf("0x%x ", ((const uint8_t *)signal_last_value(signal))[i]); \ + printf("\n\n"); \ + } while (0); + +#else +#define COVER_TGL_CB_MSG(signal) +#define COVER_TGL_SIGNAL_DETAILS(signal, size) #endif -} + +#define DEFINE_COVER_TOGGLE_CB(name, check_fnc) \ + static void name(uint64_t now, rt_signal_t *s, rt_watch_t *w, void *user) \ + { \ + uint32_t s_size = s->shared.size; \ + int32_t *toggle_mask = ((int32_t *)user); \ + COVER_TGL_CB_MSG(s) \ + for (int i = 0; i < s_size; i++) { \ + uint8_t new = ((uint8_t*)signal_value(s))[i]; \ + uint8_t old = ((uint8_t*)signal_last_value(s))[i]; \ + check_fnc(old, new, toggle_mask); \ + toggle_mask++; \ + } \ + COVER_TGL_SIGNAL_DETAILS(s, s_size) \ + } \ + + +DEFINE_COVER_TOGGLE_CB(cover_toggle_cb_0_1, cover_toggle_check_0_1) +DEFINE_COVER_TOGGLE_CB(cover_toggle_cb_0_1_u, cover_toggle_check_0_1_u) +DEFINE_COVER_TOGGLE_CB(cover_toggle_cb_0_1_z, cover_toggle_check_0_1_z) +DEFINE_COVER_TOGGLE_CB(cover_toggle_cb_0_1_u_z, cover_toggle_check_0_1_u_z) + void x_cover_setup_toggle_cb(sig_shared_t *ss, int32_t *toggle_mask) { rt_signal_t *s = container_of(ss, rt_signal_t, shared); rt_model_t *m = get_model(); - model_set_event_cb(m, s, cover_toggle_event_cb, toggle_mask, false); + cover_mask_t op_mask = get_coverage(m)->mask; + sig_event_fn_t fn = &cover_toggle_cb_0_1; + + if ((op_mask & COVER_MASK_TOGGLE_COUNT_FROM_UNDEFINED) && + (op_mask & COVER_MASK_TOGGLE_COUNT_FROM_TO_Z)) + fn = &cover_toggle_cb_0_1_u_z; + + else if (op_mask & COVER_MASK_TOGGLE_COUNT_FROM_UNDEFINED) + fn = &cover_toggle_cb_0_1_u; + + else if (op_mask & COVER_MASK_TOGGLE_COUNT_FROM_TO_Z) + fn = &cover_toggle_cb_0_1_z; + + model_set_event_cb(m, s, fn, toggle_mask, false); } @@ -866,20 +995,23 @@ static void cover_print_hierarchy_summary(FILE *f, cover_stats_t *stats, ident_t notef("code coverage results for: %s", istr(hier)); if (stats->total_stmts > 0) - notef(" statement: %.1f %%", - 100.0 * ((double)stats->hit_stmts) / stats->total_stmts); + notef(" statement: %.1f %% (%d/%d)", + 100.0 * ((double)stats->hit_stmts) / stats->total_stmts, + stats->hit_stmts, stats->total_stmts); else notef(" statement: N.A."); if (stats->total_branches > 0) - notef(" branch: %.1f %%", - 100.0 * ((double)stats->hit_branches) / stats->total_branches); + notef(" branch: %.1f %% (%d/%d)", + 100.0 * ((double)stats->hit_branches) / stats->total_branches, + stats->hit_branches, stats->total_branches); else notef(" branch: N.A."); if (stats->total_toggles > 0) - notef(" toggle: %.1f %%", - 100.0 * ((double)stats->hit_toggles) / stats->total_toggles); + notef(" toggle: %.1f %% (%d/%d)", + 100.0 * ((double)stats->hit_toggles) / stats->total_toggles, + stats->hit_toggles, stats->total_toggles); else notef(" toggle: N.A."); } @@ -972,9 +1104,9 @@ static void cover_print_chain(FILE *f, cover_chain_t *chn, tag_kind_t kind) if (kind == TAG_TOGGLE) { fprintf(f, ""); if (pair->flags & COV_FLAG_TOGGLE_TO_1) - fprintf(f, "Toggle to 1  "); + fprintf(f, "Toggle 0 -> 1  "); else if (pair->flags & COV_FLAG_TOGGLE_TO_0) - fprintf(f, "Toggle to 0  "); + fprintf(f, "Toggle 1 -> 0  "); fprintf(f, "on "); if (pair->tag->flags & COV_FLAG_TOGGLE_SIGNAL) @@ -982,7 +1114,11 @@ static void cover_print_chain(FILE *f, cover_chain_t *chn, tag_kind_t kind) else if (pair->tag->flags & COV_FLAG_TOGGLE_PORT) fprintf(f, "port:   "); fprintf(f, "
"); - fprintf(f, "%s", istr(ident_rfrom(pair->tag->hier, '.'))); + + ident_t sig_name = pair->tag->hier; + for (int i = 0; i < pair->tag->level; i++) + sig_name = ident_from(sig_name, '.'); + fprintf(f, "%s", istr(sig_name)); } fprintf(f, "

\n"); diff --git a/src/rt/cover.h b/src/rt/cover.h index f091542f..deb3c6a7 100644 --- a/src/rt/cover.h +++ b/src/rt/cover.h @@ -86,6 +86,9 @@ typedef struct _cover_tag { // Hierarchy path of the covered object ident_t hier; + + // Hierarchy level + int level; } cover_tag_t; typedef enum { @@ -106,29 +109,33 @@ typedef enum { } cover_dump_t; typedef enum { - COVER_MASK_STMT = (1 << 0), - COVER_MASK_BRANCH = (1 << 1), - COVER_MASK_TOGGLE = (1 << 2), + COVER_MASK_STMT = (1 << 0), + COVER_MASK_BRANCH = (1 << 1), + COVER_MASK_TOGGLE = (1 << 2), + COVER_MASK_TOGGLE_COUNT_FROM_UNDEFINED = (1 << 8), + COVER_MASK_TOGGLE_COUNT_FROM_TO_Z = (1 << 9), + COVER_MASK_TOGGLE_IGNORE_MEMS = (1 << 10) } cover_mask_t; #define COVER_MASK_ALL (COVER_MASK_STMT | COVER_MASK_BRANCH | COVER_MASK_TOGGLE) -cover_tagging_t *cover_tags_init(cover_mask_t mask); +cover_tagging_t *cover_tags_init(cover_mask_t mask, int array_limit); bool cover_enabled(cover_tagging_t *tagging, cover_mask_t mask); void cover_reset_scope(cover_tagging_t *tagging, ident_t hier); void cover_push_scope(cover_tagging_t *tagging, tree_t t); void cover_pop_scope(cover_tagging_t *tagging); -void cover_exclude_from_pragmas(cover_tagging_t *tagging, tree_t unit); +void cover_ignore_from_pragmas(cover_tagging_t *tagging, tree_t unit); + +void cover_inc_array_depth(cover_tagging_t *tagging); +void cover_dec_array_depth(cover_tagging_t *tagging); bool cover_is_stmt(tree_t t); +bool cover_skip_array_toggle(cover_tagging_t *tagging, int a_size); fbuf_t *cover_open_lib_file(tree_t top, fbuf_mode_t mode, bool check_null); -void cover_toggle_event_cb(uint64_t now, rt_signal_t *s, rt_watch_t *w, - void *user); - cover_tag_t *cover_add_tag(tree_t t, ident_t suffix, cover_tagging_t *ctx, tag_kind_t kind, uint32_t flags); diff --git a/src/rt/model.c b/src/rt/model.c index 3b2d369b..8d676aec 100644 --- a/src/rt/model.c +++ b/src/rt/model.c @@ -1802,6 +1802,11 @@ static void emit_coverage(rt_model_t *m) } } +cover_tagging_t *get_coverage(rt_model_t *m) +{ + return m->cover; +} + static void dump_one_signal(rt_model_t *m, rt_scope_t *scope, rt_signal_t *s, tree_t alias) { diff --git a/src/rt/model.h b/src/rt/model.h index cdb2678e..e83a17a1 100644 --- a/src/rt/model.h +++ b/src/rt/model.h @@ -40,6 +40,7 @@ void model_set_timeout_cb(rt_model_t *m, uint64_t when, timeout_fn_t fn, rt_model_t *get_model(void); rt_model_t *get_model_or_null(void); rt_proc_t *get_active_proc(void); +cover_tagging_t *get_coverage(rt_model_t *m); rt_scope_t *find_scope(rt_model_t *m, tree_t container); rt_scope_t *child_scope(rt_scope_t *scope, tree_t decl); diff --git a/test/regress/cover1.sh b/test/regress/cover1.sh index baab7e0c..442d8183 100644 --- a/test/regress/cover1.sh +++ b/test/regress/cover1.sh @@ -4,11 +4,10 @@ pwd which nvc which fstdump -nvc -a $TESTDIR/regress/cover1.vhd -e --cover cover1 -r +nvc -a $TESTDIR/regress/cover1.vhd -e --cover=statement cover1 -r nvc -c --report html work/_WORK.COVER1.elab.covdb 2>&1 | tee out.txt -# TODO: perhaps should be index.html? if [ ! -f html/index.html ]; then echo "missing coverage report" exit 1 diff --git a/test/regress/cover1.vhd b/test/regress/cover1.vhd index 9ac911dc..fa926073 100644 --- a/test/regress/cover1.vhd +++ b/test/regress/cover1.vhd @@ -2,27 +2,112 @@ entity cover1 is end entity; architecture test of cover1 is - signal s : integer; -begin - process is - variable v : integer; + signal my_signal : integer; + + procedure my_global_procedure is begin - v := 1; - s <= 2; - wait for 1 ns; - if s = 2 or s > 10 then - v := 3; + rpt1: report "Report within GLOBAL procedure"; + end procedure; + + function my_global_function(i : integer) return boolean is + begin + rpt2: report "Report within GLOBAL function"; + + if (i mod 2 = 0) then + return true; else - v := 2; + return false; end if; - while v > 0 loop - if v mod 2 = 0 then - v := v - 1; + end function; + +begin + + test_proc : process + variable cnt : integer := 0; + variable rv : boolean; + + procedure my_local_procedure is + begin + rpt3: report "Report within LOCAL procedure"; + end procedure; + + function my_local_function(i : integer) return boolean is + begin + rpt4: report "Report within LOCAL function"; + if (i mod 2 = 0) then + return true; else - v := (v / 2) * 2; + return false; end if; + end function; + + begin + + -- T_IF, T_FOR, T_CASE + loop1: for i in 0 to 4 loop + if_cond_1: if (i = 0) then + report "T_IF, i = 0"; + elsif (i = 1) then + report "T_IF, i = 1"; + elsif (i = 2) then + report "T_IF, i = 2"; + elsif (i = 3) then + report "T_IF, i = 3"; + elsif (i = 4) then + report "T_IF, i = 4"; + elsif (i = 10) then + -- Some unreachable statement to get also some uncovered statements. + assert (False); + + else + -- coverage off + assert (False) report "PRAGMA COVER OFF statement"; + -- coverage on + end if; + + case_cond_1: case i is + when 0 => + report "T_CASE, i = 0"; + when 1 => + report "T_CASE, i = 1"; + when others => + report "T_CASE, i = others"; + end case; + end loop; + + -- T_VAR_ASSIGN, T_WHILE, T_NEXT, T_EXIT + cnt := 0; + while_loop: while (true) loop + cnt := cnt + 1; + + if (cnt = 1) then + next; + end if; + + if (cnt = 10) then + exit; + end if; + report "T_WHILE, i = " & integer'image(cnt); end loop; + + -- T_SIGNAL_ASSIGN, T_ASSERT, T_WAIT + sign_assign_1: my_signal <= 1; + wait_1: wait for 0 ns; + sign_assign_2: my_signal <= 0; + wait_2: wait for 0 ns; + assert_1: assert(my_signal = 0); + + -- T_PROC_CALL, T_FUNC_CALL, T_RETURN + proc_call_global: my_global_procedure; + rv := my_global_function(cnt); + rv := my_global_function(cnt + 3); + proc_call_local: my_local_procedure; + rv := my_local_function(cnt + 2); + rv := my_local_function(cnt + 5); + + wait for 1 ns; + wait; end process; diff --git a/test/regress/cover2.sh b/test/regress/cover2.sh new file mode 100644 index 00000000..c4a1c36a --- /dev/null +++ b/test/regress/cover2.sh @@ -0,0 +1,16 @@ +set -xe + +pwd +which nvc +which fstdump + +nvc -a $TESTDIR/regress/cover2.vhd -e --cover=toggle cover2 -r + +nvc -c --report html work/_WORK.COVER2.elab.covdb 2>&1 | tee out.txt + +if [ ! -f html/index.html ]; then + echo "missing coverage report" + exit 1 +fi + +diff -u $TESTDIR/regress/gold/cover2.txt out.txt diff --git a/test/regress/cover2.vhd b/test/regress/cover2.vhd new file mode 100644 index 00000000..74382c20 --- /dev/null +++ b/test/regress/cover2.vhd @@ -0,0 +1,186 @@ +library ieee; +use ieee.std_logic_1164.all; + +entity sub_module is + port ( + a : in std_logic; + b : in std_logic; + c : out std_logic + ); +end entity; + +architecture test of sub_module is + + type t_record_in_submodule is record + elem : std_logic; + end record; + + signal record_in_submodule : t_record_in_submodule; + +begin + + tgl_proc : process (a,b) + begin + c <= a xor b; + end process; + +end architecture; + +library ieee; +use ieee.std_logic_1164.all; + +entity cover2 is +end entity; + +architecture test of cover2 is + + -- Full toggle + signal t1_a : std_logic := '0'; + signal t1_b : std_logic := '0'; + signal t1_c : std_logic; + + -- Toggle only 0 -> 1 + signal t2_a : std_logic := '0'; + signal t2_b : std_logic := '0'; + signal t2_c : std_logic := '0'; + + -- Toggle only 1 -> 0 + signal t3_a : std_logic := '1'; + signal t3_b : std_logic := '0'; + signal t3_c : std_logic; + + -- Toggle on multi-dimensional array + type t_tensor is array (natural range <>, natural range <>, natural range <>) of std_logic; + signal tensor : t_tensor(0 to 2, 2 downto 0, 2 downto 0) := (others => (others => ('1','0','1'))); + + -- Toggle on nested array + type t_memory_2d is array (2 downto 0) of std_logic_vector(0 to 3); + signal memory_2d : t_memory_2d := (others => x"A"); + + type t_memory_3d is array (2 downto 0) of t_memory_2d; + signal memory_3d : t_memory_3d := (others => (others => x"5")); + + -- Toggle on signals within record and nested record + type t_nested_record is record + a : natural; + b : std_logic; + c : std_logic_vector(2 downto 0); + end record; + + type t_record is record + x : std_logic; + y : std_logic_vector(3 downto 0); + z : t_nested_record; + end record; + + signal my_rec : t_record := ('0', "0000", (0, '0', "000")); + + -- coverage off + signal my_ignored_signal : std_logic := '0'; + -- coverage on + +begin + + -- Toggle on ports - Full + sub_module_inst : entity work.sub_module + port map ( + a => t1_a, + b => t1_b, + c => t1_c + ); + + -- Toggle on ports - 0 -> 1 only + sub_module_inst_2 : entity work.sub_module + port map ( + a => t2_a, + b => t2_b, + c => t2_c + ); + + -- Toggle on ports - 1 -> 0 only + sub_module_inst_3 : entity work.sub_module + port map ( + a => t3_a, + b => t3_b, + c => t3_c + ); + + + test_proc : process + begin + -- Toggle 0 -> 1 and 1 -> 0 on both inputs and outputs + wait for 1 ns; + t1_a <= '1'; + wait for 1 ns; + t1_b <= '1'; + wait for 1 ns; + t1_a <= '0'; + wait for 1 ns; + t1_b <= '0'; + + -- Toggle 0 -> 1 only + wait for 1 ns; + t2_a <= '1'; + wait for 1 ns; + + -- Toggle 1 -> 0 only + wait for 1 ns; + t3_a <= '0'; + wait for 1 ns; + + -- Mixed toggle on multi-dimensional array + for i in 0 to 2 loop + for j in 0 to 2 loop + for k in 0 to 2 loop + tensor(i,j,k) <= not tensor(i,j,k); + wait for 1 ns; + end loop; + end loop; + end loop; + + -- Mixed toggle on nested array + for i in 0 to 2 loop + for j in 0 to 3 loop + if (i = j) then + memory_2d(i)(j) <= not memory_2d(i)(j); + wait for 1 ns; + end if; + end loop; + end loop; + + for i in 0 to 2 loop + for j in 0 to 3 loop + for k in 0 to 3 loop + if (i = j and j = k and k = i) then + memory_3d(i)(j)(k) <= not memory_3d(i)(j)(k); + wait for 1 ns; + end if; + end loop; + end loop; + end loop; + + -- Toggle 0 -> 1 on signals within record + my_rec.x <= '1'; + my_rec.y <= "0101"; + wait for 1 ns; + + my_rec.z.b <= '1'; + my_rec.z.c <= "101"; + wait for 1 ns; + + wait; + end process; + + -- Toggle on the process just to make sure that toggle callback + -- is truly not called and does not mess something up. + process + begin + wait for 1 ns; + my_ignored_signal <= '1'; + wait for 1 ns; + my_ignored_signal <= '0'; + wait for 1 ns; + wait; + end process; + +end architecture; diff --git a/test/regress/cover3.sh b/test/regress/cover3.sh new file mode 100644 index 00000000..6ef8187e --- /dev/null +++ b/test/regress/cover3.sh @@ -0,0 +1,16 @@ +set -xe + +pwd +which nvc +which fstdump + +nvc -a $TESTDIR/regress/cover3.vhd -e --cover=branch cover3 -r + +nvc -c --report html work/_WORK.COVER3.elab.covdb 2>&1 | tee out.txt + +if [ ! -f html/index.html ]; then + echo "missing coverage report" + exit 1 +fi + +diff -u $TESTDIR/regress/gold/cover3.txt out.txt diff --git a/test/regress/cover3.vhd b/test/regress/cover3.vhd new file mode 100644 index 00000000..864ef120 --- /dev/null +++ b/test/regress/cover3.vhd @@ -0,0 +1,136 @@ + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity cover3 is +end entity; + +architecture test of cover3 is + + signal cnt : integer := 0; + signal cnt2 : integer := 0; + signal cnt3 : unsigned(3 downto 0) := "0000"; +begin + + -- Test control + process + begin + wait for 1 ns; + if (cnt < 20) then + cnt <= cnt + 1; + else + wait for 1 ns; + wait; + end if; + end process; + + process + begin + cnt2 <= 1; + wait for 1 ns; + cnt2 <= 2; + wait for 1 ns; + cnt2 <= 3; + wait for 1 ns; + wait; + end process; + + -- IF / ELSE + process(cnt) + begin + l_if_1: if (cnt = 0) then report "IF1: CNT = 0"; + elsif (cnt = 1) then report "IF1: CNT = 1"; + -- coverage off + elsif (cnt = 10) then report "IF1: CNT = 10"; + -- coverage on + else report "IF1: CNT other"; + end if; + + -- With implicit else, only counts due to "false of" with last valued branch + l_if_2: if (cnt = 10) then report "IF2: CNT = 10"; + elsif (cnt = 11) then report "IF2: CNT = 11"; + end if; + + -- With some branches uncovered + l_if_3: if (cnt = 100) then assert (False); + elsif (cnt = 19) then report "IF3: CNT = 19"; + elsif (cnt = 70) then assert (False); + end if; + end process; + + -- T_CASE with scalar + process(cnt) + begin + + -- Case with all options covered + l_case_1: case (cnt) is + when 0 => report "CASE1: CNT = 0"; + -- coverage off + when 1 => report "CASE1: CNT = 1"; + -- coverage on + when 2 => report "CASE1: CNT = 2"; + when 3 => report "CASE1: CNT = 3"; + when others => report "CASE1: CNT = OTHERS"; + end case; + + -- Case with some explicit branches uncovered + l_case_1: case (cnt) is + when 28 => assert (False); + when 29 => assert (False); + when 7 => report "CASE2: CNT = 7"; + when 8 => report "CASE2: CNT = 8"; + when others => report "CASE2: CNT = OTHERS"; + end case; + + end process; + + + process(cnt2) + begin + -- Case with only others uncovered + l_case_3: case (cnt2) is + when 0 => report "CASE3: CNT2 = 0"; + when 1 => report "CASE3: CNT2 = 1"; + when 2 => report "CASE3: CNT2 = 2"; + when 3 => report "CASE3: CNT2 = 3"; + when others => assert (False); + end case; + + end process; + + process + begin + wait for 1 ns; + if (cnt3 < "0111") then + cnt3 <= cnt3 + "0001"; + else + wait; + end if; + end process; + + process(cnt3) + begin + -- Case on array, others covered, some options uncovered + l_case_4: case (cnt3) is + when "0000" => report "CASE4: 0000"; + when "0001" => report "CASE4: 0001"; + when "0010" => report "CASE4: 0010"; + when "0ZZ1" => assert(False); + when "0011" => report "CASE4: 0011"; + when "0101" | "0110" | "0111" => report "CASE4: 5-7"; + when "1000" => assert(False); + when others => report "CASE4: others (hit by 4)"; + end case; + end process; + + process(cnt3) + begin + -- Case on array, only others uncovered + l_case_4: case (cnt3) is + when "0000" | "0001" | "0010" | "0011" | "0100" | "0101" | "0110" | "0111" => report "CASE5: Coverable"; + when others => assert(False); + end case; + end process; + +end architecture; diff --git a/test/regress/cover4.sh b/test/regress/cover4.sh new file mode 100644 index 00000000..4dad5dea --- /dev/null +++ b/test/regress/cover4.sh @@ -0,0 +1,16 @@ +set -xe + +pwd +which nvc +which fstdump + +nvc -a $TESTDIR/regress/cover4.vhd -e --cover=branch cover4 -r + +nvc -c --report html work/_WORK.COVER4.elab.covdb 2>&1 | tee out.txt + +if [ ! -f html/index.html ]; then + echo "missing coverage report" + exit 1 +fi + +diff -u $TESTDIR/regress/gold/cover4.txt out.txt diff --git a/test/regress/cover4.vhd b/test/regress/cover4.vhd new file mode 100644 index 00000000..c6ff8779 --- /dev/null +++ b/test/regress/cover4.vhd @@ -0,0 +1,55 @@ + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity cover4 is +end entity; + +architecture test of cover4 is + + signal cnt : integer := 0; + signal res_1 : std_logic_vector(3 downto 0); + signal res_2 : std_logic_vector(3 downto 0); + +begin + + -- Test control + process + begin + wait for 1 ns; + if (cnt < 20) then + cnt <= cnt + 1; + else + wait for 1 ns; + wait; + end if; + end process; + + -- When else + res_1 <= + "0000" when (cnt = 0) else + "0001" when (cnt = 1) else + -- coverage off + "0010" when (cnt = 2) else + -- coverage on + "0011" when (cnt = 3) else + "0100" when (cnt = 4) else + "0101" when (cnt = 5) else + "0111" when (cnt = 6) else + "XXXX"; + + -- With select + with (cnt) select res_2 <= + "0000" when 0, + "0001" when 1, + "0010" when 2, + "0011" when 3, + -- coverage off + "0100" when 4, + -- coverage on + "0101" when 5, + "0111" when 6, + "XXXX" when others; + +end architecture; diff --git a/test/regress/cover5.sh b/test/regress/cover5.sh new file mode 100644 index 00000000..d128e998 --- /dev/null +++ b/test/regress/cover5.sh @@ -0,0 +1,30 @@ +set -xe + +pwd +which nvc +which fstdump + +nvc -a $TESTDIR/regress/cover5.vhd -e -gG_VAL=1 --cover=all cover5 -r +mv work/_WORK.COVER5.elab.covdb DB1.covdb +nvc -c --report html DB1.covdb 2>&1 | tee out.txt + +nvc -a $TESTDIR/regress/cover5.vhd -e -gG_VAL=2 --cover=all cover5 -r +mv work/_WORK.COVER5.elab.covdb DB2.covdb +nvc -c --report html DB2.covdb 2>&1 | tee -a out.txt + +nvc -a $TESTDIR/regress/cover5.vhd -e -gG_VAL=3 --cover=all cover5 -r +mv work/_WORK.COVER5.elab.covdb DB3.covdb +nvc -c --report html DB3.covdb 2>&1 | tee -a out.txt + +nvc -a $TESTDIR/regress/cover5.vhd -e -gG_VAL=4 --cover=all cover5 -r +mv work/_WORK.COVER5.elab.covdb DB4.covdb +nvc -c --report html DB4.covdb 2>&1 | tee -a out.txt + +nvc -c --merge DB_MERGED.covdb --report html DB1.covdb DB2.covdb DB3.covdb DB4.covdb 2>&1 | tee -a out.txt + +if [ ! -f html/index.html ]; then + echo "missing coverage report" + exit 1 +fi + +diff -u $TESTDIR/regress/gold/cover5.txt out.txt diff --git a/test/regress/cover5.vhd b/test/regress/cover5.vhd new file mode 100644 index 00000000..5efcbd7e --- /dev/null +++ b/test/regress/cover5.vhd @@ -0,0 +1,64 @@ + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity cover5 is + generic ( + G_VAL : integer + ); +end entity; + +architecture test of cover5 is + + signal vect : std_logic_vector(15 downto 0) := x"AAAA"; + + -- Needed to pass via signal to avoid optimizing out parts of + -- the if/else conditions. + signal cnt : integer := G_VAL; + +begin + + -- Check on statements and branches + process(cnt) + begin + l_if1: if (cnt = 1) then report "IF1: cnt = 1"; + elsif (cnt = 2) then report "IF1: cnt = 2"; + elsif (cnt = 3) then report "IF1: cnt = 3"; + else report "IF1: cnt = others"; + end if; + end process; + + -- Check on toggles + process + begin + wait for 1 ns; + for i in 1 to G_VAL loop + vect(i * G_VAL - 1) <= not vect(i * G_VAL - 1); + end loop; + wait for 1 ns; + wait; + end process; + + -- Check with different amount of statements in each branch + -- to get different coverage results for each run! + process(cnt) + begin + l_if2: if (cnt = 1) then + report "IF2: cnt = 1/1"; + elsif (cnt = 2) then + report "IF2: cnt = 1/2"; + report "IF2: cnt = 2/2"; + elsif (cnt = 3) then + report "IF2: cnt = 1/3"; + report "IF2: cnt = 2/3"; + report "IF2: cnt = 3/3"; + elsif (cnt = 4) then + report "IF2: cnt = 1/4"; + report "IF2: cnt = 2/4"; + report "IF2: cnt = 3/4"; + report "IF2: cnt = 4/4"; + end if; + end process; + +end architecture; diff --git a/test/regress/cover6.sh b/test/regress/cover6.sh new file mode 100644 index 00000000..eb5806ef --- /dev/null +++ b/test/regress/cover6.sh @@ -0,0 +1,24 @@ +set -xe + +pwd +which nvc +which fstdump + +# Track only from U +nvc -a $TESTDIR/regress/cover6.vhd -e --cover=toggle,count-from-undefined cover6 -r +nvc -c --report html work/_WORK.COVER6.elab.covdb 2>&1 | tee out.txt + +# Track only from/to Z +nvc -a $TESTDIR/regress/cover6.vhd -e --cover=toggle,count-from-to-z cover6 -r +nvc -c --report html work/_WORK.COVER6.elab.covdb 2>&1 | tee -a out.txt + +# Track both +nvc -a $TESTDIR/regress/cover6.vhd -e --cover=toggle,count-from-undefined,count-from-to-z cover6 -r +nvc -c --report html work/_WORK.COVER6.elab.covdb 2>&1 | tee -a out.txt + +if [ ! -f html/index.html ]; then + echo "missing coverage report" + exit 1 +fi + +diff -u $TESTDIR/regress/gold/cover6.txt out.txt diff --git a/test/regress/cover6.vhd b/test/regress/cover6.vhd new file mode 100644 index 00000000..788032ba --- /dev/null +++ b/test/regress/cover6.vhd @@ -0,0 +1,53 @@ + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity cover6 is +end entity; + +architecture test of cover6 is + + signal from_u_to_1 : std_logic := 'U'; + signal from_u_to_0 : std_logic := 'U'; + + signal from_z_to_1 : std_logic := 'Z'; + signal from_z_to_0 : std_logic := 'Z'; + + signal from_1_to_z : std_logic := '1'; + signal from_0_to_z : std_logic := '0'; + +begin + + process + begin + wait for 1 ns; + + -- U -> 1 + from_u_to_1 <= '1'; + wait for 1 ns; + + -- U -> 0 + from_u_to_0 <= '0'; + wait for 1 ns; + + -- Z -> 1 + from_z_to_1 <= '1'; + wait for 1 ns; + + -- Z -> 0 + from_z_to_0 <= '0'; + wait for 1 ns; + + -- 1 -> Z + from_1_to_z <= 'Z'; + wait for 1 ns; + + -- 0 -> Z + from_0_to_z <= 'Z'; + wait for 1 ns; + + wait; + end process; + +end architecture; diff --git a/test/regress/gold/cover1.txt b/test/regress/gold/cover1.txt index 839313f6..605ed885 100644 --- a/test/regress/gold/cover1.txt +++ b/test/regress/gold/cover1.txt @@ -1,4 +1,4 @@ ** Note: code coverage results for: WORK.COVER1 -** Note: statement: 90.9 % -** Note: branch: 83.3 % +** Note: statement: 97.7 % (42/43) +** Note: branch: N.A. ** Note: toggle: N.A. diff --git a/test/regress/gold/cover2.txt b/test/regress/gold/cover2.txt new file mode 100644 index 00000000..91ef3200 --- /dev/null +++ b/test/regress/gold/cover2.txt @@ -0,0 +1,4 @@ +** Note: code coverage results for: WORK.COVER2 +** Note: statement: N.A. +** Note: branch: N.A. +** Note: toggle: 28.1 % (59/210) diff --git a/test/regress/gold/cover3.txt b/test/regress/gold/cover3.txt new file mode 100644 index 00000000..c343dffa --- /dev/null +++ b/test/regress/gold/cover3.txt @@ -0,0 +1,4 @@ +** Note: code coverage results for: WORK.COVER3 +** Note: statement: N.A. +** Note: branch: 84.3 % (43/51) +** Note: toggle: N.A. diff --git a/test/regress/gold/cover4.txt b/test/regress/gold/cover4.txt new file mode 100644 index 00000000..8155a7df --- /dev/null +++ b/test/regress/gold/cover4.txt @@ -0,0 +1,4 @@ +** Note: code coverage results for: WORK.COVER4 +** Note: statement: N.A. +** Note: branch: 100.0 % (21/21) +** Note: toggle: N.A. diff --git a/test/regress/gold/cover5.txt b/test/regress/gold/cover5.txt new file mode 100644 index 00000000..7dbc08d2 --- /dev/null +++ b/test/regress/gold/cover5.txt @@ -0,0 +1,20 @@ +** Note: code coverage results for: WORK.COVER5 +** Note: statement: 42.9 % (9/21) +** Note: branch: 14.3 % (2/14) +** Note: toggle: 3.1 % (1/32) +** Note: code coverage results for: WORK.COVER5 +** Note: statement: 47.6 % (10/21) +** Note: branch: 28.6 % (4/14) +** Note: toggle: 6.2 % (2/32) +** Note: code coverage results for: WORK.COVER5 +** Note: statement: 52.4 % (11/21) +** Note: branch: 42.9 % (6/14) +** Note: toggle: 9.4 % (3/32) +** Note: code coverage results for: WORK.COVER5 +** Note: statement: 57.1 % (12/21) +** Note: branch: 50.0 % (7/14) +** Note: toggle: 12.5 % (4/32) +** Note: code coverage results for: WORK.COVER5 +** Note: statement: 100.0 % (21/21) +** Note: branch: 92.9 % (13/14) +** Note: toggle: 28.1 % (9/32) diff --git a/test/regress/gold/cover6.txt b/test/regress/gold/cover6.txt new file mode 100644 index 00000000..21f552c1 --- /dev/null +++ b/test/regress/gold/cover6.txt @@ -0,0 +1,12 @@ +** Note: code coverage results for: WORK.COVER6 +** Note: statement: N.A. +** Note: branch: N.A. +** Note: toggle: 16.7 % (2/12) +** Note: code coverage results for: WORK.COVER6 +** Note: statement: N.A. +** Note: branch: N.A. +** Note: toggle: 33.3 % (4/12) +** Note: code coverage results for: WORK.COVER6 +** Note: statement: N.A. +** Note: branch: N.A. +** Note: toggle: 50.0 % (6/12) diff --git a/test/regress/testlist.txt b/test/regress/testlist.txt index 84b5031c..3aaccd79 100644 --- a/test/regress/testlist.txt +++ b/test/regress/testlist.txt @@ -692,3 +692,8 @@ issue570 normal,2008 issue571 normal,2008,relaxed operator6 normal,2008 vecorder2 normal,2008 +cover2 cover,shell +cover3 cover,shell +cover4 cover,shell +cover5 cover,shell +cover6 cover,shell diff --git a/test/test_lower.c b/test/test_lower.c index db5a8836..9fa760a1 100644 --- a/test/test_lower.c +++ b/test/test_lower.c @@ -2008,7 +2008,7 @@ START_TEST(test_cover) tree_t e = run_elab(); - cover_tagging_t *tagging = cover_tags_init(COVER_MASK_ALL); + cover_tagging_t *tagging = cover_tags_init(COVER_MASK_ALL, 0); lower_unit(e, tagging); vcode_unit_t v0 = find_unit("WORK.COVER.P1"); -- 2.39.2