Skip to content

Commit 95853d1

Browse files
committed
refactor: move sim related modules to top level package
Improve the clarity on what is simulation flow specific and what is common. This makes way to fix the linting and formal flows, and creates more clarity on what is required to be implemented to add a new flow. Signed-off-by: James McCorrie <[email protected]>
1 parent 062a1c5 commit 95853d1

File tree

13 files changed

+379
-283
lines changed

13 files changed

+379
-283
lines changed

src/dvsim/flow/base.py

Lines changed: 1 addition & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import sys
1111
from abc import ABC, abstractmethod
1212
from collections.abc import Mapping, Sequence
13-
from datetime import datetime, timezone
1413
from pathlib import Path
1514
from typing import TYPE_CHECKING, ClassVar
1615

@@ -20,15 +19,12 @@
2019
from dvsim.job.data import CompletedJobStatus
2120
from dvsim.launcher.factory import get_launcher_cls
2221
from dvsim.logging import log
23-
from dvsim.report.data import FlowResults, IPMeta, ResultsSummary
24-
from dvsim.report.generate import gen_block_report, gen_reports
2522
from dvsim.scheduler import Scheduler
2623
from dvsim.utils import (
2724
find_and_substitute_wildcards,
2825
rm_path,
2926
subst_wildcards,
3027
)
31-
from dvsim.utils.git import git_commit_hash
3228

3329
if TYPE_CHECKING:
3430
from dvsim.job.deploy import Deploy
@@ -447,75 +443,14 @@ def deploy_objects(self) -> Sequence[CompletedJobStatus]:
447443
interactive=self.interactive,
448444
).run()
449445

446+
@abstractmethod
450447
def gen_results(self, results: Sequence[CompletedJobStatus]) -> None:
451448
"""Generate flow results.
452449
453450
Args:
454451
results: completed job status objects.
455452
456453
"""
457-
reports_dir = Path(self.scratch_base_path) / "reports"
458-
commit = git_commit_hash(path=Path(self.proj_root))
459-
url = f"https://github.com/lowrisc/opentitan/tree/{commit}"
460-
461-
all_flow_results: Mapping[str, FlowResults] = {}
462-
463-
for item in self.cfgs:
464-
item_results = [
465-
res
466-
for res in results
467-
if res.block.name == item.name and res.block.variant == item.variant
468-
]
469-
470-
flow_results: FlowResults = item._gen_json_results(
471-
run_results=item_results,
472-
commit=commit,
473-
url=url,
474-
)
475-
476-
# Convert to lowercase to match filename
477-
block_result_index = (
478-
f"{item.name}_{item.variant}" if item.variant else item.name
479-
).lower()
480-
481-
all_flow_results[block_result_index] = flow_results
482-
483-
# Generate the block's JSON/HTML reports to the report area.
484-
gen_block_report(
485-
results=flow_results,
486-
path=reports_dir,
487-
)
488-
489-
self.errors_seen |= item.errors_seen
490-
491-
if self.is_primary_cfg:
492-
# The timestamp for this run has been taken with `utcnow()` and is
493-
# stored in a custom format. Store it in standard ISO format with
494-
# explicit timezone annotation.
495-
timestamp = (
496-
datetime.strptime(self.timestamp, "%Y%m%d_%H%M%S")
497-
.replace(tzinfo=timezone.utc)
498-
.isoformat()
499-
)
500-
501-
results_summary = ResultsSummary(
502-
top=IPMeta(
503-
name=self.name,
504-
variant=self.variant,
505-
commit=commit,
506-
branch=self.branch,
507-
url=url,
508-
),
509-
timestamp=timestamp,
510-
flow_results=all_flow_results,
511-
report_path=reports_dir,
512-
)
513-
514-
# Generate all the JSON/HTML reports to the report area.
515-
gen_reports(
516-
summary=results_summary,
517-
path=reports_dir,
518-
)
519454

520455
def has_errors(self) -> bool:
521456
"""Return error state."""

src/dvsim/flow/factory.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
from dvsim.flow.hjson import load_hjson
1212
from dvsim.flow.lint import LintCfg
1313
from dvsim.flow.rdc import RdcCfg
14-
from dvsim.flow.sim import SimCfg
1514
from dvsim.flow.syn import SynCfg
1615
from dvsim.logging import log
16+
from dvsim.sim.flow import SimCfg
1717

1818
FLOW_HANDLERS = {
1919
"cdc": CdcCfg,

src/dvsim/flow/one_shot.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
import pathlib
88
from collections import OrderedDict
9+
from collections.abc import Sequence
910

1011
from dvsim.flow.base import FlowCfg
12+
from dvsim.job.data import CompletedJobStatus
1113
from dvsim.job.deploy import CompileOneShot
1214
from dvsim.logging import log
1315
from dvsim.modes import BuildMode, Mode
@@ -149,3 +151,11 @@ def _create_deploy_objects(self) -> None:
149151

150152
# Create initial set of directories before kicking off the regression.
151153
self._create_dirs()
154+
155+
def gen_results(self, results: Sequence[CompletedJobStatus]) -> None:
156+
"""Generate flow results.
157+
158+
Args:
159+
results: completed job status objects.
160+
161+
"""

src/dvsim/report/data.py

Lines changed: 1 addition & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,11 @@
44

55
"""Report data models."""
66

7-
from collections.abc import Mapping
8-
from datetime import datetime
9-
from pathlib import Path
10-
117
from pydantic import BaseModel, ConfigDict
128

13-
from dvsim.sim_results import BucketedFailures
14-
159
__all__ = (
1610
"IPMeta",
17-
"ResultsSummary",
11+
"ToolMeta",
1812
)
1913

2014

@@ -45,197 +39,3 @@ class ToolMeta(BaseModel):
4539
"""Name of the tool."""
4640
version: str
4741
"""Version of the tool."""
48-
49-
50-
class TestResult(BaseModel):
51-
"""Test result."""
52-
53-
model_config = ConfigDict(frozen=True, extra="forbid")
54-
55-
max_time: float
56-
"""Run time."""
57-
sim_time: float
58-
"""Simulation time."""
59-
60-
passed: int
61-
"""Number of tests passed."""
62-
total: int
63-
"""Total number of tests run."""
64-
percent: float
65-
"""Percentage test pass rate."""
66-
67-
68-
class Testpoint(BaseModel):
69-
"""Testpoint."""
70-
71-
model_config = ConfigDict(frozen=True, extra="forbid")
72-
73-
tests: Mapping[str, TestResult]
74-
"""Test results."""
75-
76-
passed: int
77-
"""Number of tests passed."""
78-
total: int
79-
"""Total number of tests run."""
80-
percent: float
81-
"""Percentage test pass rate."""
82-
83-
84-
class TestStage(BaseModel):
85-
"""Test stages."""
86-
87-
model_config = ConfigDict(frozen=True, extra="forbid")
88-
89-
testpoints: Mapping[str, Testpoint]
90-
"""Results by test point."""
91-
92-
passed: int
93-
"""Number of tests passed."""
94-
total: int
95-
"""Total number of tests run."""
96-
percent: float
97-
"""Percentage test pass rate."""
98-
99-
100-
class CodeCoverageMetrics(BaseModel):
101-
"""CodeCoverage metrics."""
102-
103-
model_config = ConfigDict(frozen=True, extra="forbid")
104-
105-
block: float | None
106-
"""Block Coverage (%) - did this part of the code execute?"""
107-
line_statement: float | None
108-
"""Line/Statement Coverage (%) - did this part of the code execute?"""
109-
branch: float | None
110-
"""Branch Coverage (%) - did this if/case take all paths?"""
111-
condition_expression: float | None
112-
"""Condition/Expression Coverage (%) - did the logic evaluate to 0 & 1?"""
113-
toggle: float | None
114-
"""Toggle Coverage (%) - did the signal wiggle?"""
115-
fsm: float | None
116-
"""FSM Coverage (%) - did the state machine transition?"""
117-
118-
@property
119-
def average(self) -> float | None:
120-
"""Average code coverage (%)."""
121-
all_cov = [
122-
c
123-
for c in [
124-
self.line_statement,
125-
self.branch,
126-
self.condition_expression,
127-
self.toggle,
128-
self.fsm,
129-
]
130-
if c is not None
131-
]
132-
133-
if len(all_cov) == 0:
134-
return None
135-
136-
return sum(all_cov) / len(all_cov)
137-
138-
139-
class CoverageMetrics(BaseModel):
140-
"""Coverage metrics."""
141-
142-
code: CodeCoverageMetrics | None
143-
"""Code Coverage."""
144-
assertion: float | None
145-
"""Assertion Coverage."""
146-
functional: float | None
147-
"""Functional coverage."""
148-
149-
@property
150-
def average(self) -> float | None:
151-
"""Average code coverage (%) or None if there is no coverage."""
152-
code = self.code.average if self.code is not None else None
153-
all_cov = [
154-
c
155-
for c in [
156-
code,
157-
self.assertion,
158-
self.functional,
159-
]
160-
if c is not None
161-
]
162-
163-
if len(all_cov) == 0:
164-
return None
165-
166-
return sum(all_cov) / len(all_cov)
167-
168-
169-
class FlowResults(BaseModel):
170-
"""Flow results data."""
171-
172-
model_config = ConfigDict(frozen=True, extra="forbid")
173-
174-
block: IPMeta
175-
"""IP block metadata."""
176-
tool: ToolMeta
177-
"""Tool used in the simulation run."""
178-
timestamp: datetime
179-
"""Timestamp for when the test ran."""
180-
181-
stages: Mapping[str, TestStage]
182-
"""Results per test stage."""
183-
coverage: CoverageMetrics | None
184-
"""Coverage metrics."""
185-
186-
failed_jobs: BucketedFailures
187-
"""Bucketed failed job overview."""
188-
189-
passed: int
190-
"""Number of tests passed."""
191-
total: int
192-
"""Total number of tests run."""
193-
percent: float
194-
"""Percentage test pass rate."""
195-
196-
@staticmethod
197-
def load(path: Path) -> "FlowResults":
198-
"""Load results from JSON file.
199-
200-
Transform the fields of the loaded JSON into a more useful schema for
201-
report generation.
202-
203-
Args:
204-
path: to the json file to load.
205-
206-
"""
207-
return FlowResults.model_validate_json(path.read_text())
208-
209-
210-
class ResultsSummary(BaseModel):
211-
"""Summary of results."""
212-
213-
model_config = ConfigDict(frozen=True, extra="forbid")
214-
215-
top: IPMeta
216-
"""Meta data for the top level config."""
217-
218-
timestamp: datetime
219-
"""Run time stamp."""
220-
221-
flow_results: Mapping[str, FlowResults]
222-
"""Flow results."""
223-
224-
report_path: Path
225-
"""Path to the report JSON file."""
226-
227-
@staticmethod
228-
def load(path: Path) -> "ResultsSummary":
229-
"""Load results from JSON file.
230-
231-
Transform the fields of the loaded JSON into a more useful schema for
232-
report generation.
233-
234-
Args:
235-
path: to the json file to load.
236-
237-
Returns:
238-
The loaded ResultsSummary from JSON.
239-
240-
"""
241-
return ResultsSummary.model_validate_json(path.read_text())

src/dvsim/sim/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Copyright lowRISC contributors (OpenTitan project).
2+
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
"""Simulation workflow."""

0 commit comments

Comments
 (0)