diff --git a/.gitattributes b/.gitattributes
index 097f9f98d..6abd78759 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,8 +1,9 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
-# Linux start script should use lf
+# Linux scripts should use lf
/gradlew text eol=lf
+*.sh text eol=lf
# These are Windows script files and should use crlf
*.bat text eol=crlf
diff --git a/sys/risc-v/mia/rv_1stage.vadl b/sys/risc-v/mia/rv_1stage.vadl
new file mode 100644
index 000000000..0ec871736
--- /dev/null
+++ b/sys/risc-v/mia/rv_1stage.vadl
@@ -0,0 +1,25 @@
+// SPDX-FileCopyrightText : © 2026 TU Wien
+// SPDX-License-Identifier: Apache-2.0
+
+import "../rv32i"::RV32I
+import "../rv32im"::RV32IM
+
+import "../rv64i"::RV64I
+import "../rv64im"::RV64IM
+
+model Isa() : Id = {RV32I} // invoke vadl -m "Isa=RV64I" for 64 bit arch
+
+/**
+ * Single stage MiA description.
+ *
+ * Placing the whole instruction behavior in a single pipeline stage may require the stage to
+ * take more than one cycle for execution. In this case an implementation could share resources
+ * between the execution steps (which our hardware generation currently does not do).
+ */
+micro architecture SingleStage implements $Isa = {
+
+ stage ISS -> ( ) = {
+ let instr = decode( fetchNext ) in // fetch next instruction from memory and decode
+ instr.write
+ }
+}
diff --git a/sys/risc-v/mia/rv_3stage.vadl b/sys/risc-v/mia/rv_3stage.vadl
new file mode 100644
index 000000000..6aacec6e3
--- /dev/null
+++ b/sys/risc-v/mia/rv_3stage.vadl
@@ -0,0 +1,44 @@
+// SPDX-FileCopyrightText : © 2026 TU Wien
+// SPDX-License-Identifier: Apache-2.0
+
+import "../rv32i"::RV32I
+import "../rv32im"::RV32IM
+
+import "../rv64i"::RV64I
+import "../rv64im"::RV64IM
+
+model Isa() : Id = {RV32I} // invoke vadl -m "Isa=RV64I" for 64 bit arch
+
+/**
+ * Three stage MiA description.
+ *
+ * This models a pipeline with stages Instruction Fetch, Instruction Decode and Execute following
+ * the design of the Wildcat educational processor.
+ *
+ * Schoeberl, M. (2025). Wildcat: Educational RISC-V Microprocessors. arXiv.
+ * http://arxiv.org/abs/2502.20197
+ */
+micro architecture ThreeStage implements $Isa = {
+
+ stage IF -> ( fr : FetchResult ) = {
+ fr := fetchNext // fetch next instruction from memory (or cache)
+ }
+
+ stage ID -> ( ir : Instruction ) = {
+ let instr = decode( IF.fr ) in { // decode the fetch packet, gives an Instruction
+// if ( instr.unknown ) then
+// raise invalidInstruction // raise an invalid instruction exception
+ instr.address( @X ) // compute addresses to access register file X
+ instr.read( @X ) // read from the X register file
+ instr.read( @PC ) // read from the PC
+ ir := instr // stage output is the Instruction ir
+ }
+ }
+
+ stage EX -> ( ir : Instruction ) = {
+ let instr = ID.ir in {
+ instr.write // write PC
+ ir := instr
+ }
+ }
+}
diff --git a/sys/risc-v/rv64imFive.vadl b/sys/risc-v/mia/rv_5stage.vadl
similarity index 67%
rename from sys/risc-v/rv64imFive.vadl
rename to sys/risc-v/mia/rv_5stage.vadl
index 6124219e1..607b8018d 100644
--- a/sys/risc-v/rv64imFive.vadl
+++ b/sys/risc-v/mia/rv_5stage.vadl
@@ -1,21 +1,27 @@
// SPDX-FileCopyrightText : © 2024 TU Wien
// SPDX-License-Identifier: Apache-2.0
-// RISC-V 32 I instruction set
+// RISC-V five stage pipeline
-import rv64im::{RV64IM}
+import "../rv32i"::RV32I
+import "../rv32im"::RV32IM
-micro architecture FiveStage implements RV64IM = {
+import "../rv64i"::RV64I
+import "../rv64im"::RV64IM
+
+model Isa() : Id = {RV32I} // invoke vadl -m "Isa=RV64I" for 64 bit arch
+
+micro architecture FiveStage implements $Isa = {
stage FETCH -> ( fr : FetchResult ) = {
- fr := fetchNext // fetch next packet from memory (or cache)
+ fr := fetchNext // fetch next instruction from memory (or cache)
}
stage DECODE -> ( ir : Instruction ) = {
let instr = decode( FETCH. fr ) in { // decode the fetch packet, gives an Instruction
- if ( instr.unknown ) then
- raise invalidInstruction // raise an invalid instruction exception
- instr.address( @X ) // output computed address (X + offset ) to memory
+// if ( instr.unknown ) then
+// raise invalidInstruction // raise an invalid instruction exception
+ instr.address( @X ) // compute addresses to access register file X
instr.read( @X ) // read from the X register file
instr.read( @PC ) // read from the PC
ir := instr // stage output is the Instruction ir
diff --git a/sys/risc-v/rv64imFiveMul.vadl b/sys/risc-v/mia/rv_5stage_mul.vadl
similarity index 90%
rename from sys/risc-v/rv64imFiveMul.vadl
rename to sys/risc-v/mia/rv_5stage_mul.vadl
index 5cf4f118a..f5a1cf4e1 100644
--- a/sys/risc-v/rv64imFiveMul.vadl
+++ b/sys/risc-v/mia/rv_5stage_mul.vadl
@@ -1,11 +1,15 @@
// SPDX-FileCopyrightText : © 2024 TU Wien
// SPDX-License-Identifier: Apache-2.0
-// RISC-V 64 five stage pipeline with longer mul/div pipeline
+// RISC-V five stage pipeline with longer mul/div pipeline
-import rv64im::{RV64IM}
+import "../rv32im"::RV32IM
-micro architecture FiveStage implements RV64IM = {
+import "../rv64im"::RV64IM
+
+model Isa() : Id = {RV32IM} // invoke vadl -m "Isa=RV64IM" for 64 bit arch
+
+micro architecture FiveStageMul implements $Isa = {
operation AddOps = {ADD, SUB, AND, OR, XOR, SLT, SLTU, SLL, SRL, SRA}
operation ImmOps = {ADDI, ANDI, ORI, XORI, SLTI, SLTIU, AUIPC, LUI}
diff --git a/vadl-cli/main/vadl/cli/RtlCommand.java b/vadl-cli/main/vadl/cli/RtlCommand.java
index 03a0a79e9..0587952a1 100644
--- a/vadl-cli/main/vadl/cli/RtlCommand.java
+++ b/vadl-cli/main/vadl/cli/RtlCommand.java
@@ -37,12 +37,6 @@
)
public class RtlCommand extends BaseCommand {
- @CommandLine.Option(names = {"--dummy-mia"},
- scope = INHERIT,
- description = "Select a dummy MiA: ${COMPLETION-CANDIDATES} (stages in pipeline)",
- defaultValue = "five")
- RtlConfiguration.DummyMia dummyMia = RtlConfiguration.DummyMia.five;
-
@CommandLine.Option(names = {"--memory"},
scope = INHERIT,
description = "Configure external memory interface: ${COMPLETION-CANDIDATES}",
@@ -84,6 +78,12 @@ public class RtlCommand extends BaseCommand {
defaultValue = "false")
boolean keepSignals = false;
+ @CommandLine.Option(names = {"--rvfi"},
+ scope = INHERIT,
+ description = "Emit outputs for the RISC-V Formal Interface.",
+ defaultValue = "false")
+ boolean emitRVFI = false;
+
@CommandLine.Option(names = {"--dry-run"},
scope = INHERIT,
description = "Don't emit generated files.")
@@ -92,13 +92,13 @@ public class RtlCommand extends BaseCommand {
@Override
PassOrder passOrder(GeneralConfiguration configuration) throws IOException {
var rtlConfig = new RtlConfiguration(configuration);
- rtlConfig.setDummyMia(dummyMia);
rtlConfig.setMemory(memory);
rtlConfig.setResetVector(resetVector);
rtlConfig.setKeepSignals(keepSignals);
rtlConfig.setScalaPackageAndDirs(scalaPackage);
rtlConfig.setTopModule(topModule);
rtlConfig.setProjectName(projectName);
+ rtlConfig.setEmitRVFI(emitRVFI);
rtlConfig.setDryRun(dryRun);
return PassOrders.rtl(rtlConfig);
}
diff --git a/vadl/main/resources/templates/rtl/Makefile b/vadl/main/resources/templates/rtl/Makefile
index 6f5175b95..db5ce00be 100644
--- a/vadl/main/resources/templates/rtl/Makefile
+++ b/vadl/main/resources/templates/rtl/Makefile
@@ -1,29 +1,36 @@
# Makefile for [(${projectName})]
-SHELL=/bin/bash
+# Preserve test exit code when piping
+SHELL=/bin/bash -o pipefail
+
+.PHONY: test
test:
- mkdir -p build
- touch build/[(${topModule})]-riscv-tests.log
- set -o pipefail # Preserve test exit code when piping to tee
- sbt test 2>&1 | tee build/[(${topModule})]-riscv-tests.log
-
-riscv-tests:
- git clone https://github.com/riscv/riscv-tests
- cd riscv-tests && \
- git checkout b5ba87097c42aa41c56657e0ae049c2996e8d8d8 && \
- git submodule update --init --recursive
- # remove multicore disable because mhartid is not implemented
- sed -ie '/#define RISCV_MULTICORE_DISABLE/{n;N;d}' riscv-tests/env/p/riscv_test.h
- sed -ie '/csrr a0, mhartid;/d' riscv-tests/env/p/riscv_test.h
-[# th:unless="${hasEcall}"] # patch ecall to also write test result to tohost address
- sed -i '/ecall/i \
- la a4, tohost; \\\
- sw TESTNUM, 0(a4); \\' riscv-tests/env/p/riscv_test.h
-[/] cd riscv-tests && \
- autoconf && \
- ./configure --prefix=/usr && \
- RISCV_GCC_OPTS="-static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -I/usr/include" make isa
+ sbt "testOnly ElfSimTest"
+
+# Elfsim with C++ memory model
+SIM_BUILD=build/elfsim
+SIM_WORK=$(SIM_BUILD)/workdir-verilator
+SIM=$(SIM_WORK)/simulation
+SIM_LOG=$(SIM_BUILD)/simulation.log
+SIM_RPT=$(SIM_BUILD)/simulation.rpt
+
+TESTS?=$(filter-out %.dump,$(wildcard /riscv-tests/isa/[(${#strings.substring(isaName, 0, 4).toLowerCase()})]ui-p-*))
+TEST_OUT=$(SIM_BUILD)/tests
+
+$(SIM):
+ sbt "test"
+
+$(TEST_OUT): $(SIM)
+ mkdir -p $(TEST_OUT)
+
+$(SIM_RPT): $(SIM) $(TESTS) $(TEST_OUT)
+ (for t in $(TESTS); do echo "test $$t"; \
+ echo -e "R a\nS 1 0\nR a\nS 1 1\nT 0 0,1-5*1\nS 1 0\nR 0\nW 1\nT 0 0,1-5*ffff\nD" \
+ | ($(SIM) "+elf=$$t" 2>&1 || true) | tee $(TEST_OUT)/`basename $$t`.log ; \
+ mv trace.vcd $(TEST_OUT)/`basename $$t`.vcd; done) \
+ | tee $(SIM_LOG)
+ grep -E "^(test|^RVTEST_)" $(SIM_LOG) | tee $(SIM_RPT)
# scalafmt download and format target
ifeq ($(OS),Windows_NT)
diff --git a/vadl/main/resources/templates/rtl/cpp/SimMem.cpp b/vadl/main/resources/templates/rtl/cpp/SimMem.cpp
new file mode 100644
index 000000000..211f21a0b
--- /dev/null
+++ b/vadl/main/resources/templates/rtl/cpp/SimMem.cpp
@@ -0,0 +1,215 @@
+#include
+#include
*/
public String toIDEString(IDEDetectionMode mode) {
+ return toIDEString(mode, false);
+ }
+
+ /**
+ * Produces version that is easily understandable for IDE's.
+ *
+ * E.g.: {@code SourceLocation("relative/path/to/file.vadl", (1, 3), (2, 4))}
+ * becomes {@code "relative/path/to/file.vadl:1:3"}
+ *
+ */
+ public String toIDEString(IDEDetectionMode mode, boolean forceUnixPaths) {
if (!this.isValid()) {
return "Source Location was lost";
}
@@ -237,6 +249,9 @@ public String toIDEString(IDEDetectionMode mode) {
var currentWorkingDir = Paths.get(System.getProperty("user.dir"));
printablePath = currentWorkingDir.relativize(filePath).toFile().getPath();
}
+ if (forceUnixPaths) {
+ printablePath = printablePath.replace(FileSystems.getDefault().getSeparator(), "/");
+ }
return printablePath
+ ":"
diff --git a/vadl/main/vadl/vdt/impl/irregular/IrregularDecodeTreeGenerator.java b/vadl/main/vadl/vdt/impl/irregular/IrregularDecodeTreeGenerator.java
index 383431920..12ed4b368 100644
--- a/vadl/main/vadl/vdt/impl/irregular/IrregularDecodeTreeGenerator.java
+++ b/vadl/main/vadl/vdt/impl/irregular/IrregularDecodeTreeGenerator.java
@@ -26,8 +26,8 @@
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -128,7 +128,7 @@ private Node makeNode(DecodeEntries decodeEntries) {
return makeConditionNode(decodeEntries);
}
- final Map children = new HashMap<>();
+ final Map children = new LinkedHashMap<>();
for (BitPattern p : patterns.patterns()) {
final List matchingEntries = makeMatchingEntries(decodeEntries.entries(), p);
diff --git a/vadl/main/vadl/vdt/passes/VdtConstraintSynthesisPass.java b/vadl/main/vadl/vdt/passes/VdtConstraintSynthesisPass.java
index be3a9480d..436348413 100644
--- a/vadl/main/vadl/vdt/passes/VdtConstraintSynthesisPass.java
+++ b/vadl/main/vadl/vdt/passes/VdtConstraintSynthesisPass.java
@@ -22,8 +22,8 @@
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -145,7 +145,7 @@ private boolean isActualSubset(Set superset,
}
private Map> groupedInstructions(List entries) {
- final Map> result = new HashMap<>();
+ final Map> result = new LinkedHashMap<>();
for (DecodeEntry e : entries) {
var encoding = new InstructionEncoding(e, getFixedFields(e.source()));
diff --git a/vadl/main/vadl/viam/DefinitionVisitor.java b/vadl/main/vadl/viam/DefinitionVisitor.java
index 2d35941ff..cfe78f9a1 100644
--- a/vadl/main/vadl/viam/DefinitionVisitor.java
+++ b/vadl/main/vadl/viam/DefinitionVisitor.java
@@ -314,6 +314,7 @@ public void visit(MicroArchitecture microArchitecture) {
microArchitecture.ownRegisters().forEach(register -> register.accept(this));
microArchitecture.ownMemories().forEach(memory -> memory.accept(this));
microArchitecture.ownFunctions().forEach(function -> function.accept(this));
+ microArchitecture.isa().accept(this);
afterTraversal(microArchitecture);
}
diff --git a/vadl/main/vadl/viam/Logic.java b/vadl/main/vadl/viam/Logic.java
index a5492f7ea..c7d37769c 100644
--- a/vadl/main/vadl/viam/Logic.java
+++ b/vadl/main/vadl/viam/Logic.java
@@ -26,6 +26,7 @@
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import vadl.types.Type;
+import vadl.utils.Pair;
import vadl.viam.graph.Graph;
import vadl.viam.graph.dependency.ReadResourceNode;
import vadl.viam.graph.dependency.WriteRegTensorNode;
@@ -154,6 +155,8 @@ public static class Forwarding extends Logic {
private final Map enable = new HashMap<>();
+ private final Map, Signal> enableFrom = new HashMap<>();
+
public Forwarding(Identifier identifier) {
super(identifier);
}
@@ -182,6 +185,32 @@ public Signal getEnable(ReadResourceNode node) {
return enable.get(node);
}
+ /**
+ * Add a forward enable signal for a read node and a source stage to the forwarding logic.
+ *
+ * @param node read node
+ * @param stage stage forwarding from
+ * @param signal forward enable signal for read node and source stage
+ */
+ public void putEnableFrom(ReadResourceNode node, Stage stage, Signal signal) {
+ enableFrom.put(Pair.of(node, stage), signal);
+ if (!signals().contains(signal)) {
+ signals().add(signal);
+ }
+ }
+
+ /**
+ * Get the forward enable signal for a read node and a source stage.
+ *
+ * @param node read node
+ * @param stage stage forwarding from
+ * @return forward enable signal for read node and source stage
+ */
+ @Nullable
+ public Signal getEnableFrom(ReadResourceNode node, Stage stage) {
+ return enableFrom.get(Pair.of(node, stage));
+ }
+
}
/**
@@ -194,4 +223,27 @@ public BranchPrediction(Identifier identifier) {
}
}
+
+ /**
+ * Logic definition for RVFI logic for formal verification.
+ */
+ public static class RVFI extends Logic {
+
+ public RVFI(Identifier identifier) {
+ super(identifier);
+ }
+
+ /**
+ * Get signals the RVFI logic outputs.
+ *
+ * @return signals this logic writes to but not contains.
+ */
+ public List outputSignals() {
+ return behavior().getNodes(WriteSignalNode.class)
+ .map(WriteSignalNode::signal)
+ .filter(signals()::contains)
+ .toList();
+ }
+
+ }
}
diff --git a/vadl/main/vadl/viam/Specification.java b/vadl/main/vadl/viam/Specification.java
index 9001fae1c..8fd70f8c5 100644
--- a/vadl/main/vadl/viam/Specification.java
+++ b/vadl/main/vadl/viam/Specification.java
@@ -54,7 +54,8 @@ public Optional isa() {
.map(InstructionSetArchitecture.class::cast)
.findFirst();
if (isaDef.isEmpty()) {
- return processor().map(Processor::isa);
+ return processor().map(Processor::isa)
+ .or(() -> mia().map(MicroArchitecture::isa));
}
return isaDef;
}
diff --git a/vadl/main/vadl/viam/Stage.java b/vadl/main/vadl/viam/Stage.java
index 51f67f4ce..0a31cc2b2 100644
--- a/vadl/main/vadl/viam/Stage.java
+++ b/vadl/main/vadl/viam/Stage.java
@@ -72,6 +72,16 @@ public Stage(Identifier identifier, Graph behavior, List outputs) {
this.registers = new ArrayList<>();
this.localNames = new HashSet<>();
+ // TODO this should be handled by the frontend (only add stage outputs from definition)
+ // currently the passed stage output list is always empty, it could contain the list of
+ // outputs give in the spec
+ this.behavior.getNodes(WriteStageOutputNode.class)
+ .map(WriteStageOutputNode::stageOutput).forEach(output -> {
+ if (!this.outputs.contains(output)) {
+ this.outputs.add(output);
+ }
+ });
+
this.behavior.setParentDefinition(this);
}
diff --git a/vadl/main/vadl/viam/graph/dependency/MiaBuiltInCall.java b/vadl/main/vadl/viam/graph/dependency/MiaBuiltInCall.java
index 7afec8ef4..ea403658c 100644
--- a/vadl/main/vadl/viam/graph/dependency/MiaBuiltInCall.java
+++ b/vadl/main/vadl/viam/graph/dependency/MiaBuiltInCall.java
@@ -23,6 +23,7 @@
import vadl.types.Type;
import vadl.viam.Logic;
import vadl.viam.Resource;
+import vadl.viam.graph.Node;
import vadl.viam.graph.NodeList;
/**
@@ -55,6 +56,35 @@ public MiaBuiltInCall(BuiltInTable.BuiltIn builtIn, NodeList arg
ensure(BuiltInTable.MIA_BUILTINS.contains(builtIn), "Not a micro architecture builtin");
}
+ /**
+ * Create MiA builtin call.
+ *
+ * @param builtIn builtin type, must be in {@link BuiltInTable#MIA_BUILTINS}
+ * @param args arguments
+ * @param type result type
+ * @param resources resources referenced
+ * @param logic logic elements referenced
+ */
+ public MiaBuiltInCall(BuiltInTable.BuiltIn builtIn, NodeList args, Type type,
+ List resources, List logic) {
+ super(builtIn, args, type);
+ this.resources = new ArrayList<>(resources);
+ this.logic = new ArrayList<>(logic);
+ ensure(BuiltInTable.MIA_BUILTINS.contains(builtIn), "Not a micro architecture builtin");
+ }
+
+ @Override
+ public MiaBuiltInCall copy() {
+ return new MiaBuiltInCall(builtIn,
+ new NodeList<>(this.arguments().stream().map(ExpressionNode::copy).toList()),
+ this.type(), resources, logic);
+ }
+
+ @Override
+ public MiaBuiltInCall shallowCopy() {
+ return new MiaBuiltInCall(builtIn, args, type(), resources, logic);
+ }
+
@Override
protected void collectData(List