diff --git a/.gitignore b/.gitignore index c3306c0..f4edf83 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ .settings/ Debug/ .pydevproject +venv/ +setenv.sh +fir.xml diff --git a/examples/fir/Makefile b/examples/fir/Makefile index 06e3378..b6995a9 100644 --- a/examples/fir/Makefile +++ b/examples/fir/Makefile @@ -47,8 +47,12 @@ main: $(OBJFILES) obj/%.o: %.cpp $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) $^ -o $@ -run: +run: main $(LDPATH) && ./$(EXEC) +test: run + $(MAKE) -C ../../test/pyucis-fir/ validate_fir + clean: rm -rf obj $(EXEC) + rm coverage_results.xml diff --git a/examples/fir/README.md b/examples/fir/README.md new file mode 100644 index 0000000..e6180de --- /dev/null +++ b/examples/fir/README.md @@ -0,0 +1,15 @@ +# Build Instructions + +These are the build instructions for using the example + +``` +# export SYSTEMC_HOME="path to systemC release dir" +$ make clean +$ make +$ make run +``` + +# Test Instructions *(optional)* +Optionally test the output using the following instructions + + diff --git a/examples/fir/src/stimulus.h b/examples/fir/src/stimulus.h index 624a553..85d31ad 100644 --- a/examples/fir/src/stimulus.h +++ b/examples/fir/src/stimulus.h @@ -103,7 +103,7 @@ SC_MODULE(stimulus) { bin("invalid", 0) }; - cross reset_valid_cross = cross (this, "reset valid", + cross reset_valid_cross = cross (this, "reset_valid", &reset_cvp, &input_valid_cvp, &values_cvp diff --git a/includes/fc4sc_bin.hpp b/includes/fc4sc_bin.hpp index 34f593c..1ab49ff 100644 --- a/includes/fc4sc_bin.hpp +++ b/includes/fc4sc_bin.hpp @@ -195,7 +195,8 @@ class bin : public bin_base stream << "type=\"" << this->ucis_bin_type << "\" " - << "alias=\"" << this->get_hitcount() << "\"" + << "alias=\"" << this->get_hitcount() << "\" " + << "key=\"KEY\"" << ">\n"; // Print each range. Coverpoint writes the header (name etc.) diff --git a/includes/fc4sc_covergroup.hpp b/includes/fc4sc_covergroup.hpp index 1cc5510..4599b9d 100644 --- a/includes/fc4sc_covergroup.hpp +++ b/includes/fc4sc_covergroup.hpp @@ -246,7 +246,7 @@ class covergroup : public cvg_base << "\" line=\"" << "1" << "\" inlineCount=\"1\"/>\n"; - stream << "\n"; stream << "\n"; diff --git a/includes/fc4sc_cross.hpp b/includes/fc4sc_cross.hpp index 665c55f..41b11a2 100644 --- a/includes/fc4sc_cross.hpp +++ b/includes/fc4sc_cross.hpp @@ -265,7 +265,7 @@ class cross : public cvp_base << "\" "; stream << ">\n"; - stream << option << "\n"; + stream << "\n"; for (auto &cvp : cvps_vec) { diff --git a/includes/fc4sc_master.hpp b/includes/fc4sc_master.hpp index 4deeffa..125864e 100644 --- a/includes/fc4sc_master.hpp +++ b/includes/fc4sc_master.hpp @@ -273,7 +273,7 @@ class global stream << ">\n"; stream << " None: + + rc = validate_ucis('../../examples/fir/coverage_results.xml') + return rc + + + +if __name__ == '__main__': + rc = main() + if rc: + print("validation OK") + else: + print("validation Failure") + diff --git a/tools/.gitignore b/tools/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/tools/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/tools/VENV.md b/tools/VENV.md new file mode 100644 index 0000000..c5b5671 --- /dev/null +++ b/tools/VENV.md @@ -0,0 +1,22 @@ +# venv notes + +# Create and activate the venv + +Do this once + +``` +sudo apt-get install python3-venv +python3 -m venv venv +. venv/bin/activate +python3 -m pip install -r requirements.txt +# test that usis can be imported +python3 -c 'import ucis' +alias bazel='/home/davis/bin/bazel-4.2.1-linux-x86_64' +``` + + +# Activate the venv + +``` +. venv/bin/activate +``` diff --git a/tools/coverage_report/.gitignore b/tools/coverage_report/.gitignore new file mode 100644 index 0000000..4264d97 --- /dev/null +++ b/tools/coverage_report/.gitignore @@ -0,0 +1 @@ +JFD/* diff --git a/tools/coverage_report/README.md b/tools/coverage_report/README.md new file mode 100644 index 0000000..a9075cc --- /dev/null +++ b/tools/coverage_report/README.md @@ -0,0 +1,6 @@ +# Usage + +``` +python3 report.py --xml_report foo.xml --yaml_out foo.yml +``` + diff --git a/tools/coverage_report/report.py b/tools/coverage_report/report.py index 7d950c1..c7a923e 100644 --- a/tools/coverage_report/report.py +++ b/tools/coverage_report/report.py @@ -4,7 +4,7 @@ import argparse import xml.etree.ElementTree as ET -sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +sys.path.append(os.path.join(os.path.dirname(__file__), '../')) from ucis_parser import UCIS_DB_Parser @@ -53,7 +53,9 @@ def get_covergroup_report_data(self, covergroupCoverage, module_data): def get_coverpoint_report_data(self, cgInstance, cg_cp_bin_map, cg_data): for coverpoint in self.findall_ucis_children(cgInstance, "coverpoint"): + print("coverpoint is ", coverpoint) options = self.find_ucis_element(coverpoint, 'options') + print("JFD options is ", options) cp_name = coverpoint.get('name') cp_data = { 'item_type' : 'point', @@ -78,7 +80,11 @@ def get_coverpoint_report_data(self, cgInstance, cg_cp_bin_map, cg_data): else: cp_data['bin_misses'] += 1 cp_data['misses'].append(bin_name) - cp_data['pct_cov'] = 100 * ((cp_data['bin_count'] - cp_data['bin_misses']) / float(cp_data['bin_count'])) + if cp_data['bin_count'] == 0: + print('should not happen. get_coverpoint_report_data() cp_data["bin_count"] is zero') + print('skip') + else: + cp_data['pct_cov'] = 100 * ((cp_data['bin_count'] - cp_data['bin_misses']) / float(cp_data['bin_count'])) def collect_cross_bins(self, exprs, cg_cp_bin_map, parrent_bins): expr_name = exprs[0].text @@ -95,7 +101,6 @@ def get_cross_bin_name_from_tuple(self, cg_cp_bin_map, exprs, bin_tuple): names = [] for expr_idx, bin_idx in enumerate(bin_tuple): expr_name = exprs[expr_idx].text - #expr_bin_name = "%s(%s)" % (expr_name, cg_cp_bin_map[expr_name][bin_idx]) expr_bin_name = cg_cp_bin_map[expr_name][bin_idx] names.append(expr_bin_name) names.reverse() @@ -126,7 +131,6 @@ def get_cross_report_data(self, cgInstance, cg_cp_bin_map, cg_data): for cbin in all_cross_bins: bin_hits[cbin] = 0 - # for bin_idx, bin in enumerate(self.findall_ucis_children(cross, "crossBin")): bin_name = bin.get('name') cg_cp_bin_map[cr_name][bin_idx] = bin_name @@ -144,7 +148,10 @@ def get_cross_report_data(self, cgInstance, cg_cp_bin_map, cg_data): else: cr_data['bin_misses'] += 1 cr_data['misses'].append(self.get_cross_bin_name_from_tuple(cg_cp_bin_map, exprs, bin_tuple)) - cr_data['pct_cov'] = 100 * ((cr_data['bin_count'] - cr_data['bin_misses']) / float(cr_data['bin_count'])) + if cr_data['bin_count'] == 0: + print('JFD in get_cross_report_data() cr_data["bin_count"] is zero') + else: + cr_data['pct_cov'] = 100 * ((cr_data['bin_count'] - cr_data['bin_misses']) / float(cr_data['bin_count'])) args = {} @@ -201,12 +208,12 @@ def reduce_to_cg_inst_summary(db): args = parser.parse_args() parser = UCIS_DB_Reporter() - + if not args.xml_report: raise ValueError("No input XML provided!") if not args.yaml_out: raise ValueError("No output YAML name provided!") - + try: d = parser.get_report_data(args.xml_report) except IOError as e: @@ -224,9 +231,9 @@ def reduce_to_cg_inst_summary(db): if not args.quiet: print(""" Overall Summary: - + Total Coverage: %6.2f - + Module Summary: """ % (yaml_db['pct_cov'])) diff --git a/tools/requirements.txt b/tools/requirements.txt new file mode 100644 index 0000000..7a103cd --- /dev/null +++ b/tools/requirements.txt @@ -0,0 +1,4 @@ +pyucis==0.0.5.20220416.1 +pylint +pyyaml + diff --git a/tools/ucis_parser.py b/tools/ucis_parser.py index 6b58b40..a3f8310 100644 --- a/tools/ucis_parser.py +++ b/tools/ucis_parser.py @@ -22,27 +22,27 @@ | | from : start value of the interval | | to : end value of the interval | | - | -> contents + | -> contents | | coverageCount : the number of hits registered in this interval | 0 - | + | -> cross [0:n] | name : name of the cross | -> crossBin [0:n] | name : name of the cross bin - | + | + -> index -> index - -> index . . Number of indexes = number of crossed coverpoints . - -> index - | - -> contents + -> index + | + -> contents | coverageCount : the number of hits registered in this cross bin - 0 - + 0 + Note that this only contains the elements which are relevant for merging! """ class UCIS_DB_Parser: @@ -70,13 +70,45 @@ def __init__(self): # the master ucis DB which will be "merged" into when parsing additional DBs self.mergeDBtree = None self.mergeDBroot = None - + def find_ucis_element(self, element, subElementName): - return element.find('{0}:{1}'.format(self.ucis_ns, subElementName), self.ns_map) - + e = element.find('{0}:{1}'.format(self.ucis_ns, subElementName), self.ns_map) + if "coverpointBin" in e.tag: + if e.attrib['type'] == 'default' or e.attrib['type'] == 'illegal': + # its valid + return e + else: + # its not. Its 'ignore'. Ignore it. + return None + else: + # its something other than a coverpointBin + return e + + def findall_ucis_children(self, element, subElementName): - return element.findall('{0}:{1}'.format(self.ucis_ns, subElementName), self.ns_map) - + # instead of returning all, use the iterator form, check for the ones + # which are of tag type "coverpointBin", then examine the attribute + # to see if its default. If it is, include in results, otherwise + # ignore. + an_iter = element.iterfind('{0}:{1}'.format(self.ucis_ns, subElementName), self.ns_map) + a_filtered_list = [] + for e in an_iter: + if "coverpointBin" in e.tag: + # this element is a coverpointBin, which can be of type: Default, + # Ignore, or Illegal. Only return elements which are of type + # Default or illegal?. + if e.attrib['type'] == 'default' or e.attrib['type'] == 'illegal': + # its valid + a_filtered_list.append(e) + else: + # its not. Its 'ignore'. Ignore it. + print('\t{} is not of type="default", ignoring.'.format(e.attrib['name'])) + else: + # its something other than a coverpointBin + a_filtered_list.append(e) + + return a_filtered_list + # formats an XPath ElementTree query to search for a specified element name # and an optional attribute name together with a certain attribute value def format_et_query(self, elementName, attribName = None, attribValue = None): @@ -84,8 +116,8 @@ def format_et_query(self, elementName, attribName = None, attribValue = None): if attribName is not None and attribValue is not None: query += "[@{0}='{1}']".format(attribName, attribValue) return query - + # searches and returns the first match of the XPath query in the mergeDBtree def find_merge_element_by_query(self, xpath_query): return self.mergeDBtree.find(xpath_query, self.ns_map) - \ No newline at end of file +