dvsim 1.7.0__py3-none-any.whl → 1.7.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
dvsim/cli/admin.py CHANGED
@@ -35,9 +35,9 @@ def report() -> None:
35
35
  )
36
36
  def gen(json_path: Path, output_dir: Path) -> None:
37
37
  """Generate a report from a existing results JSON."""
38
- from dvsim.report.data import ResultsSummary
39
- from dvsim.report.generate import gen_reports
38
+ from dvsim.sim.data import SimResultsSummary
39
+ from dvsim.sim.report import gen_reports
40
40
 
41
- results: ResultsSummary = ResultsSummary.load(path=json_path)
41
+ results: SimResultsSummary = SimResultsSummary.load(path=json_path)
42
42
 
43
43
  gen_reports(summary=results, path=output_dir)
dvsim/flow/base.py CHANGED
@@ -10,7 +10,6 @@ import pprint
10
10
  import sys
11
11
  from abc import ABC, abstractmethod
12
12
  from collections.abc import Mapping, Sequence
13
- from datetime import datetime, timezone
14
13
  from pathlib import Path
15
14
  from typing import TYPE_CHECKING, ClassVar
16
15
 
@@ -20,15 +19,12 @@ from dvsim.flow.hjson import set_target_attribute
20
19
  from dvsim.job.data import CompletedJobStatus
21
20
  from dvsim.launcher.factory import get_launcher_cls
22
21
  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
25
22
  from dvsim.scheduler import Scheduler
26
23
  from dvsim.utils import (
27
24
  find_and_substitute_wildcards,
28
25
  rm_path,
29
26
  subst_wildcards,
30
27
  )
31
- from dvsim.utils.git import git_commit_hash
32
28
 
33
29
  if TYPE_CHECKING:
34
30
  from dvsim.job.deploy import Deploy
@@ -447,6 +443,7 @@ class FlowCfg(ABC):
447
443
  interactive=self.interactive,
448
444
  ).run()
449
445
 
446
+ @abstractmethod
450
447
  def gen_results(self, results: Sequence[CompletedJobStatus]) -> None:
451
448
  """Generate flow results.
452
449
 
@@ -454,68 +451,6 @@ class FlowCfg(ABC):
454
451
  results: completed job status objects.
455
452
 
456
453
  """
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
- )
519
454
 
520
455
  def has_errors(self) -> bool:
521
456
  """Return error state."""
dvsim/flow/factory.py CHANGED
@@ -11,9 +11,9 @@ from dvsim.flow.formal import FormalCfg
11
11
  from dvsim.flow.hjson import load_hjson
12
12
  from dvsim.flow.lint import LintCfg
13
13
  from dvsim.flow.rdc import RdcCfg
14
- from dvsim.flow.sim import SimCfg
15
14
  from dvsim.flow.syn import SynCfg
16
15
  from dvsim.logging import log
16
+ from dvsim.sim.flow import SimCfg
17
17
 
18
18
  FLOW_HANDLERS = {
19
19
  "cdc": CdcCfg,
dvsim/flow/lint.py CHANGED
@@ -76,7 +76,10 @@ class LintCfg(OneShotCfg):
76
76
 
77
77
  keys = self.totals.get_keys(self.report_severities)
78
78
  for cfg in self.cfgs:
79
- name_with_link = cfg._get_results_page_link(self.results_dir)
79
+ link_text = self.name.upper()
80
+ relative_link = Path(self.results_dir) / self.results_page
81
+
82
+ name_with_link = f"[{link_text}]({relative_link})"
80
83
 
81
84
  row = [name_with_link]
82
85
  row += cfg.result_summary.get_counts_md(keys)
dvsim/flow/one_shot.py CHANGED
@@ -4,10 +4,13 @@
4
4
 
5
5
  """Class describing a one-shot build configuration object."""
6
6
 
7
- import pathlib
7
+ from abc import abstractmethod
8
8
  from collections import OrderedDict
9
+ from collections.abc import Sequence
10
+ from pathlib import Path
9
11
 
10
12
  from dvsim.flow.base import FlowCfg
13
+ from dvsim.job.data import CompletedJobStatus
11
14
  from dvsim.job.deploy import CompileOneShot
12
15
  from dvsim.logging import log
13
16
  from dvsim.modes import BuildMode, Mode
@@ -15,9 +18,7 @@ from dvsim.utils import rm_path
15
18
 
16
19
 
17
20
  class OneShotCfg(FlowCfg):
18
- """Simple one-shot build flow for non-simulation targets like
19
- linting, synthesis and FPV.
20
- """
21
+ """Simple one-shot build flow for non-simulation targets like linting, synthesis and FPV."""
21
22
 
22
23
  ignored_wildcards = [*FlowCfg.ignored_wildcards, "build_mode", "index", "test"]
23
24
 
@@ -134,8 +135,10 @@ class OneShotCfg(FlowCfg):
134
135
  def _create_dirs(self) -> None:
135
136
  """Create initial set of directories."""
136
137
  for link in self.links:
137
- rm_path(self.links[link])
138
- pathlib.Path(self.links[link]).mkdir(parents=True)
138
+ link_path = Path(self.links[link])
139
+
140
+ rm_path(link_path)
141
+ link_path.mkdir(parents=True)
139
142
 
140
143
  def _create_deploy_objects(self) -> None:
141
144
  """Create deploy objects from build modes."""
@@ -149,3 +152,49 @@ class OneShotCfg(FlowCfg):
149
152
 
150
153
  # Create initial set of directories before kicking off the regression.
151
154
  self._create_dirs()
155
+
156
+ @abstractmethod
157
+ def _gen_results(self):
158
+ """Generate results for this config."""
159
+
160
+ @abstractmethod
161
+ def gen_results_summary(self):
162
+ """Gathers the aggregated results from all sub configs."""
163
+
164
+ def gen_results(self, results: Sequence[CompletedJobStatus]) -> None:
165
+ """Generate flow results.
166
+
167
+ Args:
168
+ results: completed job status objects.
169
+
170
+ """
171
+ for item in self.cfgs:
172
+ project = item.name
173
+
174
+ item_results = [
175
+ res
176
+ for res in results
177
+ if res.block.name == item.name and res.block.variant == item.variant
178
+ ]
179
+
180
+ result = item._gen_results(item_results)
181
+
182
+ log.info("[results]: [%s]:\n%s\n", project, result)
183
+ log.info("[scratch_path]: [%s] [%s]", project, item.scratch_path)
184
+
185
+ # TODO: Implement HTML report using templates
186
+
187
+ results_dir = Path(self.results_dir)
188
+ results_dir.mkdir(exist_ok=True, parents=True)
189
+
190
+ # (results_dir / self.results_html_name).write_text(
191
+ # md_results_to_html(self.results_title, self.css_file, item.results_md)
192
+ # )
193
+
194
+ log.verbose("[report]: [%s] [%s/report.html]", project, item.results_dir)
195
+
196
+ self.errors_seen |= item.errors_seen
197
+
198
+ if self.is_primary_cfg:
199
+ self.gen_results_summary()
200
+ # self.write_results(self.results_html_name, self.results_summary_md)
dvsim/job/deploy.py CHANGED
@@ -74,6 +74,10 @@ class Deploy:
74
74
  # Cross ref the whole cfg object for ease.
75
75
  self.sim_cfg = sim_cfg
76
76
  self.flow = sim_cfg.name
77
+
78
+ if not hasattr(self.sim_cfg, "variant"):
79
+ self.sim_cfg.variant = ""
80
+
77
81
  self._variant_suffix = f"_{self.sim_cfg.variant}" if self.sim_cfg.variant else ""
78
82
 
79
83
  # A list of jobs on which this job depends.
dvsim/launcher/base.py CHANGED
@@ -339,25 +339,28 @@ class Launcher(ABC):
339
339
  # since it is devoid of the delays incurred due to infrastructure and
340
340
  # setup overhead.
341
341
 
342
- plugin = get_sim_tool_plugin(tool=self.job_spec.tool.name)
342
+ time = self.job_runtime_secs
343
+ unit = "s"
344
+
345
+ if self.job_spec.job_type in [
346
+ "CompileSim",
347
+ "RunTest",
348
+ "CovUnr",
349
+ "CovMerge",
350
+ "CovReport",
351
+ "CovAnalyze",
352
+ ]:
353
+ plugin = get_sim_tool_plugin(tool=self.job_spec.tool.name)
343
354
 
344
- try:
345
- time, unit = plugin.get_job_runtime(log_text=lines)
346
- self.job_runtime.set(time, unit)
347
-
348
- except RuntimeError as e:
349
- log.warning(
350
- f"{self.job_spec.full_name}: {e} Using dvsim-maintained job_runtime instead."
351
- )
352
- self.job_runtime.set(self.job_runtime_secs, "s")
353
-
354
- if self.job_spec.job_type == "RunTest":
355
355
  try:
356
- time, unit = plugin.get_simulated_time(log_text=lines)
357
- self.simulated_time.set(time, unit)
356
+ time, unit = plugin.get_job_runtime(log_text=lines)
358
357
 
359
358
  except RuntimeError as e:
360
- log.debug(f"{self.job_spec.full_name}: {e}")
359
+ log.warning(
360
+ f"{self.job_spec.full_name}: {e} Using dvsim-maintained job_runtime instead."
361
+ )
362
+
363
+ self.job_runtime.set(time, unit)
361
364
 
362
365
  if chk_failed or chk_passed:
363
366
  for cnt, line in enumerate(lines):
dvsim/report/data.py CHANGED
@@ -4,17 +4,11 @@
4
4
 
5
5
  """Report data models."""
6
6
 
7
- from collections.abc import Mapping
8
- from datetime import datetime
9
- from pathlib import Path
10
-
11
7
  from pydantic import BaseModel, ConfigDict
12
8
 
13
- from dvsim.sim_results import BucketedFailures
14
-
15
9
  __all__ = (
16
10
  "IPMeta",
17
- "ResultsSummary",
11
+ "ToolMeta",
18
12
  )
19
13
 
20
14
 
@@ -45,197 +39,3 @@ class ToolMeta(BaseModel):
45
39
  """Name of the tool."""
46
40
  version: str
47
41
  """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())
dvsim/sim/__init__.py ADDED
@@ -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."""
dvsim/sim/data.py ADDED
@@ -0,0 +1,259 @@
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 data models."""
6
+
7
+ from collections.abc import Mapping
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+
11
+ from pydantic import BaseModel, ConfigDict
12
+
13
+ from dvsim.report.data import IPMeta, ToolMeta
14
+ from dvsim.sim_results import BucketedFailures
15
+
16
+ __all__ = (
17
+ "CodeCoverageMetrics",
18
+ "CoverageMetrics",
19
+ "SimFlowResults",
20
+ "SimResultsSummary",
21
+ "TestResult",
22
+ "TestStage",
23
+ "Testpoint",
24
+ )
25
+
26
+
27
+ class TestResult(BaseModel):
28
+ """Test result."""
29
+
30
+ model_config = ConfigDict(frozen=True, extra="forbid")
31
+
32
+ max_time: float
33
+ """Run time."""
34
+ sim_time: float
35
+ """Simulation time."""
36
+
37
+ passed: int
38
+ """Number of tests passed."""
39
+ total: int
40
+ """Total number of tests run."""
41
+ percent: float
42
+ """Percentage test pass rate."""
43
+
44
+
45
+ class Testpoint(BaseModel):
46
+ """Testpoint."""
47
+
48
+ model_config = ConfigDict(frozen=True, extra="forbid")
49
+
50
+ tests: Mapping[str, TestResult]
51
+ """Test results."""
52
+
53
+ passed: int
54
+ """Number of tests passed."""
55
+ total: int
56
+ """Total number of tests run."""
57
+ percent: float
58
+ """Percentage test pass rate."""
59
+
60
+
61
+ class TestStage(BaseModel):
62
+ """Test stages."""
63
+
64
+ model_config = ConfigDict(frozen=True, extra="forbid")
65
+
66
+ testpoints: Mapping[str, Testpoint]
67
+ """Results by test point."""
68
+
69
+ passed: int
70
+ """Number of tests passed."""
71
+ total: int
72
+ """Total number of tests run."""
73
+ percent: float
74
+ """Percentage test pass rate."""
75
+
76
+
77
+ class CodeCoverageMetrics(BaseModel):
78
+ """CodeCoverage metrics."""
79
+
80
+ model_config = ConfigDict(frozen=True, extra="forbid")
81
+
82
+ block: float | None
83
+ """Block Coverage (%) - did this part of the code execute?"""
84
+ line_statement: float | None
85
+ """Line/Statement Coverage (%) - did this part of the code execute?"""
86
+ branch: float | None
87
+ """Branch Coverage (%) - did this if/case take all paths?"""
88
+ condition_expression: float | None
89
+ """Condition/Expression Coverage (%) - did the logic evaluate to 0 & 1?"""
90
+ toggle: float | None
91
+ """Toggle Coverage (%) - did the signal wiggle?"""
92
+ fsm: float | None
93
+ """FSM Coverage (%) - did the state machine transition?"""
94
+
95
+ @property
96
+ def average(self) -> float | None:
97
+ """Average code coverage (%)."""
98
+ all_cov = [
99
+ c
100
+ for c in [
101
+ self.line_statement,
102
+ self.branch,
103
+ self.condition_expression,
104
+ self.toggle,
105
+ self.fsm,
106
+ ]
107
+ if c is not None
108
+ ]
109
+
110
+ if len(all_cov) == 0:
111
+ return None
112
+
113
+ return sum(all_cov) / len(all_cov)
114
+
115
+
116
+ class CoverageMetrics(BaseModel):
117
+ """Coverage metrics."""
118
+
119
+ code: CodeCoverageMetrics | None
120
+ """Code Coverage."""
121
+ assertion: float | None
122
+ """Assertion Coverage."""
123
+ functional: float | None
124
+ """Functional coverage."""
125
+
126
+ @property
127
+ def average(self) -> float | None:
128
+ """Average code coverage (%) or None if there is no coverage."""
129
+ code = self.code.average if self.code is not None else None
130
+ all_cov = [
131
+ c
132
+ for c in [
133
+ code,
134
+ self.assertion,
135
+ self.functional,
136
+ ]
137
+ if c is not None
138
+ ]
139
+
140
+ if len(all_cov) == 0:
141
+ return None
142
+
143
+ return sum(all_cov) / len(all_cov)
144
+
145
+
146
+ class FlowResults(BaseModel):
147
+ """Flow results data."""
148
+
149
+ model_config = ConfigDict(frozen=True, extra="forbid")
150
+
151
+ block: IPMeta
152
+ """IP block metadata."""
153
+ tool: ToolMeta
154
+ """Tool used in the simulation run."""
155
+ timestamp: datetime
156
+ """Timestamp for when the test ran."""
157
+
158
+ stages: Mapping[str, TestStage]
159
+ """Results per test stage."""
160
+ coverage: CoverageMetrics | None
161
+ """Coverage metrics."""
162
+
163
+ failed_jobs: BucketedFailures
164
+ """Bucketed failed job overview."""
165
+
166
+ passed: int
167
+ """Number of tests passed."""
168
+ total: int
169
+ """Total number of tests run."""
170
+ percent: float
171
+ """Percentage test pass rate."""
172
+
173
+ @staticmethod
174
+ def load(path: Path) -> "FlowResults":
175
+ """Load results from JSON file.
176
+
177
+ Transform the fields of the loaded JSON into a more useful schema for
178
+ report generation.
179
+
180
+ Args:
181
+ path: to the json file to load.
182
+
183
+ """
184
+ return FlowResults.model_validate_json(path.read_text())
185
+
186
+
187
+ class SimFlowResults(BaseModel):
188
+ """Flow results data."""
189
+
190
+ model_config = ConfigDict(frozen=True, extra="forbid")
191
+
192
+ block: IPMeta
193
+ """IP block metadata."""
194
+ tool: ToolMeta
195
+ """Tool used in the simulation run."""
196
+ timestamp: datetime
197
+ """Timestamp for when the test ran."""
198
+
199
+ stages: Mapping[str, TestStage]
200
+ """Results per test stage."""
201
+ coverage: CoverageMetrics | None
202
+ """Coverage metrics."""
203
+
204
+ failed_jobs: BucketedFailures
205
+ """Bucketed failed job overview."""
206
+
207
+ passed: int
208
+ """Number of tests passed."""
209
+ total: int
210
+ """Total number of tests run."""
211
+ percent: float
212
+ """Percentage test pass rate."""
213
+
214
+ @staticmethod
215
+ def load(path: Path) -> "FlowResults":
216
+ """Load results from JSON file.
217
+
218
+ Transform the fields of the loaded JSON into a more useful schema for
219
+ report generation.
220
+
221
+ Args:
222
+ path: to the json file to load.
223
+
224
+ """
225
+ return FlowResults.model_validate_json(path.read_text())
226
+
227
+
228
+ class SimResultsSummary(BaseModel):
229
+ """Summary of results."""
230
+
231
+ model_config = ConfigDict(frozen=True, extra="forbid")
232
+
233
+ top: IPMeta
234
+ """Meta data for the top level config."""
235
+
236
+ timestamp: datetime
237
+ """Run time stamp."""
238
+
239
+ flow_results: Mapping[str, SimFlowResults]
240
+ """Flow results."""
241
+
242
+ report_path: Path
243
+ """Path to the report JSON file."""
244
+
245
+ @staticmethod
246
+ def load(path: Path) -> "SimResultsSummary":
247
+ """Load results from JSON file.
248
+
249
+ Transform the fields of the loaded JSON into a more useful schema for
250
+ report generation.
251
+
252
+ Args:
253
+ path: to the json file to load.
254
+
255
+ Returns:
256
+ The loaded ResultsSummary from JSON.
257
+
258
+ """
259
+ return SimResultsSummary.model_validate_json(path.read_text())
@@ -7,7 +7,7 @@
7
7
  import fnmatch
8
8
  import sys
9
9
  from collections import OrderedDict, defaultdict
10
- from collections.abc import Sequence
10
+ from collections.abc import Mapping, Sequence
11
11
  from datetime import datetime, timezone
12
12
  from pathlib import Path
13
13
  from typing import ClassVar
@@ -25,12 +25,24 @@ from dvsim.job.deploy import (
25
25
  from dvsim.logging import log
26
26
  from dvsim.modes import BuildMode, Mode, RunMode, find_mode
27
27
  from dvsim.regression import Regression
28
- from dvsim.report.data import FlowResults, IPMeta, Testpoint, TestResult, TestStage, ToolMeta
28
+ from dvsim.sim.data import (
29
+ IPMeta,
30
+ SimFlowResults,
31
+ SimResultsSummary,
32
+ Testpoint,
33
+ TestResult,
34
+ TestStage,
35
+ ToolMeta,
36
+ )
37
+ from dvsim.sim.report import gen_block_report, gen_reports
29
38
  from dvsim.sim_results import BucketedFailures, SimResults
30
39
  from dvsim.test import Test
31
40
  from dvsim.testplan import Testplan
32
41
  from dvsim.tool.utils import get_sim_tool_plugin
33
42
  from dvsim.utils import TS_FORMAT, rm_path
43
+ from dvsim.utils.git import git_commit_hash
44
+
45
+ __all__ = ("SimCfg",)
34
46
 
35
47
  # This affects the bucketizer failure report.
36
48
  _MAX_UNIQUE_TESTS = 5
@@ -574,13 +586,83 @@ class SimCfg(FlowCfg):
574
586
  for item in self.cfgs:
575
587
  item._cov_unr()
576
588
 
589
+ def gen_results(self, results: Sequence[CompletedJobStatus]) -> None:
590
+ """Generate flow results.
591
+
592
+ Args:
593
+ results: completed job status objects.
594
+
595
+ """
596
+ reports_dir = Path(self.scratch_base_path) / "reports"
597
+ commit = git_commit_hash(path=Path(self.proj_root))
598
+ url = f"https://github.com/lowrisc/opentitan/tree/{commit}"
599
+
600
+ all_flow_results: Mapping[str, SimFlowResults] = {}
601
+
602
+ for item in self.cfgs:
603
+ item_results = [
604
+ res
605
+ for res in results
606
+ if res.block.name == item.name and res.block.variant == item.variant
607
+ ]
608
+
609
+ flow_results: SimFlowResults = item._gen_json_results(
610
+ run_results=item_results,
611
+ commit=commit,
612
+ url=url,
613
+ )
614
+
615
+ # Convert to lowercase to match filename
616
+ block_result_index = (
617
+ f"{item.name}_{item.variant}" if item.variant else item.name
618
+ ).lower()
619
+
620
+ all_flow_results[block_result_index] = flow_results
621
+
622
+ # Generate the block's JSON/HTML reports to the report area.
623
+ gen_block_report(
624
+ results=flow_results,
625
+ path=reports_dir,
626
+ )
627
+
628
+ self.errors_seen |= item.errors_seen
629
+
630
+ if self.is_primary_cfg:
631
+ # The timestamp for this run has been taken with `utcnow()` and is
632
+ # stored in a custom format. Store it in standard ISO format with
633
+ # explicit timezone annotation.
634
+ timestamp = (
635
+ datetime.strptime(self.timestamp, "%Y%m%d_%H%M%S")
636
+ .replace(tzinfo=timezone.utc)
637
+ .isoformat()
638
+ )
639
+
640
+ results_summary = SimResultsSummary(
641
+ top=IPMeta(
642
+ name=self.name,
643
+ variant=self.variant,
644
+ commit=commit,
645
+ branch=self.branch,
646
+ url=url,
647
+ ),
648
+ timestamp=timestamp,
649
+ flow_results=all_flow_results,
650
+ report_path=reports_dir,
651
+ )
652
+
653
+ # Generate all the JSON/HTML reports to the report area.
654
+ gen_reports(
655
+ summary=results_summary,
656
+ path=reports_dir,
657
+ )
658
+
577
659
  def _gen_json_results(
578
660
  self,
579
661
  run_results: Sequence[CompletedJobStatus],
580
662
  commit: str,
581
663
  url: str,
582
- ) -> FlowResults:
583
- """Generate structured FlowResults from simulation run data.
664
+ ) -> SimFlowResults:
665
+ """Generate structured SimFlowResults from simulation run data.
584
666
 
585
667
  Args:
586
668
  run_results: completed job status.
@@ -700,7 +782,7 @@ class SimCfg(FlowCfg):
700
782
  failures = BucketedFailures.from_job_status(results=run_results)
701
783
 
702
784
  # --- Final result ---
703
- return FlowResults(
785
+ return SimFlowResults(
704
786
  block=block,
705
787
  tool=tool,
706
788
  timestamp=timestamp,
@@ -7,7 +7,7 @@
7
7
  from pathlib import Path
8
8
 
9
9
  from dvsim.logging import log
10
- from dvsim.report.data import FlowResults, ResultsSummary
10
+ from dvsim.sim.data import SimFlowResults, SimResultsSummary
11
11
  from dvsim.templates.render import render_static, render_template
12
12
 
13
13
  __all__ = (
@@ -17,7 +17,7 @@ __all__ = (
17
17
  )
18
18
 
19
19
 
20
- def gen_block_report(results: FlowResults, path: Path) -> None:
20
+ def gen_block_report(results: SimFlowResults, path: Path) -> None:
21
21
  """Generate a block report.
22
22
 
23
23
  Args:
@@ -47,7 +47,7 @@ def gen_block_report(results: FlowResults, path: Path) -> None:
47
47
  )
48
48
 
49
49
 
50
- def gen_summary_report(summary: ResultsSummary, path: Path) -> None:
50
+ def gen_summary_report(summary: SimResultsSummary, path: Path) -> None:
51
51
  """Generate a summary report.
52
52
 
53
53
  Args:
@@ -88,7 +88,7 @@ def gen_summary_report(summary: ResultsSummary, path: Path) -> None:
88
88
  )
89
89
 
90
90
 
91
- def gen_reports(summary: ResultsSummary, path: Path) -> None:
91
+ def gen_reports(summary: SimResultsSummary, path: Path) -> None:
92
92
  """Generate a full set of reports for the given regression run.
93
93
 
94
94
  Args:
@@ -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 Tools."""
@@ -8,7 +8,7 @@ from collections.abc import Mapping, Sequence
8
8
  from pathlib import Path
9
9
  from typing import Protocol, runtime_checkable
10
10
 
11
- from dvsim.report.data import CoverageMetrics
11
+ from dvsim.sim.data import CoverageMetrics
12
12
 
13
13
  __all__ = ("SimTool",)
14
14
 
@@ -8,7 +8,7 @@ import re
8
8
  from collections.abc import Mapping, Sequence
9
9
  from pathlib import Path
10
10
 
11
- from dvsim.report.data import CodeCoverageMetrics, CoverageMetrics
11
+ from dvsim.sim.data import CodeCoverageMetrics, CoverageMetrics
12
12
 
13
13
  __all__ = ("VCS",)
14
14
 
@@ -9,7 +9,7 @@ from collections import OrderedDict
9
9
  from collections.abc import Mapping, Sequence
10
10
  from pathlib import Path
11
11
 
12
- from dvsim.report.data import CodeCoverageMetrics, CoverageMetrics
12
+ from dvsim.sim.data import CodeCoverageMetrics, CoverageMetrics
13
13
 
14
14
  __all__ = ("Xcelium",)
15
15
 
dvsim/templates/render.py CHANGED
@@ -10,6 +10,7 @@ DVSim. Templates can be referenced relative to this directory.
10
10
 
11
11
  from collections.abc import Mapping
12
12
  from importlib import resources
13
+ from pathlib import Path
13
14
 
14
15
  from jinja2 import Environment, PackageLoader, select_autoescape
15
16
 
@@ -28,9 +29,11 @@ def render_static(path: str) -> str:
28
29
  string containing the static file content
29
30
 
30
31
  """
32
+ full_path = Path("dvsim/templates/static") / path
33
+
31
34
  return resources.read_text(
32
- "dvsim",
33
- f"templates/static/{path}",
35
+ ".".join(full_path.parts[:-1]), # Module path
36
+ full_path.name,
34
37
  )
35
38
 
36
39
 
dvsim/tool/utils.py CHANGED
@@ -5,9 +5,9 @@
5
5
  """EDA Tool base."""
6
6
 
7
7
  from dvsim.logging import log
8
- from dvsim.tool.sim import SimTool
9
- from dvsim.tool.vcs import VCS
10
- from dvsim.tool.xcelium import Xcelium
8
+ from dvsim.sim.tool.base import SimTool
9
+ from dvsim.sim.tool.vcs import VCS
10
+ from dvsim.sim.tool.xcelium import Xcelium
11
11
 
12
12
  __all__ = ("get_sim_tool_plugin",)
13
13
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dvsim
3
- Version: 1.7.0
3
+ Version: 1.7.2
4
4
  Summary: DV system
5
5
  Author: lowRISC contributors (OpenTitan project)
6
6
  License-File: LICENSE
@@ -10,29 +10,28 @@ dvsim/test.py,sha256=lMTieXHFIGEYQRJGJFsh_cP4bZ1_EadUolRWzIVmBvs,5610
10
10
  dvsim/testplan.py,sha256=Qhne2FqlU8R9UGB5o3QOufMVh1xdbKFMhtLvjvyGyts,28389
11
11
  dvsim/testplanner.py,sha256=kvgb6iAbmfaKmOLem1crKm49eygvOQX1q79Jv5UAHe0,1326
12
12
  dvsim/cli/__init__.py,sha256=vggha7pCXj7dUK4VZChIR37MhKONBcJqt3j8l9xKM_U,238
13
- dvsim/cli/admin.py,sha256=vDCMYrcSD6MQTdbB1wxsjX8_96qIsOu43yddaRjXjwA,1181
13
+ dvsim/cli/admin.py,sha256=bBnz0ZX1gd8RBTLd4CB3ZFvm08ayIjUUszI-rauxIK4,1182
14
14
  dvsim/cli/run.py,sha256=_S6ejDxGRyxMYKgNreCH6CC7vLFoJFRPmnCTPqQ6PBc,27968
15
15
  dvsim/examples/testplanner/common_testplan.hjson,sha256=IDkf3AYleDdbuEarj8FItBhg5lMgoVrfgnAMzTU9_IM,806
16
16
  dvsim/examples/testplanner/foo_dv_doc.md,sha256=AZkreUFyAehr2-6xh6Z7eRo4Eok9HFUH4TrDnByDor0,331
17
17
  dvsim/examples/testplanner/foo_sim_results.hjson,sha256=WAx9FIwS11oybfw1BU7AmY_14tRDXWtoLBxAL0ZDh8I,1642
18
18
  dvsim/examples/testplanner/foo_testplan.hjson,sha256=QQs4Egu77svReT9N3pIBSDP-xWCqoicQCo4Y_2-ZybA,2307
19
19
  dvsim/flow/__init__.py,sha256=K6yruWpqvtuLCORlnTspPxwBjsIpQhMaCfQSqqtyvTQ,185
20
- dvsim/flow/base.py,sha256=jUZkH0dzTnBAX8U1Hg6OaanGe6Xj9j_6bB1sSrrtytM,18946
20
+ dvsim/flow/base.py,sha256=2Ac15oc3rDpnAFlaKabLOMSOGwyi9ReVTd9zdeoLcgs,16602
21
21
  dvsim/flow/cdc.py,sha256=87aDKAsHNDnFs4dRkclQSJ5-Pm_djY8BlSR-nzcPAMY,558
22
- dvsim/flow/factory.py,sha256=J4Z3b2wTLn0pd944bca0wHSCCrN3ybCFkqcEMKHOuYo,3862
22
+ dvsim/flow/factory.py,sha256=Wtp3o87vqbGzZwku4I2zxAizEMzNyZRgDwgj7aWjw4U,3862
23
23
  dvsim/flow/formal.py,sha256=DVu-MkKJP-bJrfskXBIfqnL_sQTbqbDRuNiLv9_QQ2E,9935
24
24
  dvsim/flow/hjson.py,sha256=iKR8gAHZl1XsYNkTPaePwKSkAcPgqO8BlKNBs4ER3XY,6483
25
- dvsim/flow/lint.py,sha256=2HxItKwCZK9JC6irG0vfmK5XTDw3MxdmgsSRZWEbibQ,6960
26
- dvsim/flow/one_shot.py,sha256=5YaEQZmvO-655bl1Nhp79_gCaj_h1YYVjoah1lqRcsc,5155
25
+ dvsim/flow/lint.py,sha256=YeLR8eegChWWkJj-jMtzqZp6VWN8grbuxz4kclaN9us,7063
26
+ dvsim/flow/one_shot.py,sha256=qtbqQvql_VMoO33lfEz9qM7aGpr_CKSG4nXA1gbooBQ,6772
27
27
  dvsim/flow/rdc.py,sha256=pu7GKw-eNd3RXdhq6oFKidBOflMtr1joAokS3SosTi4,562
28
- dvsim/flow/sim.py,sha256=iHGrJvz321WBPetGZWq_tXACRW0R8wHaVHt_Ea1E8kg,28009
29
28
  dvsim/flow/syn.py,sha256=LnwDM6dUhxNXRoTcUCbESZ2VCpBYEFdfuur3jsojejQ,15996
30
29
  dvsim/job/__init__.py,sha256=EtF6aoKkGy3sxCsfQMm2MAHgjYknmI1No90xvyjXRpY,186
31
30
  dvsim/job/data.py,sha256=dsc4bnAvTMW74m8QItCGMKJTyXa50fjTWngPtkw2lUc,4460
32
- dvsim/job/deploy.py,sha256=0J7suzihsKiFabJ5nlsa0d2hJGqO6salX11ATPfZz-A,31388
31
+ dvsim/job/deploy.py,sha256=Ec5pmsGOtxGnwcAq4qh7b2B6URjlejyf6jLNwZo_aVg,31477
33
32
  dvsim/job/time.py,sha256=RLQn16cIEGRKmRf5bQC1h1P8SaSBAIjvynyDyYrKUrY,5369
34
33
  dvsim/launcher/__init__.py,sha256=P-A-eAsx-3Nz4b3mnautm9fGV02qVelwBrNX_PGfOUw,188
35
- dvsim/launcher/base.py,sha256=adjA2_XBurCiMUWrB0vmAQIejhpwntmHNs50Ybzv6g0,15099
34
+ dvsim/launcher/base.py,sha256=TitiE4BTTkg0hcT-cKL9neYD6oudZXC0FpOGN_WpTfM,15027
36
35
  dvsim/launcher/factory.py,sha256=zs4bJ4n2TeWbbVk1IRXAazC5viNg4mPUITofP0fqHEc,2620
37
36
  dvsim/launcher/fake.py,sha256=ODtwNOzgaGYklPQPy3J69uWOlWbMGUw67sAmWQoENHQ,2426
38
37
  dvsim/launcher/local.py,sha256=eoCYkmkm-uS-zezVn4oRp7OuTwCnWr20ewtUFnxFmg8,6816
@@ -46,10 +45,17 @@ dvsim/launcher/sge/qsubopts.py,sha256=RxjgULUI_JDrdsn1VxQnBSxW0Z-4nfzP8w6l9x3tm6
46
45
  dvsim/linting/__init__.py,sha256=SOXGwiOyg_dBPIHSFwctNLKk2M84jXpDpZjjGyPmcPA,183
47
46
  dvsim/linting/parser.py,sha256=19o4F5YTh43u1s9Hx7nPeGujYgo61v44JHpPiy-e-Hw,3722
48
47
  dvsim/report/__init__.py,sha256=m58UrZ_U3jKI0QlB3r6cGdRb-QfgzN7t0udJpYa2DMs,196
49
- dvsim/report/data.py,sha256=MC9Apc3H7Cd3k0ykaJ9b3EsdZc-nfTnUaIsSPoHkesM,5829
50
- dvsim/report/generate.py,sha256=2zDJkMmpb71tlMxhopMq2vV_XDhOw2r5krSI0nZ4btQ,2634
48
+ dvsim/report/data.py,sha256=Li_FSIPT7yEdF5buLLEbx7DnTX3eHf1UZ5aBHk6C5Es,935
49
+ dvsim/sim/__init__.py,sha256=XU7GhNkaQxhHqsO_P5XDXnWURcT27iScKg630EQLJ40,195
50
+ dvsim/sim/data.py,sha256=J1PkWk2ivFZm0FLUmLexGb3KAAhgnT51KDE6oZ1FqNM,6369
51
+ dvsim/sim/flow.py,sha256=h1-zgd3_yGhODwD0EcKoknGU8xeVeWMb3uc1apQ1YI8,30552
52
+ dvsim/sim/report.py,sha256=ljpmFu_ln3plZyhNs2X2_xkRFqERk5Txhn_zXvFl4fU,2646
53
+ dvsim/sim/tool/__init__.py,sha256=VCe2PQYQUkthjXjbSVSxNALwO1t0Y6mVW6eszl_JtW4,192
54
+ dvsim/sim/tool/base.py,sha256=HQJDapJQEf6GsPGJPDsNpNA4omk7i0CqzzxT27-ytjk,2573
55
+ dvsim/sim/tool/vcs.py,sha256=qRxvr8KVaNJrcshPqpbwhCuyKq8o7dbb3X4cHh8wYSo,4813
56
+ dvsim/sim/tool/xcelium.py,sha256=z_fbSODH7IGI-5ivZQ1WPlideY9Qo2OcMra0HlJNpOM,6155
51
57
  dvsim/templates/__init__.py,sha256=vZhkgLLCkdOPjOK_wGaa2AqDLs2fZLuc3_TJK62ttA8,195
52
- dvsim/templates/render.py,sha256=ENCYDWbvoi9M3HPmDdCoh7LF7pYjiaSRzOLk1ssm5LM,1476
58
+ dvsim/templates/render.py,sha256=uAc798QYvZJBugTl53XFz1nft8ZK_YFsyIK0oQWWErQ,1582
53
59
  dvsim/templates/reports/__init__.py,sha256=eu0aBRe908Or6yAc4K61fuxUx-48lhjqBpsslUtn5Z8,192
54
60
  dvsim/templates/reports/block_report.html,sha256=3gP-v3uS2jpmgBjqS9UvSiQu6PZ4khAf9relJJZL1qo,9768
55
61
  dvsim/templates/reports/redirect.html,sha256=nGUoYTSrNiVXWXpluFMoHjdP5QldtEXeZMGd6Y2rPzc,593
@@ -62,11 +68,8 @@ dvsim/templates/static/js/htmx.min.js,sha256=Iig-9oy3VFkU8KiKG97cclanA9HVgMHSVSF
62
68
  dvsim/tool/__init__.py,sha256=Y8HxxxI8UTJGDg07FFytr1pVujdBsbtHulthnEOZHcI,197
63
69
  dvsim/tool/ascentlint.py,sha256=reBueV8vqEgwwmcbBNncVM4Z4fAFqxqRzLv5rVgcCh8,4047
64
70
  dvsim/tool/meridianrdc.py,sha256=RKAhQ5Rk6SfSc3DurZTQJP36qSpSAIej42plGq5ApL0,9675
65
- dvsim/tool/sim.py,sha256=mttJ57FS16vXN6_r8NAFBieCjDDlNcXw-4oYaNXfQA0,2576
66
- dvsim/tool/utils.py,sha256=bBK-y1PjTzuOBoEUsCjW7XcAkHKd5fcXFWGv9060ktI,834
67
- dvsim/tool/vcs.py,sha256=G1J8N_8xQ0VHDGle16RtVhcTCanOYPtTol4rlOAs34E,4816
71
+ dvsim/tool/utils.py,sha256=lxLO1f0DdeATzW1XMQuJfzKOLVrmpyTD9lmgAmZM5U4,847
68
72
  dvsim/tool/verilator.py,sha256=MQykIh1Ua7PcCHmwxLaieuDx43B48bNWrJ_oviVHtQs,3655
69
- dvsim/tool/xcelium.py,sha256=T0X3H6_DP34jmi6IKha6S3LLbtnP9rt0qybljmcu_dk,6158
70
73
  dvsim/utils/__init__.py,sha256=UYFviDvztoGOBA6btN9xBCwRhaaagK45X4M0Kc1xQwU,1102
71
74
  dvsim/utils/check.py,sha256=b8bu_0zJHk4ieD5erlGfaG2F1GiDwadAvqB6kW4mOsU,994
72
75
  dvsim/utils/fs.py,sha256=NqPmnRlP8l2l1Cy3nAkLsPvEn_o2yQavP5dsUanQ_ZU,3859
@@ -77,8 +80,8 @@ dvsim/utils/status_printer.py,sha256=azwNWS83rI-h7OfOYPIj-Sy46aIz35L3y_Yiwkx60ZE
77
80
  dvsim/utils/subprocess.py,sha256=8jje9hmwZ_JH0zQ8JNwhsNldntgI3_SF6L5SUHQ9luo,1829
78
81
  dvsim/utils/timer.py,sha256=jD4R3wB_rBxaefiMr-k34aH325xdcoqtHHRCtSczSC8,1604
79
82
  dvsim/utils/wildcards.py,sha256=5WkByZLHyPlo5lD4rIXPuDjjrhrWqNpcTYxmQ5k294o,11701
80
- dvsim-1.7.0.dist-info/METADATA,sha256=qobLOXeIEAWXcwmdzYIcY7JxnYaZrxO-q9c_1Hxqgtc,5882
81
- dvsim-1.7.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
82
- dvsim-1.7.0.dist-info/entry_points.txt,sha256=6A-YOJ6XYbedSfSJ5dP56lRkoHYrLanJt0AYNH9w3r0,75
83
- dvsim-1.7.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
84
- dvsim-1.7.0.dist-info/RECORD,,
83
+ dvsim-1.7.2.dist-info/METADATA,sha256=Mpw4nSelVLJwKDLZjhN6NW26vtic8yVcbH1mNyxARFM,5882
84
+ dvsim-1.7.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
85
+ dvsim-1.7.2.dist-info/entry_points.txt,sha256=6A-YOJ6XYbedSfSJ5dP56lRkoHYrLanJt0AYNH9w3r0,75
86
+ dvsim-1.7.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
87
+ dvsim-1.7.2.dist-info/RECORD,,
File without changes