Skip to content

Commit c5e4439

Browse files
authored
RTL riscv instruction tests (#583)
* rtl: prepare RTL test base - refactor firtool build image - prepare dependencies for test-base image * rtl: Integrate RTL tests into openvadl tests * fix: checkstyle issue * rtl: Add build action for rtl-test-base image * rtl: Fix signal name conflict * rtl: Add image digest to firtool image import * rtl: Use ghcr rtl-test-base image * ci: Add test-rtl task to CI workflow * rtl: Use latest rtl-test-base image
1 parent 3ca4b6f commit c5e4439

File tree

9 files changed

+166
-7
lines changed

9 files changed

+166
-7
lines changed

.github/workflows/openvadl-ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
uses: ./.github/workflows/reusable-test.yml
4242
strategy:
4343
matrix:
44-
task: [ test-lcb, test-common, test-iss ]
44+
task: [ test-lcb, test-common, test-iss, test-rtl ]
4545
include:
4646
- runner: self-hosted
4747
with:
@@ -60,4 +60,4 @@ jobs:
6060
run: exit 0
6161
- name: Failing CI
6262
if: ${{ contains(needs.*.result, 'failure') }}
63-
run: exit 1
63+
run: exit 1

docker/rtl/test-base/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
FROM ghcr.io/openvadl/merged-toolchain@sha256:2ceaf5369996a5f3db0e0bb8e16ae5fe53003d6ac180bd9e60a9ced954f99a9a AS toolchains
2-
FROM ghcr.io/openvadl/firtool AS firtool
2+
FROM ghcr.io/openvadl/firtool@sha256:84c7b84322068a9b800c908a13656a6d756a507751230dad1c99c38b96bad335 AS firtool
33

44
FROM ubuntu:22.04 AS build
55

vadl/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ tasks.withType<Test> {
135135
}
136136

137137
// generators to be tested separately
138-
val generators = listOf("iss", "lcb")
138+
val generators = listOf("iss", "lcb", "rtl")
139139

140140
for (gen in generators) {
141141
tasks.register<Test>("test-$gen") {

vadl/main/resources/templates/rtl/CoreTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class CoreTest extends AnyFunSpec with ChiselSim {
5151
simulate(new [(${topModule})]) { dut =>
5252
enableWaves()
5353

54-
new java.io.File("riscv-tests/isa").listFiles(_.getName.matches("[(${#strings.substring(isaName, 0, 4).toLowerCase()})]ui-p-[^.]*"))
54+
new java.io.File("/riscv-tests/isa").listFiles(_.getName.matches("[(${#strings.substring(isaName, 0, 4).toLowerCase()})]ui-p-[^.]*"))
5555
.filter(file => !file.getName.contains("fence")) // not supported
5656
.foreach(file => {
5757
println(f"test $file")

vadl/main/resources/templates/rtl/Makefile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Makefile for [(${projectName})]
22

3-
test: riscv-tests
3+
SHELL=/bin/bash
4+
5+
test:
6+
mkdir -p build
7+
touch build/[(${topModule})]-riscv-tests.log
8+
set -o pipefail # Preserve test exit code when piping to tee
49
sbt test 2>&1 | tee build/[(${topModule})]-riscv-tests.log
510

611
riscv-tests:
@@ -52,4 +57,4 @@ SCALAFMT_URL = https://github.com/scalameta/scalafmt/releases/download/v$(SCALAF
5257

5358
format: .scalafmt/scalafmt$(SCALAFMT_EXT)
5459
$< [# th:each="file : ${scalaFiles}"]\
55-
[(${file})][/]
60+
[(${file})][/]

vadl/main/vadl/rtl/template/HdlEmitContext.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public String name(Collection<Node> inlinedNodes, Set<String> existing,
102102
nodeContext.shortestNameHint(existing, 30)
103103
.or(() -> fallback(inlinedNode, existing))
104104
.orElse("n_" + nodeContext.node().id().numericId()))
105+
.filter(name -> !existing.contains(name))
105106
.orElseGet(() -> {
106107
if (fallback == null || existing.contains(fallback)) {
107108
return fallback(inlinedNode, existing)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
3+
cd /rtl
4+
5+
mkdir -p build
6+
touch build/rv32i-riscv-tests.log
7+
8+
# Preserve sbt exit code when piping to tee
9+
set -o pipefail
10+
11+
sbt test 2>&1 | tee build/rv32i-riscv-tests.log
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// SPDX-FileCopyrightText : © 2025 TU Wien <vadl@tuwien.ac.at>
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package vadl.rtl;
18+
19+
import java.io.IOException;
20+
import java.nio.file.Path;
21+
import java.util.Set;
22+
import java.util.concurrent.ConcurrentHashMap;
23+
import org.testcontainers.images.builder.ImageFromDockerfile;
24+
import vadl.DockerExecutionTest;
25+
import vadl.configuration.RtlConfiguration;
26+
import vadl.pass.PassOrders;
27+
import vadl.pass.exception.DuplicatedPassKeyException;
28+
29+
public abstract class RtlDockerTest extends DockerExecutionTest {
30+
31+
private static final String RTL_BASE_IMAGE =
32+
"ghcr.io/openvadl/rtl-test-base@sha256:975bacbae46da25dda52340a18772d349c7e43e3dd8aae43be61f254fc728ca8";
33+
34+
private static final ConcurrentHashMap<String, ImageFromDockerfile> imageCache =
35+
new ConcurrentHashMap<>();
36+
37+
protected ImageFromDockerfile generateRtlImage(String specPath,
38+
RtlConfiguration configuration) {
39+
40+
final var cacheKey = String.valueOf(Set.of(specPath, configuration).hashCode());
41+
return RtlDockerTest.imageCache.computeIfAbsent(cacheKey, (k) -> {
42+
43+
try {
44+
// Generate RTL core
45+
setupPassManagerAndRunSpec(specPath, PassOrders.rtl(configuration));
46+
47+
// Return image with generated files
48+
return getImage(configuration);
49+
} catch (IOException | DuplicatedPassKeyException e) {
50+
throw new RuntimeException(e);
51+
}
52+
});
53+
}
54+
55+
private ImageFromDockerfile getImage(RtlConfiguration configuration
56+
) {
57+
58+
// find output dir
59+
var outputPath = Path.of(configuration.outputPath() + "/rtl").toAbsolutePath();
60+
if (!outputPath.toFile().exists()) {
61+
throw new IllegalStateException("RTL output path was not found (not generated?)");
62+
}
63+
64+
// Get redis cache for faster compilation using sccache
65+
var redisCache = getRunningRedisCache();
66+
67+
var dockerImage = new ImageFromDockerfile()
68+
.withDockerfileFromBuilder(d -> {
69+
d.from(RTL_BASE_IMAGE);
70+
71+
d.workDir("/rtl");
72+
73+
// Copy files into container
74+
d.copy("/rtl", "/rtl");
75+
d.copy("/scripts", "/scripts");
76+
77+
d.run("chmod +x /scripts/*");
78+
79+
d.build();
80+
}
81+
)
82+
// Make generated sources available to image builder
83+
.withFileFromPath("/rtl", outputPath)
84+
// make scripts available to image builder
85+
.withFileFromClasspath("/scripts", "/scripts/rtl");
86+
87+
// As we have to use the same network as the redis cache, we have to build it there
88+
return redisCache.setupEnv(dockerImage);
89+
}
90+
91+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-FileCopyrightText : © 2025 TU Wien <vadl@tuwien.ac.at>
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package vadl.rtl.riscv;
18+
19+
import java.nio.file.Path;
20+
import org.junit.jupiter.api.Test;
21+
import vadl.configuration.DecoderOptions;
22+
import vadl.configuration.GeneralConfiguration;
23+
import vadl.configuration.RtlConfiguration;
24+
import vadl.rtl.RtlDockerTest;
25+
26+
public class RiscVInstructionTest extends RtlDockerTest {
27+
28+
@Test
29+
void rv32imTest() {
30+
31+
/* GIVEN */
32+
var generalConfig =
33+
new GeneralConfiguration(Path.of("build/test-output"), false);
34+
var config = new RtlConfiguration(generalConfig);
35+
36+
var decoderOptions = new DecoderOptions();
37+
decoderOptions.setGenerator(DecoderOptions.Generator.REGULAR);
38+
config.setDecoderOptions(decoderOptions);
39+
40+
var image = generateRtlImage("sys/risc-v/rv32im.vadl", config);
41+
42+
runContainer(image,
43+
/* WHEN */
44+
c -> c.withCommand("/scripts/test.sh"),
45+
/* THEN */
46+
c -> {
47+
// No post actions for now, we rely on the exit code instead
48+
});
49+
}
50+
51+
}

0 commit comments

Comments
 (0)