Skip to content

Commit

Permalink
[ImportVerilog] Add rvalue assignment pattern support (#7428)
Browse files Browse the repository at this point in the history
Add support for assignment patterns like `'{42, 9001}` in rvalue
position. These are roughly SystemVerilog's equivalent of
`struct_create` and `array_create`. This commit also adds an
`array_create` op to support assignment patterns for arrays.
  • Loading branch information
fabianschuiki authored Aug 4, 2024
1 parent 17e85f1 commit e95e92d
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 0 deletions.
14 changes: 14 additions & 0 deletions include/circt/Dialect/Moore/MooreOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1037,6 +1037,20 @@ def DynExtractRefOp : MooreOp<"dyn_extract_ref", [Pure]> {
}];
}

//===----------------------------------------------------------------------===//
// Array Manipulation
//===----------------------------------------------------------------------===//

def ArrayCreateOp : MooreOp<"array_create", [Pure, SameTypeOperands]> {
let summary = "Create an array value from individual elements";
let arguments = (ins Variadic<UnpackedType>:$elements);
let results = (outs AnyStaticArrayType:$result);
let assemblyFormat = [{
$elements attr-dict `:` type($elements) `->` type($result)
}];
let hasVerifier = 1;
}

//===----------------------------------------------------------------------===//
// Struct Manipulation
//===----------------------------------------------------------------------===//
Expand Down
6 changes: 6 additions & 0 deletions include/circt/Dialect/Moore/MooreTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,12 @@ def BitType : MooreType<CPred<[{
}];
}

/// A packed or unpacked array type with a fixed size.
def AnyStaticArrayType : MooreType<
Or<[ArrayType.predicate, UnpackedArrayType.predicate]>,
"packed or unpacked static array type",
"moore::UnpackedType">;

/// A packed or unpacked struct type.
def AnyStructType : MooreType<
Or<[StructType.predicate, UnpackedStructType.predicate]>,
Expand Down
64 changes: 64 additions & 0 deletions lib/Conversion/ImportVerilog/Expressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,70 @@ struct RvalueExprVisitor {
return builder.create<moore::StringConstantOp>(loc, type, expr.getValue());
}

/// Handle assignment patterns.
Value visitAssignmentPattern(
const slang::ast::AssignmentPatternExpressionBase &expr,
unsigned replCount = 1) {
auto type = context.convertType(*expr.type);

// Convert the individual elements first.
auto elementCount = expr.elements().size();
SmallVector<Value> elements;
elements.reserve(replCount * elementCount);
for (auto elementExpr : expr.elements()) {
auto value = context.convertRvalueExpression(*elementExpr);
if (!value)
return {};
elements.push_back(value);
}
for (unsigned replIdx = 1; replIdx < replCount; ++replIdx)
for (unsigned elementIdx = 0; elementIdx < elementCount; ++elementIdx)
elements.push_back(elements[elementIdx]);

// Handle packed structs.
if (auto structType = dyn_cast<moore::StructType>(type)) {
assert(structType.getMembers().size() == elements.size());
return builder.create<moore::StructCreateOp>(loc, structType, elements);
}

// Handle unpacked structs.
if (auto structType = dyn_cast<moore::UnpackedStructType>(type)) {
assert(structType.getMembers().size() == elements.size());
return builder.create<moore::StructCreateOp>(loc, structType, elements);
}

// Handle packed arrays.
if (auto arrayType = dyn_cast<moore::ArrayType>(type)) {
assert(arrayType.getSize() == elements.size());
return builder.create<moore::ArrayCreateOp>(loc, arrayType, elements);
}

// Handle unpacked arrays.
if (auto arrayType = dyn_cast<moore::UnpackedArrayType>(type)) {
assert(arrayType.getSize() == elements.size());
return builder.create<moore::ArrayCreateOp>(loc, arrayType, elements);
}

mlir::emitError(loc) << "unsupported assignment pattern with type " << type;
return {};
}

Value visit(const slang::ast::SimpleAssignmentPatternExpression &expr) {
return visitAssignmentPattern(expr);
}

Value visit(const slang::ast::StructuredAssignmentPatternExpression &expr) {
return visitAssignmentPattern(expr);
}

Value visit(const slang::ast::ReplicatedAssignmentPatternExpression &expr) {
slang::ast::EvalContext evalContext(context.compilation,
slang::ast::EvalFlags::CacheResults);
auto count = expr.count().eval(evalContext).integer().as<unsigned>();
assert(count && "Slang guarantees constant non-zero replication count");
return visitAssignmentPattern(expr, *count);
}

/// Emit an error for all other expressions.
template <typename T>
Value visit(T &&node) {
Expand Down
33 changes: 33 additions & 0 deletions lib/Dialect/Moore/MooreOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,39 @@ LogicalResult ConcatRefOp::inferReturnTypes(
return success();
}

//===----------------------------------------------------------------------===//
// ArrayCreateOp
//===----------------------------------------------------------------------===//

static std::pair<unsigned, UnpackedType> getArrayElements(Type type) {
if (auto arrayType = dyn_cast<ArrayType>(type))
return {arrayType.getSize(), arrayType.getElementType()};
if (auto arrayType = dyn_cast<UnpackedArrayType>(type))
return {arrayType.getSize(), arrayType.getElementType()};
assert(0 && "expected ArrayType or UnpackedArrayType");
return {};
}

LogicalResult ArrayCreateOp::verify() {
auto [size, elementType] = getArrayElements(getType());

// Check that the number of operands matches the array size.
if (getElements().size() != size)
return emitOpError() << "has " << getElements().size()
<< " operands, but result type requires " << size;

// Check that the operand types match the array element type. We only need to
// check one of the operands, since the `SameTypeOperands` trait ensures all
// operands have the same type.
if (size > 0) {
auto value = getElements()[0];
if (value.getType() != elementType)
return emitOpError() << "operands have type " << value.getType()
<< ", but array requires " << elementType;
}
return success();
}

//===----------------------------------------------------------------------===//
// StructCreateOp
//===----------------------------------------------------------------------===//
Expand Down
66 changes: 66 additions & 0 deletions test/Conversion/ImportVerilog/basic.sv
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,10 @@ module Expressions;
struct packed {
int a, b;
} struct0;
// CHECK: %ustruct0 = moore.variable : <ustruct<{a: i32, b: i32}>>
struct {
int a, b;
} ustruct0;
// CHECK: %struct1 = moore.variable : <struct<{c: struct<{a: i32, b: i32}>, d: struct<{a: i32, b: i32}>}>>
struct packed {
struct packed {
Expand All @@ -512,6 +516,10 @@ module Expressions;
// CHECK: %r1 = moore.variable : <real>
// CHECK: %r2 = moore.variable : <real>
real r1,r2;
// CHECK: %arrayInt = moore.variable : <array<2 x i32>>
bit [1:0][31:0] arrayInt;
// CHECK: %uarrayInt = moore.variable : <uarray<2 x i32>>
bit [31:0] uarrayInt [2];

initial begin
// CHECK: moore.constant 0 : i32
Expand Down Expand Up @@ -1058,6 +1066,64 @@ module Expressions;
// CHECK: [[TMP2:%.+]] = moore.struct_extract [[TMP1]], "b" : struct<{a: i32, b: i32}> -> i32
// CHECK: moore.blocking_assign %b, [[TMP2]]
b = struct0.b;

//===------------------------------------------------------------------===//
// Assignment Patterns

// CHECK: [[TMP0:%.+]] = moore.constant 17
// CHECK: [[TMP1:%.+]] = moore.constant 17
// CHECK: moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
struct0 = '{default: 17};

// CHECK: [[TMP0:%.+]] = moore.constant 1337
// CHECK: [[TMP1:%.+]] = moore.constant 1337
// CHECK: moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
struct0 = '{int: 1337};

// CHECK: [[TMP0:%.+]] = moore.constant 420
// CHECK: moore.struct_create [[TMP0]], [[TMP0]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
struct0 = '{2{420}};

// CHECK: [[TMP0:%.+]] = moore.constant 42
// CHECK: [[TMP1:%.+]] = moore.constant 9001
// CHECK: moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
struct0 = '{a: 42, b: 9001};

// CHECK: [[TMP0:%.+]] = moore.constant 43
// CHECK: [[TMP1:%.+]] = moore.constant 9002
// CHECK: moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
struct0 = '{43, 9002};

// CHECK: [[TMP0:%.+]] = moore.constant 44
// CHECK: [[TMP1:%.+]] = moore.constant 9003
// CHECK: moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> ustruct<{a: i32, b: i32}>
ustruct0 = '{44, 9003};

// CHECK: [[TMP0:%.+]] = moore.constant 1
// CHECK: [[TMP1:%.+]] = moore.constant 2
// CHECK: [[TMP2:%.+]] = moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
// CHECK: [[TMP0:%.+]] = moore.constant 3
// CHECK: [[TMP1:%.+]] = moore.constant 4
// CHECK: [[TMP3:%.+]] = moore.struct_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
// CHECK: moore.struct_create [[TMP2]], [[TMP3]] : !moore.struct<{a: i32, b: i32}>, !moore.struct<{a: i32, b: i32}> -> struct<{c: struct<{a: i32, b: i32}>, d: struct<{a: i32, b: i32}>}>
struct1 = '{c: '{a: 1, b: 2}, d: '{a: 3, b: 4}};

// CHECK: [[TMP0:%.+]] = moore.constant 42
// CHECK: [[TMP1:%.+]] = moore.constant 9001
// CHECK: moore.array_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> array<2 x i32>
arrayInt = '{42, 9001};

// CHECK: [[TMP0:%.+]] = moore.constant 43
// CHECK: [[TMP1:%.+]] = moore.constant 9002
// CHECK: moore.array_create [[TMP0]], [[TMP1]] : !moore.i32, !moore.i32 -> uarray<2 x i32>
uarrayInt = '{43, 9002};

// CHECK: [[TMP0:%.+]] = moore.constant 1
// CHECK: [[TMP1:%.+]] = moore.constant 2
// CHECK: [[TMP2:%.+]] = moore.constant 3
// CHECK: [[TMP3:%.+]] = moore.array_create [[TMP0]], [[TMP1]], [[TMP2]], [[TMP0]], [[TMP1]], [[TMP2]] : !moore.i4, !moore.i4, !moore.i4, !moore.i4, !moore.i4, !moore.i4 -> uarray<6 x i4>
// CHECK: moore.array_create [[TMP3]], [[TMP3]], [[TMP3]] : !moore.uarray<6 x i4>, !moore.uarray<6 x i4>, !moore.uarray<6 x i4> -> uarray<3 x uarray<6 x i4>>
arr = '{3{'{2{4'd1, 4'd2, 4'd3}}}};

//===------------------------------------------------------------------===//
// Builtin Functions
Expand Down
3 changes: 3 additions & 0 deletions test/Dialect/Moore/basic.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ moore.module @Expressions(
moore.yield %b : i32
}

// CHECK: moore.array_create [[A]], [[B]] : !moore.i32, !moore.i32 -> array<2 x i32>
moore.array_create %a, %b : !moore.i32, !moore.i32 -> array<2 x i32>

// CHECK: moore.struct_create [[A]], [[B]] : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
moore.struct_create %a, %b : !moore.i32, !moore.i32 -> struct<{a: i32, b: i32}>
// CHECK: moore.struct_extract [[STRUCT1]], "a" : struct<{a: i32, b: i32}> -> i32
Expand Down

0 comments on commit e95e92d

Please sign in to comment.