diff --git a/util/verification/csv2hjson.py b/util/verification/csv2hjson.py new file mode 100755 index 0000000000000..b7172517c1725 --- /dev/null +++ b/util/verification/csv2hjson.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +import sys +import csv +import hjson + +# Check if the file to be converted has been defined +if len(sys.argv) > 1: + # Read the CSV file + csv_file = sys.argv[1] + + with open(csv_file, mode='r', newline='', encoding='utf-8') as file: + csv_reader = csv.DictReader(file) + data = [row for row in csv_reader] + + # Convert data into HJSON + hjson_data = hjson.dumps(data, indent=2) + + # Write the HJSON file + with open('vplan_example_converted.hjson', mode='w', encoding='utf-8') as file: + file.write(hjson_data) + + print("Conversion successfully completed") +else: + print("Error: please specify the CSV file path to be converted") + print(" eg.: ./csv2hjson.py dv/files/verif/my_vplan.csv") + exit diff --git a/util/verification/hjson2csv.py b/util/verification/hjson2csv.py new file mode 100755 index 0000000000000..7b32228277af1 --- /dev/null +++ b/util/verification/hjson2csv.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +import sys +import csv +import hjson + +# Check if the file to be converted has been defined +if len(sys.argv) > 1: + # Load data from the HJSON file + hjson_file = sys.argv[1] + with open(hjson_file, 'r', encoding='utf-8') as file: + data = hjson.load(file) + + # Check that the data is a list of objects + if isinstance(data, list) and all(isinstance(item, dict) for item in data): + # Extract column headers from the keys of the first object + headers = data[0].keys() + + # Create the CSV file + csv_file = 'vplan_example_converted.csv' + with open(csv_file, 'w', newline='', encoding='utf-8') as file: + writer = csv.DictWriter(file, fieldnames=headers) + + # Write headers in the CSV + writer.writeheader() + + # Write the data rows in the CSV + writer.writerows(data) + + print("Conversion successfully completed") + else: + print("HJSON data is not in the expected format") +else: + print("Error: please specify the HJSON file path to be converted") + print(" eg.: ./hjson2csv.py dv/files/verif/my_vplan.hjson") + exit diff --git a/util/verification/mem_xyz_trace_req_test.md b/util/verification/mem_xyz_trace_req_test.md new file mode 100644 index 0000000000000..6d506a107486a --- /dev/null +++ b/util/verification/mem_xyz_trace_req_test.md @@ -0,0 +1,25 @@ +# MEM_XYZ HW IP Technical Specification + +[`mem_xyz`](https://reports.opentitan.org/hw/ip/mem_xyz/dv/latest/report.html): +![](https://dashboards.lowrisc.org/badges/dv/mem_xyz/test.svg) +![](https://dashboards.lowrisc.org/badges/dv/mem_xyz/passing.svg) +![](https://dashboards.lowrisc.org/badges/dv/mem_xyz/functional.svg) +![](https://dashboards.lowrisc.org/badges/dv/mem_xyz/code.svg) + +# Overview + +This document specifies MEM_XYZ hardware IP functionality. This module conforms to +the [OpenTitan guideline for peripheral device functionality.](../../../doc/contributing/hw/comportability/README.md) +See that document for integration overview within the broader OpenTitan top level system. + + +## Features + +- Memory address space is... +- It supports read and write commands +- All the address space is read and write accessible +- Address width is an RTL parameter among: + - 8 + - 16 + - 32 +- Multiple modes are available: mode_a, mode_b and mode_c diff --git a/util/verification/trace_req.py b/util/verification/trace_req.py new file mode 100755 index 0000000000000..66a9eb5fd00fd --- /dev/null +++ b/util/verification/trace_req.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +import sys +import os +import hjson +import argparse +import re + +def find_markdown_files(directory): + """Recursively find all Markdown files in a directory, except in 'dv' directories.""" + markdown_files = [] + + # Walk through directory and subdirectories + for root, dirs, files in os.walk(directory): + # Exclude 'dv' directories + dirs[:] = [d for d in dirs if d != 'dv'] + + # Find all .md files + for file in files: + if file.endswith('.md'): + markdown_files.append(os.path.join(root, file)) + + return markdown_files + +def extract_tags_from_markdown(markdown_file, block_name): + """Extract tags from the Markdown file using a regex pattern based on block_name.""" + try: + with open(markdown_file, 'r') as md_file: + content = md_file.read() + except FileNotFoundError: + print(f"Error: The Markdown file '{markdown_file}' was not found.") + return set() + + # Regex pattern to find tags in the form of 'req__xxxx' where xxxx is 4 hexadecimal digits + pattern = fr"req_{block_name}_[0-9a-fA-F]{{4}}" + tags = re.findall(pattern, content) + return set(tags) # Return unique tags as a set + +def extract_tags_from_hjson(hjson_file, block_name): + """Extract tags from the HJSON file using a regex pattern based on block_name.""" + try: + with open(hjson_file, 'r') as hjson_file: + hjson_content = hjson.load(hjson_file) + except FileNotFoundError: + print(f"Error: The HJSON file '{hjson_file}' was not found.") + sys.exit(1) + + # Convert the HJSON file to string and search for tags + content = str(hjson_content) + + # Regex pattern to find tags in the form of 'req__xxxx' where xxxx is 4 hexadecimal digits + pattern = fr"req_{block_name}_[0-9a-fA-F]{{4}}" + tags = re.findall(pattern, content) + return set(tags) # Return unique tags as a set + +def compare_tags(directory, hjson_file, block_name): + """Compare tags from Markdown files in a directory with those in the HJSON file.""" + + # Find all .md files in the directory (except 'dv') + markdown_files = find_markdown_files(directory) + + if not markdown_files: + print(f"No Markdown files found in directory '{directory}'") + return + + # Display found Markdown files + print("The following Markdown files have been found and will be analyzed:") + for md_file in markdown_files: + print(f" {md_file}") + + print() # Blank line before continuing + + # Collect tags from all Markdown files, and map tags to files + markdown_tags = set() + tags_in_files = {} # To store which tags are in which files + + for md_file in markdown_files: + file_tags = extract_tags_from_markdown(md_file, block_name) + markdown_tags.update(file_tags) + + # Map tags to the file they were found in + for tag in file_tags: + if tag not in tags_in_files: + tags_in_files[tag] = [] + tags_in_files[tag].append(md_file) + + # Extract tags from the HJSON file + hjson_tags = extract_tags_from_hjson(hjson_file, block_name) + + # Count number of tags + num_md_tags = len(markdown_tags) + num_matching_tags = len(markdown_tags.intersection(hjson_tags)) + + # Find tags that are missing in the HJSON file + missing_in_hjson = markdown_tags - hjson_tags + + # Find tags that are in HJSON but not in Markdown + missing_in_markdown = hjson_tags - markdown_tags + + # Print summary + print(f"Total number of tags found in Markdown files: {num_md_tags}") + print(f"Number of tags found in both Markdown and HJSON: {num_matching_tags}") + + if missing_in_hjson: + print("\nTags found in Markdown but missing in HJSON:") + for tag in missing_in_hjson: + print(f" {tag} (found in {', '.join(tags_in_files[tag])})") + else: + print("All tags in the Markdown files are present in the HJSON file.") + + print(f"\n") + print(f"Total number of tags found in HJSON file: {len(hjson_tags)}") + print(f"Number of tags found in HJSON but missing in Markdown: {len(missing_in_markdown)}") + + if missing_in_markdown: + print("Tags found in HJSON but missing in Markdown:") + for tag in missing_in_markdown: + print(f" {tag}") + +def main(): + # Set up argument parser + parser = argparse.ArgumentParser( + description="Compare 'req__xxxx' tags between Markdown specifications and the HJSON verification plan.") + parser.add_argument( + 'block_name', + type=str, + help='The block name to look for in the tag format "req__xxxx".' + ) + parser.add_argument( + 'directory', + type=str, + help='The path to the root directory of the block/IP. ' + 'The sub-directories will be automatically explored except the "dv" folder.' + 'Eg.: hw/ip/block_name' + ) + parser.add_argument( + 'hjson_file', + type=str, + help='The path to the HJSON file.' + ) + + # Parse arguments + args = parser.parse_args() + + # Call the compare function with the provided directory, HJSON file, and block name + compare_tags(args.directory, args.hjson_file, args.block_name) + +if __name__ == '__main__': + main() diff --git a/util/verification/vplan_example_original.csv b/util/verification/vplan_example_original.csv new file mode 100644 index 0000000000000..76b01994e5ec2 --- /dev/null +++ b/util/verification/vplan_example_original.csv @@ -0,0 +1,22 @@ +Name,ID,Coverage Result,Description,Depth,Node Kind,Metric Kind,Item,Owner,Priority,Milestone,Weight,Issue,Comment +mem_xyz,,,A synchronous memory with a parameterizable address width and data width.,0,dut,,,,,,,, +testlist,,,Testlist,1,title,,,,,,,, +main_test,req_mem_xyz_1324,,Main test,2,coverage,testcase,tc_my_test,Michel,1,V1,5,#15926, +bad_addr,req_mem_xyz_0E55,,Bad address,2,coverage,testcase,tc_addr_err,Andrew,2,V3,3,#15927, +fcov,,,Feature coverage,1,title,,,,,,,, +addr_space,req_mem_xyz_162F,,All address space,2,coverage,functional,cp_addr,Francesca,1,V1,2,#14562, +rw_cmd,req_mem_xyz_0882,,Read and write commands,2,coverage,functional,cp_cmd,Michel,1,V1,2,#14562, +rw_addr_space,req_mem_xyz_24BE,,Read and write to all address space,2,coverage,functional,cp_addr_x_cp_cmd,Francesca,1,V1,2,#14562, +mem_full,req_mem_xyz_0F0A,,Reach memory full,2,coverage,functional,cp_full,Andrew,2,V2,3,#14562,This cannot be tested if block_abc is not ready +concurrent_rw,req_mem_xyz_2730,,Read and write simultaneously,2,coverage,assertion,assert_rd_wr,Andrew,3,V3,2,#11487, +addr_width_8bits,req_mem_xyz_364E,,Address 8 bits width,2,coverage,formal,fpv_addr_8bits,Andrew,1,V2,1,#2287, +addr_width_16bits,req_mem_xyz_3A6A,,Address 16 bits width,2,coverage,formal,fpv_addr_16bits,Andrew,1,V2,1,#2287, +addr_width_32bits,req_mem_xyz_3A4E,,Address 32 bits width,2,coverage,formal,fpv_addr_32bits,Andrew,3,V2,1,#2287, +data_width_8bits,req_mem_xyz_3637,,Data width 8 bits,2,coverage,formal,fpv_data_8width,Andrew,1,V2,1,#2287, +instanciate_fifo_common_lib,req_mem_xyz_9A4D,,"Uses the FIFO from the common library, test de la virgule, c'est OK ?",2,vplan,,vplan_fifo,,1,V1,10,#2145,Incorporate the sub-module verification plan +...,...,,...,...,...,...,...,...,...,,,..., +mem_depth,req_mem_xyz_12A4,,Memory depth,2,coverage,formal,fpv_mem_depth,Andrew,1,,1,#2287, +code_cov,,,Code coverage,1,title,,,,,V3,40%,, +code_cov_statement,req_mem_xyz_47D7,,Statement,2,coverage,code,s_cov(memory_xyz),,3,,,#7894,Need to run UNR before +code_cov_fsm,req_mem_xyz_2047,,FSM,2,coverage,code,f_cov(memory_xyz),,3,,,#7894, +code_cov_branch,req_mem_xyz_309C,,Branch...,2,coverage,code,b_cov(memory_xyz),,3,,,#7894,