From 1ad3f895f838d9175bb69e188a972be03877bac9 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sat, 1 Jul 2023 09:36:55 +0100 Subject: [PATCH] Store coverage scopes directly in database --- src/option.c | 1 + src/option.h | 1 + src/rt/cover.c | 627 ++++++++++++++++++++++++++++++------------------- src/rt/cover.h | 6 +- test/questa.sh | 21 +- 5 files changed, 408 insertions(+), 248 deletions(-) diff --git a/src/option.c b/src/option.c index bd68f4d2..5ca61ccd 100644 --- a/src/option.c +++ b/src/option.c @@ -164,4 +164,5 @@ void set_default_options(void) opt_set_str(OPT_PSL_VERBOSE, getenv("NVC_PSL_VERBOSE")); opt_set_int(OPT_PSL_COMMENTS, 0); opt_set_int(OPT_NO_COLLAPSE, 0); + opt_set_int(OPT_COVER_VERBOSE, get_int_env("NVC_COVER_VERBOSE", 0)); } diff --git a/src/option.h b/src/option.h index 14f3ec0e..ef7db659 100644 --- a/src/option.h +++ b/src/option.h @@ -60,6 +60,7 @@ typedef enum { OPT_PSL_VERBOSE, OPT_PSL_COMMENTS, OPT_NO_COLLAPSE, + OPT_COVER_VERBOSE, OPT_LAST_NAME } opt_name_t; diff --git a/src/rt/cover.c b/src/rt/cover.c index 880e0fd9..081a5a3f 100644 --- a/src/rt/cover.c +++ b/src/rt/cover.c @@ -1,5 +1,5 @@ // -// Copyright (C) 2013-2022 Nick Gasson +// Copyright (C) 2013-2023 Nick Gasson // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -35,6 +35,7 @@ #include #include #include +#include //#define COVER_DEBUG_EMIT //#define COVER_DEBUG_DUMP @@ -62,12 +63,20 @@ typedef struct _cover_exclude_ctx cover_exclude_ctx_t; typedef struct _cover_rpt_buf cover_rpt_buf_t; typedef struct _cover_spec cover_spec_t; +typedef enum { + CSCOPE_UNKNOWN, + CSCOPE_INSTANCE, +} scope_type_t; + typedef struct _cover_scope { + scope_type_t type; ident_t name; int branch_label; int stmt_label; int expression_label; cover_scope_t *parent; + ptr_list_t children; + tag_array_t tags; range_array_t ignore_lines; ident_t block_name; int sig_pos; @@ -89,7 +98,6 @@ struct _cover_spec { struct _cover_tagging { int next_tag; ident_t hier; - tag_array_t tags; cover_mask_t mask; int array_limit; int array_depth; @@ -97,6 +105,7 @@ struct _cover_tagging { cover_rpt_buf_t *rpt_buf; cover_spec_t *spec; cover_scope_t *top_scope; + cover_scope_t *root_scope; }; typedef struct { @@ -147,7 +156,6 @@ struct _cover_report_ctx { cover_stats_t flat_stats; cover_stats_t nested_stats; cover_report_ctx_t *parent; - cover_tag_t *start_tag; cover_chain_t ch_stmt; cover_chain_t ch_branch; cover_chain_t ch_toggle; @@ -206,6 +214,16 @@ typedef enum { PAIR_LAST = 3 } cov_pair_kind_t; +typedef enum { + CTRL_PUSH_SCOPE, + CTRL_POP_SCOPE, + CTRL_END_OF_FILE, +} cov_control_t; + +static void cover_report_children(cover_report_ctx_t *ctx, + cover_scope_t *s, const char *dir, + FILE *summf, int *skipped); + bool cover_is_stmt(tree_t t) { switch (tree_kind(t)) { @@ -346,6 +364,9 @@ cover_tag_t *cover_add_tag(tree_t t, ident_t suffix, cover_tagging_t *ctx, printf("\n\n"); #endif + if (kind == TAG_HIER) + ctx->top_scope->type = CSCOPE_INSTANCE; // TODO: temporary hack + cover_tag_t new = { .kind = kind, .tag = ctx->next_tag++, @@ -358,61 +379,113 @@ cover_tag_t *cover_add_tag(tree_t t, ident_t suffix, cover_tagging_t *ctx, .sig_pos = ctx->top_scope->sig_pos, }; - APUSH(ctx->tags, new); + APUSH(ctx->top_scope->tags, new); - return AREF(ctx->tags, ctx->tags.count - 1); + return AREF(ctx->top_scope->tags, ctx->top_scope->tags.count - 1); } -void cover_dump_tags(cover_tagging_t *ctx, fbuf_t *f, cover_dump_t dt, - const int32_t *counts) +LCOV_EXCL_START +static void cover_debug_dump(cover_scope_t *s, int indent) { + color_printf("%*s$!blue$%s", indent, "", istr(s->name)); + switch (s->type) { + case CSCOPE_INSTANCE: color_printf(" : instance"); break; + default: break; + } + color_printf("$$\n"); -#ifdef COVER_DEBUG_DUMP - printf("Dumping coverage entries:\n"); - printf("Number of tags: %d\n", ctx->next_tag); - printf("Total tag count: %d\n", ctx->tags.count); -#endif + for (int i = 0; i < s->tags.count; i++) { + cover_tag_t *tag = &(s->tags.items[i]); - write_u32(ctx->mask, f); - write_u32(ctx->array_limit, f); - write_u32(ctx->next_tag, f); + char *path LOCAL = xstrdup(loc_file_str(&tag->loc)); + printf("%*s%d: %s %s %s:%d => %x\n", indent + 2, "", tag->tag, + tag_kind_str[tag->kind], istr(tag->hier), + basename(path), tag->loc.first_line, tag->data); + } - loc_wr_ctx_t *loc_wr = loc_write_begin(f); - ident_wr_ctx_t ident_ctx = ident_write_begin(f); + for (list_iter(cover_scope_t *, c, s->children)) + cover_debug_dump(c, indent + 2); +} +LCOV_EXCL_STOP - for (int i = 0; i < ctx->tags.count; i++) { - cover_tag_t *tag = &(ctx->tags.items[i]); +static void cover_merge_one_tag(cover_tag_t *tag, int32_t data) +{ + switch (tag->kind) { + case TAG_STMT: + tag->data += data; + break; + case TAG_TOGGLE: + case TAG_BRANCH: + case TAG_EXPRESSION: + tag->data |= data; + break; + default: + break; + } +} - write_u8(tag->kind, f); - write_u32(tag->tag, f); +static void cover_update_counts(cover_scope_t *s, const int32_t *counts) +{ + for (int i = 0; i < s->tags.count; i++) { + cover_tag_t *tag = &(s->tags.items[i]); - if (dt == COV_DUMP_RUNTIME) { - const int32_t data = counts ? counts[tag->tag] : 0; - write_u32(data, f); + const int32_t data = counts ? counts[tag->tag] : 0; + cover_merge_one_tag(tag, data); + } -#ifdef COVER_DEBUG_DUMP - printf("Index: %4d Tag: %s Kind: %d Data: %d\n", tag->tag, - istr(tag->hier), tag->kind, data); -#endif + for (list_iter(cover_scope_t *, it, s->children)) + cover_update_counts(it, counts); +} - } - else { - write_u32(tag->data, f); -#ifdef COVER_DEBUG_DUMP - printf("Index: %4d Tag: %s Kind: %d Data: %d\n", tag->tag, - istr(tag->hier), tag->kind, tag->data); -#endif - } +static void cover_write_scope(cover_scope_t *s, fbuf_t *f, + ident_wr_ctx_t ident_ctx, loc_wr_ctx_t *loc_ctx) +{ + write_u8(CTRL_PUSH_SCOPE, f); + + fbuf_put_uint(f, s->type); + ident_write(s->name, ident_ctx); + + fbuf_put_uint(f, s->tags.count); + for (int i = 0; i < s->tags.count; i++) { + cover_tag_t *tag = &(s->tags.items[i]); + + write_u8(tag->kind, f); + write_u32(tag->tag, f); + write_u32(tag->data, f); write_u32(tag->flags, f); // Do not dump "excl_msk" since it is only filled at // report generation time write_u32(tag->unrc_msk, f); - loc_write(&(tag->loc), loc_wr); + loc_write(&(tag->loc), loc_ctx); ident_write(tag->hier, ident_ctx); write_u32(tag->sig_pos, f); } - write_u8(TAG_LAST, f); + for (list_iter(cover_scope_t *, it, s->children)) + cover_write_scope(it, f, ident_ctx, loc_ctx); + + write_u8(CTRL_POP_SCOPE, f); +} + +void cover_dump_tags(cover_tagging_t *ctx, fbuf_t *f, cover_dump_t dt, + const int32_t *counts) +{ + if (dt == COV_DUMP_RUNTIME) + cover_update_counts(ctx->root_scope, counts); + + if (opt_get_int(OPT_COVER_VERBOSE)) + cover_debug_dump(ctx->root_scope, 0); + + write_u32(ctx->mask, f); + write_u32(ctx->array_limit, f); + write_u32(ctx->next_tag, f); + + loc_wr_ctx_t *loc_wr = loc_write_begin(f); + ident_wr_ctx_t ident_ctx = ident_write_begin(f); + + cover_write_scope(ctx->root_scope, f, ident_ctx, loc_wr); + + write_u8(CTRL_END_OF_FILE, f); loc_write_end(loc_wr); ident_write_end(ident_ctx); @@ -442,7 +515,7 @@ void cover_reset_scope(cover_tagging_t *tagging, ident_t hier) cover_scope_t *s = xcalloc(sizeof(cover_scope_t)); s->name = hier; - tagging->top_scope = s; + tagging->top_scope = tagging->root_scope = s; tagging->hier = hier; } @@ -548,6 +621,8 @@ void cover_push_scope(cover_tagging_t *tagging, tree_t t) if (s->sig_pos == 0) s->sig_pos = tagging->top_scope->sig_pos; + list_add(&tagging->top_scope->children, s); + tagging->top_scope = s; tagging->hier = ident_prefix(tagging->hier, name, '.'); @@ -569,9 +644,7 @@ void cover_pop_scope(cover_tagging_t *tagging) ACLEAR(tagging->top_scope->ignore_lines); - cover_scope_t *tmp = tagging->top_scope->parent; - free(tagging->top_scope); - tagging->top_scope = tmp; + tagging->top_scope = tagging->top_scope->parent; #ifdef COVER_DEBUG_SCOPE printf("Popping cover scope: %s\n", istr(tagging->hier)); @@ -645,12 +718,10 @@ static void cover_read_header(fbuf_t *f, cover_tagging_t *tagging) tagging->next_tag = read_u32(f); } -void cover_read_one_tag(fbuf_t *f, loc_rd_ctx_t *loc_rd, - ident_rd_ctx_t ident_ctx, cover_tag_t *tag) +static void cover_read_one_tag(fbuf_t *f, loc_rd_ctx_t *loc_rd, + ident_rd_ctx_t ident_ctx, cover_tag_t *tag) { tag->kind = read_u8(f); - if (tag->kind == TAG_LAST) - return; tag->tag = read_u32(f); tag->data = read_u32(f); @@ -663,6 +734,38 @@ void cover_read_one_tag(fbuf_t *f, loc_rd_ctx_t *loc_rd, tag->sig_pos = read_u32(f); } +static cover_scope_t *cover_read_scope(fbuf_t *f, ident_rd_ctx_t ident_ctx, + loc_rd_ctx_t *loc_ctx) +{ + cover_scope_t *s = xcalloc(sizeof(cover_scope_t)); + s->type = fbuf_get_uint(f); + s->name = ident_read(ident_ctx); + + const int ntags = fbuf_get_uint(f); + for (int i = 0; i < ntags; i++) { + cover_tag_t new; + cover_read_one_tag(f, loc_ctx, ident_ctx, &new); + + APUSH(s->tags, new); + } + + for (;;) { + const uint8_t ctrl = read_u8(f); + switch (ctrl) { + case CTRL_PUSH_SCOPE: + { + cover_scope_t *child = cover_read_scope(f, ident_ctx, loc_ctx); + list_add(&s->children, child); + } + break; + case CTRL_POP_SCOPE: + return s; + default: + fatal_trace("invalid control word %x in cover db", ctrl); + } + } +} + cover_tagging_t *cover_read_tags(fbuf_t *f, uint32_t pre_mask) { cover_tagging_t *tagging = xcalloc(sizeof(cover_tagging_t)); @@ -672,75 +775,104 @@ cover_tagging_t *cover_read_tags(fbuf_t *f, uint32_t pre_mask) loc_rd_ctx_t *loc_rd = loc_read_begin(f); ident_rd_ctx_t ident_ctx = ident_read_begin(f); - for (;;) { - cover_tag_t new; - cover_read_one_tag(f, loc_rd, ident_ctx, &new); - - if (new.kind == TAG_LAST) + bool eof = false; + do { + const uint8_t ctrl = read_u8(f); + switch (ctrl) { + case CTRL_PUSH_SCOPE: + tagging->root_scope = cover_read_scope(f, ident_ctx, loc_rd); break; + case CTRL_END_OF_FILE: + eof = true; + break; + default: + fatal_trace("invalid control word %x in cover db", ctrl); + } + } while (!eof); - APUSH(tagging->tags, new); - } - + ident_read_end(ident_ctx); loc_read_end(loc_rd); return tagging; } -void cover_merge_tags(fbuf_t *f, cover_tagging_t *tagging) +static void cover_merge_scope(cover_scope_t *old_s, cover_scope_t *new_s) { - assert (tagging != NULL); + // TODO: Could merging be done more efficiently? + bool found = false; + for (int i = 0; i < new_s->tags.count; i++) { + cover_tag_t *new = AREF(new_s->tags, i); - cover_read_header(f, tagging); - - loc_rd_ctx_t *loc_rd = loc_read_begin(f); - ident_rd_ctx_t ident_ctx = ident_read_begin(f); - - for (;;) { - cover_tag_t new; - cover_read_one_tag(f, loc_rd, ident_ctx, &new); - - if (new.kind == TAG_LAST) - break; - - // TODO: Could merging be done more efficiently? - bool found = false; - for (int i = 0; i < tagging->tags.count; i++) { - cover_tag_t *old = AREF(tagging->tags, i); + for (int j = 0; j < old_s->tags.count; j++) { + cover_tag_t *old = AREF(old_s->tags, j); // Compare based on hierarchical path, each // statement / branch / signal has unique hierarchical name - if (new.hier == old->hier) { - assert(new.kind == old->kind); + if (new->hier == old->hier) { + assert(new->kind == old->kind); #ifdef COVER_DEBUG_MERGE printf("Merging coverage tag: %s\n", istr(old->hier)); #endif - switch (new.kind) { - case TAG_STMT: - old->data += new.data; - break; - case TAG_TOGGLE: - case TAG_BRANCH: - case TAG_EXPRESSION: - old->data |= new.data; - break; - default: - break; - } + cover_merge_one_tag(old, new->data); found = true; break; } + + // TODO: Append the new tag just before popping hierarchy tag + // with longest common prefix of new tag. That will allow to + // merge coverage of IPs from different configurations of + // generics which form hierarchy differently! + if (!found) + warnf("dropping coverage tag: %s", istr(new->hier)); + } + } + + for (list_iter(cover_scope_t *, new_c, new_s->children)) { + bool found = false; + for (list_iter(cover_scope_t *, old_c, old_s->children)) { + if (new_c->name == old_c->name) { + cover_merge_scope(old_c, new_c); + found = true; + break; + } } - // TODO: Append the new tag just before popping hierarchy tag - // with longest common prefix of new tag. That will allow to - // merge coverage of IPs from different configurations of - // generics which form hierarchy differently! if (!found) - warnf("Dropping coverage tag: %s\n", istr(new.hier)); + warnf("scope %s not found in original database", istr(new_c->name)); } } +void cover_merge_tags(fbuf_t *f, cover_tagging_t *tagging) +{ + assert (tagging != NULL); + + cover_read_header(f, tagging); + + loc_rd_ctx_t *loc_rd = loc_read_begin(f); + ident_rd_ctx_t ident_ctx = ident_read_begin(f); + + bool eof = false; + do { + const uint8_t ctrl = read_u8(f); + switch (ctrl) { + case CTRL_PUSH_SCOPE: + { + cover_scope_t *new = cover_read_scope(f, ident_ctx, loc_rd); + cover_merge_scope(tagging->root_scope, new); + } + break; + case CTRL_END_OF_FILE: + eof = true; + break; + default: + fatal_trace("invalid control word %x in cover db", ctrl); + } + } while (!eof); + + ident_read_end(ident_ctx); + loc_read_end(loc_rd); +} + void cover_count_tags(cover_tagging_t *tagging, int32_t *n_tags) { if (tagging == NULL) @@ -1023,16 +1155,16 @@ static uint32_t cover_bin_str_to_bmask(cover_exclude_ctx_t *ctx, const char *bin return 0; } -static void cover_exclude_hier(cover_tagging_t *tagging, cover_exclude_ctx_t *ctx, +static bool cover_exclude_hier(cover_scope_t *s, cover_exclude_ctx_t *ctx, const char *excl_hier, const char *bin) { bool match = false; int len = strlen(excl_hier); - for (int i = 0; i < tagging->tags.count; i++) { - cover_tag_t *tag = AREF(tagging->tags, i); + for (int i = 0; i < s->tags.count; i++) { + cover_tag_t *tag = AREF(s->tags, i); - if (tag->kind == TAG_HIER || tag->kind == TAG_LAST) + if (tag->kind == TAG_HIER) continue; if (ident_glob(tag->hier, excl_hier, len)) { @@ -1085,9 +1217,10 @@ static void cover_exclude_hier(cover_tagging_t *tagging, cover_exclude_ctx_t *ct } } - if (!match) - warn_at(&ctx->loc, "exluded hierarchy does not match any " - "coverage item: '%s'", excl_hier); + for (list_iter(cover_scope_t *, it, s->children)) + match |= cover_exclude_hier(it, ctx, excl_hier, bin); + + return match; } void cover_load_exclude_file(const char *path, cover_tagging_t *tagging) @@ -1128,7 +1261,9 @@ void cover_load_exclude_file(const char *path, cover_tagging_t *tagging) i++; } - cover_exclude_hier(tagging, &ctx, excl_hier, bin); + if (!cover_exclude_hier(tagging->root_scope, &ctx, excl_hier, bin)) + warn_at(&ctx.loc, "exluded hierarchy does not match any " + "coverage item: '%s'", excl_hier); } else fatal_at(&ctx.loc, "invalid command: $bold$%s$$", tok); @@ -1210,7 +1345,7 @@ static cover_file_t *cover_file(const loc_t *loc) static void cover_print_html_header(FILE *f, cover_report_ctx_t *ctx, bool top, - const char *title, ...) + cover_scope_t *s, const char *title, ...) { fprintf(f, "\n" "\n" @@ -1303,12 +1438,15 @@ static void cover_print_html_header(FILE *f, cover_report_ctx_t *ctx, bool top, " \n" "
\n"); + cover_tag_t *tag0 = AREF(s->tags, 0); + assert(tag0->kind == TAG_HIER); + if (!top) { fprintf(f, "