A DV engineer receives a regression summary, an STA engineer receives a timing report, and a PD engineer receives a congestion export. Each looks like rows of numbers, but the layout alone does not define the meaning. Before asking AI to summarize them, identify the producing tool, report revision, extraction rule, units, and scope. An RTL build label without a source revision may refer to several different designs. A timing value without its corner and mode can invite a misleading comparison.
CSV is useful for a flat list with consistent columns. JSON is useful when a run has nested configuration, multiple results, or optional metadata. Neither format guarantees correct engineering meaning. Choose a stable schema, a documented agreement about field names and their types, before collecting many exports. Keep the original approved export available so a reviewer can trace a normalized value back to its source.
Synthetic CSV: one row is one completed run attempt.
run_id,test_name,seed,status,duration_s,note
R101,reset_smoke,11,PASS,12.5,"reset, then traffic"
R102,fifo_stress,22,FAIL,18.0,"timeout"
Equivalent JSON record for the first row:
{"run_id":"R101","test_name":"reset_smoke","seed":11,
"status":"PASS","duration_s":12.5,"note":"reset, then traffic"}The comma inside the quoted note is part of one field. Splitting each line on commas would silently create an extra column. A normal CSV reader handles the quoting. CSV fields usually arrive as strings, so convert the seed and duration deliberately. In JSON, numbers and strings differ: the string "11" is not the integer 11. Keep identifiers as strings even when an export happens to use only digits; converting an identifier such as 0007 to an integer loses information.
import csv
import io
import json
source = 'run_id,seed,status,duration_s\nR101,11,PASS,12.5\n'
records = []
for raw in csv.DictReader(io.StringIO(source)):
if raw["status"] not in {"PASS", "FAIL", "ERROR"}:
raise ValueError("Unknown status")
records.append({
"run_id": raw["run_id"], "seed": int(raw["seed"]),
"status": raw["status"], "duration_s": float(raw["duration_s"]),
})
expected = [{"run_id": "R101", "seed": 11, "status": "PASS", "duration_s": 12.5}]
assert records == expected
assert type(records[0]["seed"]) is int
assert type(records[0]["duration_s"]) is float
encoded = json.dumps(records, sort_keys=True)
assert json.loads(encoded) == expected
print(encoded)
print("PASS: synthetic CSV conversion and JSON round trip")This independent, complete snippet parses an embedded synthetic string and prints JSON. It does not open files. When adapting a reader to an approved file, specify its encoding and use the CSV module's documented newline handling. Decide whether an empty numeric field means missing, invalid, or unavailable before conversion. Never convert a missing slack measurement to zero: zero slack and no measurement describe different states.
Preserve 0007 as a string. Represent missing duration explicitly, for example as None and then JSON null, under a documented policy. Reject or quarantine SKIPPED until the allowed status vocabulary is updated; do not silently map it to PASS or FAIL. Record the rejected row's source location.
Review the row count before and after conversion and account for each rejected row. Check that a note containing a comma remains one field. Add a schema version to the surrounding export manifest so a renamed duration column is detected instead of becoming a column of missing values. An AI-generated parser is a draft until these behaviors have been inspected against representative exports.
Parsing validates shape, not whether the simulator actually completed or whether an STA report used the intended constraints. Local examples do not grant permission to upload reports: obtain approval for the source data and destination first, and keep production access read-only. Reference: Python 3.12 CSV and JSON library documentation.