Skip to content

iss: Support rv32csr.vadl specification #223

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
May 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/handbook/iss.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,6 @@ However, it’s enough to demonstrate how to run simple C programs.

| Previous | Next |
|:--------------------------|--------------------------------:|
| [Tutorial](tutorial.html) | [LLVM Combiler Backend](lcb.md) |
| [Tutorial](tutorial.html) | [LLVM Compiler Backend](lcb.md) |

</div>
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
uint64_t vaddr, void *udata) {

qemu_plugin_mem_value last = qemu_plugin_mem_get_value(meminfo);
if (last.type != QEMU_PLUGIN_MEM_VALUE_U64) {
if (last.type != QEMU_PLUGIN_MEM_VALUE_U64 && last.type != QEMU_PLUGIN_MEM_VALUE_U32) {
return;
}

Expand All @@ -68,7 +68,7 @@ static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo,
return;
}

uint64_t val_written = last.data.u64;
uint64_t val_written = last.data.u32;
uint8_t device = val_written >> HTIF_DEV_SHIFT;
uint8_t cmd = val_written >> HTIF_CMD_SHIFT;
uint64_t payload = val_written & 0xFFFFFFFFFFFFULL;
Expand Down
75 changes: 72 additions & 3 deletions vadl/main/vadl/ast/AnnotationTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
import vadl.utils.WithLocation;
import vadl.utils.functionInterfaces.TriConsumer;
import vadl.viam.AssemblyDescription;
import vadl.viam.Constant;
import vadl.viam.MemoryRegion;
import vadl.viam.RegisterTensor;
import vadl.viam.Relocation;
import vadl.viam.annotations.AsmParserCaseSensitive;
import vadl.viam.annotations.AsmParserCommentString;
Expand Down Expand Up @@ -73,9 +75,13 @@ class AnnotationTable {
// FIXME: Apply to AST
.build();

annotationOn(RegisterDefinition.class, "zero", ExprAnnotation::new)
// FIXME: Typecheck
// FIXME: Apply to VIAM
annotationOn(RegisterDefinition.class, "zero", ZeroConstraintAnnotation::new)
.applyViam((def, annotation, lowering) -> {
var viamDef = (RegisterTensor) def;
var indices = annotation.indices.stream().map(ConstantValue::toViamConstant).toList();
var zero = Constant.Value.of(0, viamDef.resultType(indices.size()));
viamDef.addConstraint(new RegisterTensor.Constraint(indices, zero));
})
.build();

groupOn(RelocationDefinition.class)
Expand Down Expand Up @@ -1045,4 +1051,67 @@ void typeCheck(AnnotationDefinition definition, TypeChecker typeChecker) {
public String usageString() {
return "[ " + name + " : <expr> ]";
}

}

/**
* The {@code [ zero : <register>(<expr>) ]} annotation.
* This is its own class, as the typechecking is rather complex and determines
* new properties.
* <pre>{@code
* [ zero : X(0) ]
* register X: Bits<5> -> Bits<64>
* }</pre>
*/
class ZeroConstraintAnnotation extends ExprAnnotation {
@LazyInit
List<ConstantValue> indices;

@Override
void typeCheck(AnnotationDefinition definition, TypeChecker typeChecker) {
super.typeCheck(definition, typeChecker);
var def = definition.target;
if (!(node instanceof CallIndexExpr callExpr)) {
throw error("Invalid zero annotation", this)
.locationDescription(this, "Zero annotation must be of form %s.", usageString())
.build();
}
if (!(callExpr.computedTarget() == def)) {
throw error("Invalid zero annotation", callExpr.target)
.locationDescription(callExpr.target,
"Zero annotation target must be the annotated register.")
.locationNote(def, "This is the register to target.")
.build();
}


var args = callExpr.argsIndices.stream().flatMap(a -> a.values.stream()).toList();
// FIXME: Ones we have multi dimensional registers,
// we must loose this restriction, so that there can be multiple indices set.
if (args.size() != 1) {
throw error("Invalid zero annotation", callExpr)
.locationDescription(callExpr, "Exactly one register index was expected, but found %s.",
args.size())
.note("In the future it will be possible to have constraints on multiple dimensions.")
.build();
}

this.indices = args.stream().map(expr -> {
try {
return typeChecker.constantEvaluator.eval(expr);
} catch (EvaluationError e) {
throw error("Invalid zero annotation", expr)
.locationDescription(expr, "Index must be a constant expression.")
.locationNote(def, "%s", requireNonNull(e.getMessage()))
.build();
}
}).toList();

}

@Override
public String usageString() {
return "[ " + name + " : " + "<register>( <expr> ) ]";
}
}

39 changes: 23 additions & 16 deletions vadl/main/vadl/ast/BehaviorLowering.java
Original file line number Diff line number Diff line change
Expand Up @@ -205,21 +205,24 @@ Function getRegisterAliasReadFunc(AliasDefinition definition) {
graph.setSourceLocation(definition.location());
currentGraph = graph;

var identifier = viamLowering.generateIdentifier(definition.viamId, definition.loc);
var identifier = viamLowering.generateIdentifier(definition.viamId + "::read", definition.loc);
var regFileDef = (RegisterDefinition) Objects.requireNonNull(definition.computedTarget);

DataType resultType;
var indices = new NodeList<ExpressionNode>();
// Initially the indices are all fixed arguments specified in the alias definition.
// E.g. in `register alias Z = X(1)` is `1` a fixed argument.
var indices = Objects.requireNonNull(definition.computedFixedArgs).stream()
.map(this::fetch).collect(Collectors.toCollection(NodeList::new));
var params = new ArrayList<>();

// FIXME: Support pre-indexed registers, for example:
// register X = Bits<3><4><32>
// register alias Z = X(1, 2)
// register X: Bits<3><4><32>
// register alias Z = X(1)(2)
if (definition.type() instanceof ConcreteRelationType relType) {
// FIXME: Wrap input and output in casts
// FIXME: Add conditions based on annotations
var param = new vadl.viam.Parameter(
viamLowering.generateIdentifier(
identifier.name() + "::readFunc::index",
identifier.name() + "::index",
identifier.location()),
relType.argTypes().getFirst());
params.add(param);
Expand All @@ -229,6 +232,7 @@ Function getRegisterAliasReadFunc(AliasDefinition definition) {
resultType = definition.type().asDataType();
}

// FIXME: Add conditions based on annotations
var reg = (RegisterTensor) viamLowering.fetch(regFileDef).orElseThrow();
var regReadType = regFileDef.type() instanceof ConcreteRelationType relType
? relType.resultType().asDataType() : resultType.asDataType();
Expand All @@ -244,8 +248,7 @@ Function getRegisterAliasReadFunc(AliasDefinition definition) {

// FIXME: Modify based on annotations
return new Function(
viamLowering.generateIdentifier(identifier.name() + "::readFunc",
identifier.location()),
identifier,
params.toArray(vadl.viam.Parameter[]::new),
resultType,
graph
Expand All @@ -257,11 +260,14 @@ Procedure getRegisterAliasWriteProc(AliasDefinition definition) {
graph.setSourceLocation(definition.location());
currentGraph = graph;

var identifier = viamLowering.generateIdentifier(definition.viamId, definition.loc);
var identifier = viamLowering.generateIdentifier(definition.viamId + "::write", definition.loc);
var regFileDef = (RegisterDefinition) Objects.requireNonNull(definition.computedTarget);

DataType resultType;
var indices = new NodeList<ExpressionNode>();
// Initially the indices are all fixed arguments specified in the alias definition.
// E.g. in `register alias Z = X(1)` is `1` a fixed argument.
var indices = Objects.requireNonNull(definition.computedFixedArgs).stream()
.map(this::fetch).collect(Collectors.toCollection(NodeList::new));
var params = new ArrayList<>();
// FIXME: Support pre-indexed registers, for example:
// register X = Bits<3><4><32>
Expand All @@ -271,7 +277,7 @@ Procedure getRegisterAliasWriteProc(AliasDefinition definition) {
// FIXME: Add conditions based on annotations
var param = new vadl.viam.Parameter(
viamLowering.generateIdentifier(
identifier.name() + "::writeProc::index",
identifier.name() + "::index",
identifier.location()),
relType.argTypes().getFirst());
params.add(param);
Expand All @@ -283,7 +289,7 @@ Procedure getRegisterAliasWriteProc(AliasDefinition definition) {

var valueParam = new vadl.viam.Parameter(
viamLowering.generateIdentifier(
identifier.name() + "::writeProc::value",
identifier.name() + "::value",
identifier.location()),
resultType);
params.add(valueParam);
Expand All @@ -307,8 +313,7 @@ Procedure getRegisterAliasWriteProc(AliasDefinition definition) {

// FIXME: Modify based on annotations
return new Procedure(
viamLowering.generateIdentifier(identifier.name() + "::readFunc",
identifier.location()),
identifier,
params.toArray(vadl.viam.Parameter[]::new),
graph
);
Expand Down Expand Up @@ -367,6 +372,8 @@ private ExpressionNode fetch(Expr expr) {
var result = expr.accept(this);
result.setSourceLocationIfNotSet(expr.location());
expressionCache.put(expr, result);
result.ensure(!(result.type() instanceof ConstantType),
"Constant types must not exist in the VIAM");
return result;
}

Expand Down Expand Up @@ -567,8 +574,8 @@ public ExpressionNode visit(GroupedExpr expr) {

@Override
public ExpressionNode visit(IntegerLiteral expr) {
throw new RuntimeException(
"The behavior generator doesn't implement yet: " + expr.getClass().getSimpleName());
// IntegerLiteral should never be reached as it should always be substituted by the typechecker.
throw new IllegalStateException("IntegerLiteral should never be reached in the VIAM lowering.");
}

@Override
Expand Down
6 changes: 5 additions & 1 deletion vadl/main/vadl/ast/Definition.java
Original file line number Diff line number Diff line change
Expand Up @@ -2045,12 +2045,16 @@ class AliasDefinition extends Definition implements IdentifiableNode, TypedNode
@Nullable
Type type;


/**
* Set by the typechecker, the register file or register the alias points to.
*/
@Nullable
Definition computedTarget;
/**
* Set by the typechecker, the arguments used to pre-access the computedTarget.
*/
@Nullable
List<Expr> computedFixedArgs;

AliasDefinition(IdentifierOrPlaceholder id, AliasKind kind,
@Nullable TypeLiteral aliasType, @Nullable TypeLiteral targetType, Expr value,
Expand Down
6 changes: 6 additions & 0 deletions vadl/main/vadl/ast/ParserUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,12 @@ static Definition importModules(Parser parser,
var macroOverrides = new HashMap<String, String>();
for (StringLiteral arg : args) {
var keyValue = arg.value.split("=", 2);
if (keyValue.length != 2) {
throw error("Invalid import", arg)
.locationDescription(arg,
"Macro overrides must have the form `<macro-name>=<substitute>`.")
.build();
}
macroOverrides.put(keyValue[0], keyValue[1]);
}
try {
Expand Down
6 changes: 6 additions & 0 deletions vadl/main/vadl/ast/TypeChecker.java
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,12 @@ public Void visit(AliasDefinition definition) {
}

var valType = check(definition.value);
definition.computedFixedArgs = List.of();
if (definition.value instanceof CallIndexExpr callIndexExpr) {
// If the target is a call index expression, we get all fixed arguments of the
// alias register call.
definition.computedFixedArgs = AstUtils.flatArguments(callIndexExpr.args());
}

if (definition.aliasType != null) {
definition.type = check(definition.aliasType);
Expand Down
20 changes: 4 additions & 16 deletions vadl/main/vadl/ast/ViamLowering.java
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ public Optional<vadl.viam.Definition> visit(AliasDefinition definition) {

return Optional.of(new ArtificialResource(
identifier,
ArtificialResource.Kind.REG_ALIAS,
ArtificialResource.Kind.REGISTER,
innerResource,
new BehaviorLowering(this).getRegisterAliasReadFunc(definition),
new BehaviorLowering(this).getRegisterAliasWriteProc(definition)
Expand Down Expand Up @@ -778,8 +778,7 @@ public Optional<vadl.viam.Definition> visit(CounterDefinition definition) {

// FIXME: Further research for the parameters (probably don't apply to counter)
var reg = new RegisterTensor(identifier,
List.of(dimFromType(0, resultType)),
new RegisterTensor.Constraint[] {}
List.of(dimFromType(0, resultType))
);

var counter = new Counter(identifier,
Expand Down Expand Up @@ -1138,7 +1137,7 @@ private InstructionSetArchitecture visitIsa(InstructionSetDefinition definition)
.findFirst().orElse(null);
var memories = filterAndCastToInstance(allDefinitions, Memory.class);
// TODO: @flofriday compute artifical resources
var artificialResources = new ArrayList<ArtificialResource>();
var artificialResources = filterAndCastToInstance(allDefinitions, ArtificialResource.class);

// Add programCounter to registers if it is a register.
// The register list is the owner of the PC register itself.
Expand Down Expand Up @@ -1388,20 +1387,9 @@ public Optional<vadl.viam.Definition> visit(RegisterDefinition definition) {
// now we add the dimensions of the form T<d0><d1>..
dimensions.add(dimFromType(dimensions.size(), resultType));

// FIXME: Remove this and add it using the [zero: ..] annotation
var constraints = new ArrayList<RegisterTensor.Constraint>();
if (type instanceof ConcreteRelationType relType) {
var zeroConstraint = new RegisterTensor.Constraint(
List.of(Constant.Value.of(0, relType.argTypes().getFirst().asDataType())),
Constant.Value.of(0, resultType));
constraints.add(zeroConstraint);
}


var reg = new RegisterTensor(
generateIdentifier(definition.viamId, definition.identifier()),
dimensions,
constraints.toArray(new RegisterTensor.Constraint[0])
dimensions
);
return Optional.of(reg);
}
Expand Down
8 changes: 6 additions & 2 deletions vadl/main/vadl/cppCodeGen/context/CGenContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
public abstract class CGenContext<T> {

protected String prefix = "";
protected int tabSize = 3;
private boolean newLine = true;
protected final Consumer<String> writer;

Expand Down Expand Up @@ -64,15 +65,18 @@ public CGenContext(
* Enters tab mode and every following write is tabbed until {@code tabOut} is called.
*/
public CGenContext<T> spacedIn() {
prefix = " ";
prefix += " ".repeat(tabSize);
return this;
}

/**
* Exits tab mode and every following write is not tabbed anymore.
*/
public CGenContext<T> spaceOut() {
prefix = "";
if (!prefix.isEmpty()) {
prefix = prefix.substring(0, prefix.length() - tabSize);
}

return this;
}

Expand Down
18 changes: 13 additions & 5 deletions vadl/main/vadl/cppCodeGen/mixins/CDefaultMixins.java
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,10 @@ interface IfElse {
default void handle(CGenContext<Node> ctx, IfNode node) {
ctx.wr("if (")
.gen(node.condition())
.ln(") { ")
.gen(node.trueBranch())
.ln("} else {")
.gen(node.falseBranch())
.ln(") { ").spacedIn()
.gen(node.trueBranch()).spaceOut()
.ln("} else {").spacedIn()
.gen(node.falseBranch()).spaceOut()
.ln("}");
}
}
Expand Down Expand Up @@ -338,9 +338,17 @@ default void handle(CGenContext<Node> ctx, SliceNode toHandle) {

@SuppressWarnings("MissingJavadocType")
interface Select {

@Handler
@SuppressWarnings("MissingJavadocMethodCheck")
default void handle(CGenContext<Node> ctx, SelectNode toHandle) {
throw new UnsupportedOperationException("Type SelectNode not yet implemented");
ctx.wr("(")
.gen(toHandle.condition())
.wr(" ? ")
.gen(toHandle.trueCase())
.wr(": ")
.gen(toHandle.falseCase())
.wr(")");
}
}

Expand Down
Loading
Loading