From dc81bac3cb9325e2040228f67e604f1642816c3e Mon Sep 17 00:00:00 2001 From: Rich McKeever Date: Wed, 15 May 2024 20:15:30 -0700 Subject: [PATCH] Add support in VAST for various SystemVerilog constructs. - Typedefs - Enums - Structs - Arrays with a specified element type - Arrays with an expression that is the max rather than the width - Return statements - More complete parameter and argument declarations - Literals that explicitly declare width and signedness - Power operator - Remove the prohibition on DataType::Emit(), since it now needs to be emitted in contexts without an identifier. PiperOrigin-RevId: 634178542 --- xls/codegen/BUILD | 1 + xls/codegen/vast.cc | 251 +++++++++++++++++++++-------- xls/codegen/vast.h | 338 ++++++++++++++++++++++++++++++++++----- xls/codegen/vast_test.cc | 204 +++++++++++++++++++---- 4 files changed, 656 insertions(+), 138 deletions(-) diff --git a/xls/codegen/BUILD b/xls/codegen/BUILD index 45c8817d0a..b910382edb 100644 --- a/xls/codegen/BUILD +++ b/xls/codegen/BUILD @@ -169,6 +169,7 @@ cc_test( "//xls/common:xls_gunit_main", "//xls/common/status:matchers", "//xls/ir:bits", + "//xls/ir:format_preference", "//xls/ir:number_parser", "//xls/ir:source_location", "@com_google_absl//absl/status", diff --git a/xls/codegen/vast.cc b/xls/codegen/vast.cc index 6f0e9a3b4a..e63e3b1929 100644 --- a/xls/codegen/vast.cc +++ b/xls/codegen/vast.cc @@ -75,6 +75,31 @@ void LineInfoIncrease(LineInfo* line_info, int64_t delta) { } } +// Converts a `DataKind` to its SystemVerilog name, if any. Emitting a data type +// in most contexts requires the containing entity to emit both the `DataKind` +// and the `DataType`, at least one of which should emit as nonempty. +std::string DataKindToString(DataKind kind) { + switch (kind) { + case DataKind::kReg: + return "reg"; + case DataKind::kWire: + return "wire"; + case DataKind::kLogic: + return "logic"; + case DataKind::kInteger: + return "integer"; + default: + // For any other type, the `DataType->Emit()` output is sufficient. + return ""; + } +} + +std::string EmitNothing(const VastNode* node, LineInfo* line_info) { + LineInfoStart(line_info, node); + LineInfoEnd(line_info, node); + return ""; +} + } // namespace std::string PartialLineSpans::ToString() const { @@ -151,6 +176,16 @@ std::string ToString(Direction direction) { } } +std::string ScalarType::Emit(LineInfo* line_info) const { + // The `DataKind` preceding the type is enough. + return EmitNothing(this, line_info); +} + +std::string IntegerType::Emit(LineInfo* line_info) const { + // The `DataKind` preceding the type is enough. + return EmitNothing(this, line_info); +} + std::string MacroRef::Emit(LineInfo* line_info) const { LineInfoStart(line_info, this); LineInfoIncrease(line_info, NumberOfNewlines(name_)); @@ -229,8 +264,7 @@ std::string VerilogFile::Emit(LineInfo* line_info) const { return out; } -LocalParamItemRef* LocalParam::AddItem(std::string_view name, - Expression* value, +LocalParamItemRef* LocalParam::AddItem(std::string_view name, Expression* value, const SourceInfo& loc) { items_.push_back(file()->Make(loc, name, value)); return file()->Make(loc, items_.back()); @@ -308,6 +342,11 @@ LogicRef* VerilogFunction::AddArgument(std::string_view name, DataType* type, return file()->Make(loc, argument_defs_.back()); } +LogicRef* VerilogFunction::AddArgument(Def* def, const SourceInfo& loc) { + argument_defs_.push_back(def); + return file()->Make(loc, argument_defs_.back()); +} + LogicRef* VerilogFunction::return_value_ref() { return file()->Make(return_value_def_->loc(), return_value_def_); } @@ -317,7 +356,7 @@ std::string VerilogFunction::Emit(LineInfo* line_info) const { std::string return_type = return_value_def_->data_type()->EmitWithIdentifier(line_info, name()); std::string parameters = - absl::StrJoin(argument_defs_, ", ", [=](std::string* out, RegDef* d) { + absl::StrJoin(argument_defs_, ", ", [=](std::string* out, Def* d) { absl::StrAppend(out, "input ", d->EmitNoSemi(line_info)); }); LineInfoIncrease(line_info, 1); @@ -405,6 +444,16 @@ ParameterRef* Module::AddParameter(std::string_view name, Expression* rhs, return file()->Make(loc, param); } +ParameterRef* Module::AddParameter(Def* def, Expression* rhs, + const SourceInfo& loc) { + Parameter* param = AddModuleMember(file()->Make(loc, def, rhs)); + return file()->Make(loc, param); +} + +Typedef* Module::AddTypedef(Def* def, const SourceInfo& loc) { + return AddModuleMember(file()->Make(loc, def)); +} + Literal* Expression::AsLiteralOrDie() { CHECK(IsLiteral()); return static_cast(this); @@ -470,69 +519,57 @@ static std::string WidthToLimit(LineInfo* line_info, Expression* expr) { return width_minus_one->Emit(line_info); } -std::string ScalarType::EmitWithIdentifier(LineInfo* line_info, - std::string_view identifier) const { - LineInfoStart(line_info, this); - LineInfoEnd(line_info, this); - return absl::StrFormat(" %s", identifier); -} - -std::string IntegerType::EmitWithIdentifier(LineInfo* line_info, - std::string_view identifier) const { - LineInfoStart(line_info, this); - LineInfoEnd(line_info, this); - return absl::StrFormat(" %s", identifier); -} - BitVectorType::BitVectorType(int64_t width, bool is_signed, VerilogFile* file, const SourceInfo& loc) : DataType(file, loc), - width_(file->PlainLiteral(static_cast(width), loc)), + size_expr_(file->PlainLiteral(static_cast(width), loc)), is_signed_(is_signed) {} absl::StatusOr BitVectorType::WidthAsInt64() const { - if (!width_->IsLiteral()) { + if (!size_expr_->IsLiteral() || size_expr_is_max_) { return absl::FailedPreconditionError("Width is not a literal: " + - width_->Emit(nullptr)); + size_expr_->Emit(nullptr)); } - return width_->AsLiteralOrDie()->bits().ToUint64(); + return size_expr_->AsLiteralOrDie()->bits().ToUint64(); } absl::StatusOr BitVectorType::FlatBitCountAsInt64() const { return WidthAsInt64(); } -std::string BitVectorType::EmitWithIdentifier( - LineInfo* line_info, std::string_view identifier) const { +std::string BitVectorType::Emit(LineInfo* line_info) const { LineInfoStart(line_info, this); - std::string result = absl::StrFormat("%s [%s:0]", is_signed_ ? " signed" : "", - WidthToLimit(line_info, width_)); - absl::StrAppend(&result, " ", identifier); + std::string result = + absl::StrFormat("%s [%s:0]", is_signed_ ? " signed" : "", + size_expr_is_max_ ? size_expr_->Emit(line_info) + : WidthToLimit(line_info, size_expr_)); LineInfoEnd(line_info, this); return result; } +PackedArrayType::PackedArrayType(Expression* width, + absl::Span packed_dims, + bool is_signed, VerilogFile* file, + const SourceInfo& loc) + : DataType(file, loc), + element_type_(file->Make(loc, width, is_signed)), + packed_dims_(packed_dims.begin(), packed_dims.end()) { + CHECK(!packed_dims.empty()); +} + PackedArrayType::PackedArrayType(int64_t width, absl::Span packed_dims, bool is_signed, VerilogFile* file, const SourceInfo& loc) : DataType(file, loc), - width_(file->PlainLiteral(static_cast(width), loc)), - is_signed_(is_signed) { + element_type_(file->Make(loc, static_cast(width), + is_signed)) { CHECK(!packed_dims.empty()); for (int64_t dim : packed_dims) { packed_dims_.push_back(file->PlainLiteral(static_cast(dim), loc)); } } -absl::StatusOr PackedArrayType::WidthAsInt64() const { - if (!width_->IsLiteral()) { - return absl::FailedPreconditionError("Width is not a literal: " + - width_->Emit(nullptr)); - } - return width_->AsLiteralOrDie()->bits().ToUint64(); -} - absl::StatusOr PackedArrayType::FlatBitCountAsInt64() const { XLS_ASSIGN_OR_RETURN(int64_t bit_count, WidthAsInt64()); for (Expression* dim : packed_dims()) { @@ -547,15 +584,19 @@ absl::StatusOr PackedArrayType::FlatBitCountAsInt64() const { return bit_count; } -std::string PackedArrayType::EmitWithIdentifier( - LineInfo* line_info, std::string_view identifier) const { +std::string PackedArrayType::Emit(LineInfo* line_info) const { LineInfoStart(line_info, this); - std::string result = is_signed_ ? " signed" : ""; - absl::StrAppendFormat(&result, " [%s:0]", WidthToLimit(line_info, width_)); + std::string result = element_type_->Emit(line_info); + if (element_type_->IsUserDefined()) { + // Imitate the space that a bit vector emits between the kind and innermost + // dimension. + absl::StrAppend(&result, " "); + } for (Expression* dim : packed_dims()) { - absl::StrAppendFormat(&result, "[%s:0]", WidthToLimit(line_info, dim)); + absl::StrAppendFormat( + &result, "[%s:0]", + dims_are_max_ ? dim->Emit(line_info) : WidthToLimit(line_info, dim)); } - absl::StrAppend(&result, " ", identifier); LineInfoEnd(line_info, this); return result; } @@ -613,21 +654,7 @@ std::string Def::Emit(LineInfo* line_info) const { std::string Def::EmitNoSemi(LineInfo* line_info) const { LineInfoStart(line_info, this); - std::string kind_str; - switch (data_kind()) { - case DataKind::kReg: - kind_str = "reg"; - break; - case DataKind::kWire: - kind_str = "wire"; - break; - case DataKind::kLogic: - kind_str = "logic"; - break; - case DataKind::kInteger: - kind_str = "integer"; - break; - } + std::string kind_str = DataKindToString(data_kind()); std::string result = absl::StrCat( kind_str, data_type()->EmitWithIdentifier(line_info, GetName())); LineInfoEnd(line_info, this); @@ -661,6 +688,15 @@ std::string LogicDef::Emit(LineInfo* line_info) const { return result; } +std::string UserDef::Emit(LineInfo* line_info) const { + std::string result = Def::EmitNoSemi(line_info); + if (init_ != nullptr) { + absl::StrAppend(&result, " = ", init_->Emit(line_info)); + } + absl::StrAppend(&result, ";"); + return result; +} + IntegerDef::IntegerDef(std::string_view name, VerilogFile* file, const SourceInfo& loc) : Def(name, DataKind::kInteger, file->IntegerType(loc), file, loc), @@ -689,6 +725,8 @@ std::string EmitModuleMember(LineInfo* line_info, const ModuleMember& member) { Visitor{[=](Def* d) { return d->Emit(line_info); }, [=](LocalParam* p) { return p->Emit(line_info); }, [=](Parameter* p) { return p->Emit(line_info); }, + [=](Typedef* d) { return d->Emit(line_info); }, + [=](Enum* e) { return e->Emit(line_info); }, [=](Instantiation* i) { return i->Emit(line_info); }, [=](ContinuousAssignment* c) { return c->Emit(line_info); }, [=](Comment* c) { return c->Emit(line_info); }, @@ -864,7 +902,7 @@ std::string Literal::Emit(LineInfo* line_info) const { if (format_ == FormatPreference::kUnsignedDecimal) { std::string prefix; if (emit_bit_count_) { - prefix = absl::StrFormat("%d'd", bits_.bit_count()); + prefix = absl::StrFormat("%d'd", effective_bit_count_); } return absl::StrFormat( "%s%s", prefix, @@ -872,13 +910,15 @@ std::string Literal::Emit(LineInfo* line_info) const { } if (format_ == FormatPreference::kBinary) { return absl::StrFormat( - "%d'b%s", bits_.bit_count(), + "%d'b%s", effective_bit_count_, BitsToRawDigits(bits_, format_, /*emit_leading_zeros=*/true)); } CHECK_EQ(format_, FormatPreference::kHex); - return absl::StrFormat("%d'h%s", bits_.bit_count(), - BitsToRawDigits(bits_, FormatPreference::kHex, - /*emit_leading_zeros=*/true)); + const std::string raw_digits = BitsToRawDigits(bits_, FormatPreference::kHex, + /*emit_leading_zeros=*/true); + return declared_as_signed_ + ? absl::StrFormat("%d'sh%s", effective_bit_count_, raw_digits) + : absl::StrFormat("%d'h%s", effective_bit_count_, raw_digits); } bool Literal::IsLiteralWithValue(int64_t target) const { @@ -977,8 +1017,84 @@ std::string Ternary::Emit(LineInfo* line_info) const { std::string Parameter::Emit(LineInfo* line_info) const { LineInfoStart(line_info, this); LineInfoIncrease(line_info, NumberOfNewlines(name_)); - std::string result = - absl::StrFormat("parameter %s = %s;", name_, rhs_->Emit(line_info)); + std::string result = absl::StrFormat( + "parameter %s = %s;", def_ ? def_->EmitNoSemi(line_info) : name_, + rhs_->Emit(line_info)); + LineInfoEnd(line_info, this); + return result; +} + +std::string Typedef::Emit(LineInfo* line_info) const { + LineInfoStart(line_info, this); + LineInfoIncrease(line_info, NumberOfNewlines(def_->GetName())); + std::string result = absl::StrFormat("typedef %s", def_->Emit(line_info)); + LineInfoEnd(line_info, this); + return result; +} + +std::string TypedefType::Emit(LineInfo* line_info) const { + LineInfoStart(line_info, this); + std::string result = type_def_->GetName(); + LineInfoEnd(line_info, this); + return result; +} + +std::string Enum::Emit(LineInfo* line_info) const { + LineInfoStart(line_info, this); + std::string result = "enum {\n"; + if (kind_ != DataKind::kUntypedEnum) { + result = absl::StrFormat("enum %s%s {\n", DataKindToString(kind_), + BaseType()->Emit(line_info)); + } + LineInfoIncrease(line_info, 1); + for (int i = 0; i < members_.size(); i++) { + LineInfoIncrease(line_info, 1); + std::string member_str = members_[i]->Emit(line_info); + if (i == members_.size() - 1) { + absl::StrAppend(&member_str, "\n"); + } else { + absl::StrAppend(&member_str, ",\n"); + } + absl::StrAppend(&result, Indent(member_str)); + } + absl::StrAppend(&result, "}"); + LineInfoEnd(line_info, this); + return result; +} + +EnumMemberRef* Enum::AddMember(std::string_view name, Expression* rhs, + const SourceInfo& loc) { + members_.push_back(file()->Make(loc, name, rhs)); + return file()->Make(loc, members_.back()); +} + +std::string EnumMember::Emit(LineInfo* line_info) const { + LineInfoStart(line_info, this); + std::string result = absl::StrFormat("%s = %s", name_, rhs_->Emit(line_info)); + LineInfoEnd(line_info, this); + return result; +} + +absl::StatusOr Struct::FlatBitCountAsInt64() const { + int64_t result = 0; + for (const Def* next : members_) { + XLS_ASSIGN_OR_RETURN(int64_t def_bit_count, + next->data_type()->FlatBitCountAsInt64()); + result += def_bit_count; + } + return result; +} + +std::string Struct::Emit(LineInfo* line_info) const { + LineInfoStart(line_info, this); + std::string result = "struct packed {\n"; + LineInfoIncrease(line_info, 1); + for (const Def* next : members_) { + LineInfoIncrease(line_info, 1); + absl::StrAppend(&result, Indent(next->Emit(line_info)), "\n"); + } + absl::StrAppend(&result, "}"); + LineInfoIncrease(line_info, 1); LineInfoEnd(line_info, this); return result; } @@ -1262,6 +1378,13 @@ std::string NonblockingAssignment::Emit(LineInfo* line_info) const { return absl::StrFormat("%s <= %s;", lhs, rhs); } +std::string ReturnStatement::Emit(LineInfo* line_info) const { + LineInfoStart(line_info, this); + std::string expr = expr_->Emit(line_info); + LineInfoEnd(line_info, this); + return absl::StrFormat("return %s;", expr); +} + StructuredProcedure::StructuredProcedure(VerilogFile* file, const SourceInfo& loc) : VastNode(file, loc), statements_(file->Make(loc)) {} diff --git a/xls/codegen/vast.h b/xls/codegen/vast.h index 63ae96de89..7f6c6a0166 100644 --- a/xls/codegen/vast.h +++ b/xls/codegen/vast.h @@ -37,6 +37,7 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "absl/types/span.h" #include "xls/codegen/module_signature.pb.h" #include "xls/ir/bits.h" @@ -197,6 +198,10 @@ class DataType : public VastNode { // Returns whether this is a scalar signal type (for example, "wire foo"). virtual bool IsScalar() const { return false; } + // Returns whether this type represents to a typedef, struct, enum, or an + // array of a user-defined type. + virtual bool IsUserDefined() const { return false; } + // Returns the width of the def (not counting packed or unpacked dimensions) // as an int64_t. Returns an error if this is not possible because the width // is not a literal. For example, the width of the following def is 8: @@ -217,10 +222,6 @@ class DataType : public VastNode { virtual bool is_signed() const { return false; } - std::string Emit(LineInfo* line_info) const override { - LOG(FATAL) << "EmitWithIdentifier should be called rather than emit"; - } - // Returns a string which denotes this type along with an identifier for use // in definitions, arguments, etc. Example output if identifier is 'foo': // @@ -231,7 +232,9 @@ class DataType : public VastNode { // This method is required rather than simply Emit because an identifier // string is nested within the string describing the type. virtual std::string EmitWithIdentifier(LineInfo* line_info, - std::string_view identifier) const = 0; + std::string_view identifier) const { + return absl::StrFormat("%s %s", Emit(line_info), identifier); + } }; // Represents a scalar type. Example: @@ -244,8 +247,7 @@ class ScalarType : public DataType { absl::StatusOr WidthAsInt64() const override { return 1; } absl::StatusOr FlatBitCountAsInt64() const override { return 1; } std::optional width() const override { return std::nullopt; } - std::string EmitWithIdentifier(LineInfo* line_info, - std::string_view identifier) const override; + std::string Emit(LineInfo* line_info) const override; }; // Represents an integer type. Example: @@ -263,30 +265,42 @@ class IntegerType : public DataType { "Cannot get flat bit count of integer types"); } std::optional width() const override { return std::nullopt; } - std::string EmitWithIdentifier(LineInfo* line_info, - std::string_view identifier) const override; + std::string Emit(LineInfo* line_info) const override; }; // Represents a bit-vector type. Example: // reg[7:0] foo; class BitVectorType : public DataType { public: - BitVectorType(Expression* width, bool is_signed, VerilogFile* file, + BitVectorType(Expression* size_expr, bool is_signed, bool size_expr_is_max, + VerilogFile* file, const SourceInfo& loc) + : DataType(file, loc), + size_expr_(size_expr), + size_expr_is_max_(size_expr_is_max), + is_signed_(is_signed) {} + + BitVectorType(Expression* size_expr, bool is_signed, VerilogFile* file, const SourceInfo& loc) - : DataType(file, loc), width_(width), is_signed_(is_signed) {} + : BitVectorType(size_expr, is_signed, /*size_expr_is_max=*/false, file, + loc) {} BitVectorType(int64_t width, bool is_signed, VerilogFile* file, const SourceInfo& loc); bool IsScalar() const override { return false; } absl::StatusOr WidthAsInt64() const override; absl::StatusOr FlatBitCountAsInt64() const override; - std::optional width() const override { return width_; } + std::optional width() const override { + return size_expr_is_max_ ? nullptr : size_expr_; + } bool is_signed() const override { return is_signed_; } - std::string EmitWithIdentifier(LineInfo* line_info, - std::string_view identifier) const override; + std::string Emit(LineInfo* line_info) const override; private: - Expression* width_; + Expression* size_expr_; + // Whether the `size_expr_` represents the max index as opposed to the width. + // Currently this is only true for vectors originating from SystemVerilog + // source code. + bool size_expr_is_max_ = false; bool is_signed_; }; @@ -295,23 +309,37 @@ class BitVectorType : public DataType { class PackedArrayType : public DataType { public: PackedArrayType(Expression* width, absl::Span packed_dims, - bool is_signed, VerilogFile* file, const SourceInfo& loc) + bool is_signed, VerilogFile* file, const SourceInfo& loc); + + PackedArrayType(int64_t width, absl::Span packed_dims, + bool is_signed, VerilogFile* file, const SourceInfo& loc); + + PackedArrayType(DataType* element_type, + absl::Span packed_dims, bool dims_are_max, + VerilogFile* file, const SourceInfo& loc) : DataType(file, loc), - width_(width), - is_signed_(is_signed), - packed_dims_(packed_dims.begin(), packed_dims.end()) { + element_type_(element_type), + packed_dims_(packed_dims.begin(), packed_dims.end()), + dims_are_max_(dims_are_max) { CHECK(!packed_dims.empty()); } - PackedArrayType(int64_t width, absl::Span packed_dims, - bool is_signed, VerilogFile* file, const SourceInfo& loc); bool IsScalar() const override { return false; } - absl::StatusOr WidthAsInt64() const override; + + bool IsUserDefined() const override { return element_type_->IsUserDefined(); } + + absl::StatusOr WidthAsInt64() const override { + return element_type_->WidthAsInt64(); + } + absl::StatusOr FlatBitCountAsInt64() const override; - std::optional width() const override { return width_; } - bool is_signed() const override { return is_signed_; } - std::string EmitWithIdentifier(LineInfo* line_info, - std::string_view identifier) const override; + + std::optional width() const override { + return element_type_->width(); + } + + bool is_signed() const override { return element_type_->is_signed(); } + std::string Emit(LineInfo* line_info) const override; // Returns the packed dimensions for the type. For example, the net type for // "wire [7:0][42:0][3:0] foo;" has {43, 4} as the packed dimensions. The @@ -322,9 +350,12 @@ class PackedArrayType : public DataType { absl::Span packed_dims() const { return packed_dims_; } private: - Expression* width_; - bool is_signed_; + DataType* element_type_; std::vector packed_dims_; + // Whether the `packed_dims_` represent max indices as opposed to widths. + // Currently this is only the case in arrays originating from SystemVerilog + // source code. + bool dims_are_max_ = false; }; // Represents an unpacked array of bit-vectors or packed array types. Example: @@ -346,12 +377,18 @@ class UnpackedArrayType : public DataType { const SourceInfo& loc); bool IsScalar() const override { return false; } + bool IsUserDefined() const override { return element_type_->IsUserDefined(); } absl::StatusOr WidthAsInt64() const override; absl::StatusOr FlatBitCountAsInt64() const override; std::optional width() const override { return element_type_->width(); } bool is_signed() const override { return element_type_->is_signed(); } + + std::string Emit(LineInfo* line_info) const override { + LOG(FATAL) << "EmitWithIdentifier should be called rather than emit"; + } + std::string EmitWithIdentifier(LineInfo* line_info, std::string_view identifier) const override; @@ -367,7 +404,17 @@ class UnpackedArrayType : public DataType { // The kind of a net/variable. kReg, kWire, kLogic can be arbitrarily // typed. kInteger definitions can only be of IntegerType. -enum class DataKind : int8_t { kReg, kWire, kLogic, kInteger }; +enum class DataKind : int8_t { + kReg, + kWire, + kLogic, + kInteger, + // Any user-defined type, such as a typedef, struct, or enum. + kUser, + // The data kind of an enum definition itself that has no specified kind, i.e. + // "enum { elements }" as opposed to "enum int { elements }" or similar. + kUntypedEnum +}; // Represents the definition of a variable or net. class Def : public Statement { @@ -381,7 +428,7 @@ class Def : public Statement { : Statement(file, loc), name_(name), data_kind_(data_kind), - data_type_(std::move(data_type)) {} + data_type_(data_type) {} std::string Emit(LineInfo* line_info) const override; @@ -448,6 +495,23 @@ class LogicDef : public Def { Expression* init_; }; +// Variable definition with a type that is a user-defined name. Example: +// foo_t [41:0] foo; +class UserDef : public Def { + public: + UserDef(std::string_view name, DataType* data_type, VerilogFile* file, + const SourceInfo& loc) + : Def(name, DataKind::kUser, data_type, file, loc), init_(nullptr) {} + UserDef(std::string_view name, DataType* data_type, Expression* init, + VerilogFile* file, const SourceInfo& loc) + : Def(name, DataKind::kUser, data_type, file, loc), init_(init) {} + + std::string Emit(LineInfo* line_info) const override; + + protected: + Expression* init_; +}; + // Integer variable definition.Example: // integer foo; class IntegerDef : public Def { @@ -539,6 +603,21 @@ class NonblockingAssignment : public Statement { Expression* rhs_; }; +// Represents an explicit SystemVerilog function return statement. The +// alternative construct for this is an assignment to the function name. +// Currently an explicit return statement is only modeled in VAST trees coming +// from parsed SystemVerilog. +class ReturnStatement : public Statement { + public: + ReturnStatement(Expression* expr, VerilogFile* file, const SourceInfo& loc) + : Statement(file, loc), expr_(expr) {} + + std::string Emit(LineInfo* line_info) const override; + + private: + Expression* expr_; +}; + // An abstraction representing a sequence of statements within a structured // procedure (e.g., an "always" statement). class StatementBlock : public VastNode { @@ -876,9 +955,13 @@ class MacroRef : public Expression { std::string name_; }; -// Defines a module parameter. +// Defines a module parameter. A parameter must be assigned to an expression, +// and may have an explicit type def. class Parameter : public NamedTrait { public: + Parameter(Def* def, Expression* rhs, VerilogFile* file, const SourceInfo& loc) + : NamedTrait(file, loc), name_(def->GetName()), def_(def), rhs_(rhs) {} + Parameter(std::string_view name, Expression* rhs, VerilogFile* file, const SourceInfo& loc) : NamedTrait(file, loc), name_(name), rhs_(rhs) {} @@ -887,10 +970,152 @@ class Parameter : public NamedTrait { std::string GetName() const override { return name_; } private: + // Agrees with `def_` in all cases where `def_` is non-null. std::string name_; + // Currently this is only used for parameters originating from SystemVerilog + // source code. + Def* def_ = nullptr; Expression* rhs_; }; +// A user-defined type that gives a new name to another type, perhaps with some +// value set constraints, e.g. an enum or typedef. +class UserDefinedAliasType : public DataType { + public: + UserDefinedAliasType(DataType* base_type, VerilogFile* file, + const SourceInfo& loc) + : DataType(file, loc), base_type_(base_type) {} + + DataType* BaseType() const { return base_type_; } + + bool IsScalar() const override { return base_type_->IsScalar(); } + + bool IsUserDefined() const override { return true; } + + absl::StatusOr WidthAsInt64() const override { + return base_type_->WidthAsInt64(); + } + + absl::StatusOr FlatBitCountAsInt64() const override { + return base_type_->FlatBitCountAsInt64(); + } + + std::optional width() const override { + return base_type_->width(); + } + + bool is_signed() const override { return base_type_->is_signed(); } + + private: + DataType* base_type_; +}; + +// The declaration of a typedef. This emits "typedef actual_type name;". +class Typedef : public VastNode { + public: + Typedef(Def* def, VerilogFile* file, const SourceInfo& loc) + : VastNode(file, loc), def_(def) {} + + std::string Emit(LineInfo* line_info) const override; + + std::string GetName() const { return def_->GetName(); } + + DataType* data_type() const { return def_->data_type(); } + + private: + Def* def_; +}; + +// The type of an entity when its type is a typedef. This emits just the name of +// the typedef. +class TypedefType : public UserDefinedAliasType { + public: + explicit TypedefType(Typedef* type_def, VerilogFile* file, + const SourceInfo& loc) + : UserDefinedAliasType(type_def->data_type(), file, loc), + type_def_(type_def) {} + + std::string Emit(LineInfo* line_info) const override; + + private: + Typedef* type_def_; +}; + +// Represents the definition of a member of an enum. +class EnumMember : public NamedTrait { + public: + EnumMember(std::string_view name, Expression* rhs, VerilogFile* file, + const SourceInfo& loc) + : NamedTrait(file, loc), name_(name), rhs_(rhs) {} + + std::string GetName() const override { return name_; } + + std::string Emit(LineInfo* line_info) const override; + + private: + std::string name_; + Expression* rhs_; +}; + +// Refers to an enum item for use in expressions. +class EnumMemberRef : public Expression { + public: + EnumMemberRef(EnumMember* member, VerilogFile* file, const SourceInfo& loc) + : Expression(file, loc), member_(member) {} + + std::string Emit(LineInfo* line_info) const override { + return member_->GetName(); + } + + private: + EnumMember* member_; +}; + +// Represents an enum definition. +class Enum : public UserDefinedAliasType { + public: + Enum(DataKind kind, DataType* data_type, VerilogFile* file, + const SourceInfo& loc) + : UserDefinedAliasType(data_type, file, loc), kind_(kind) {} + + EnumMemberRef* AddMember(std::string_view name, Expression* rhs, + const SourceInfo& loc); + + std::string Emit(LineInfo* line_info) const override; + + private: + DataKind kind_; + std::vector members_; +}; + +// Represents a struct type. Currently assumes packed and unsigned. +class Struct : public DataType { + public: + Struct(absl::Span members, VerilogFile* file, + const SourceInfo& loc) + : DataType(file, loc), members_(members.begin(), members.end()) {} + + bool IsScalar() const override { return false; } + + bool IsUserDefined() const override { return true; } + + absl::StatusOr WidthAsInt64() const override { + return absl::UnimplementedError( + "WidthAsInt64 is not implemented for structs."); + } + + absl::StatusOr FlatBitCountAsInt64() const override; + + std::optional width() const override { return std::nullopt; } + + bool is_signed() const override { return false; } + + std::string Emit(LineInfo* line_info) const override; + + private: + std::vector members_; +}; + // Defines an item in a localparam. class LocalParamItem : public NamedTrait { public: @@ -1172,17 +1397,32 @@ class Literal : public Expression { Literal(Bits bits, FormatPreference format, VerilogFile* file, const SourceInfo& loc) : Expression(file, loc), - bits_(bits), + bits_(std::move(bits)), format_(format), - emit_bit_count_(true) {} + emit_bit_count_(true), + effective_bit_count_(bits_.bit_count()) {} Literal(Bits bits, FormatPreference format, bool emit_bit_count, VerilogFile* file, const SourceInfo& loc) : Expression(file, loc), - bits_(bits), + bits_(std::move(bits)), + format_(format), + emit_bit_count_(emit_bit_count), + effective_bit_count_(bits_.bit_count()) { + CHECK(emit_bit_count_ || bits_.bit_count() == 32); + } + + Literal(Bits bits, FormatPreference format, int64_t declared_bit_count, + bool emit_bit_count, bool declared_as_signed, VerilogFile* file, + const SourceInfo& loc) + : Expression(file, loc), + bits_(std::move(bits)), format_(format), - emit_bit_count_(emit_bit_count) { - CHECK(emit_bit_count_ || bits.bit_count() == 32); + emit_bit_count_(emit_bit_count), + declared_as_signed_(declared_as_signed), + effective_bit_count_(declared_bit_count) { + CHECK(declared_bit_count >= bits_.bit_count()); + CHECK(emit_bit_count_ || effective_bit_count_ == 32); } std::string Emit(LineInfo* line_info) const override; @@ -1192,6 +1432,8 @@ class Literal : public Expression { bool IsLiteral() const override { return true; } bool IsLiteralWithValue(int64_t target) const override; + FormatPreference format() const { return format_; } + private: Bits bits_; FormatPreference format_; @@ -1199,6 +1441,12 @@ class Literal : public Expression { // false if the width of bits_ is 32 as the width of an undecorated number // literal in Verilog is 32. bool emit_bit_count_; + // Currently only true for literals originating in SystemVerilog source code + // that are marked by their prefix as signed. + bool declared_as_signed_ = false; + // Usually the same as `bits_.bit_count()`, but if created from a declaration + // with specified bit count in SV source code, it is that specified count. + int64_t effective_bit_count_; }; // Represents a quoted literal string. @@ -1514,6 +1762,8 @@ class VerilogFunction : public VastNode { LogicRef* AddArgument(std::string_view name, DataType* type, const SourceInfo& loc); + LogicRef* AddArgument(Def* def, const SourceInfo& loc); + // Adds a RegDef to the function and returns a LogicRef to it. This should be // used for adding RegDefs to the function instead of AddStatement because // the RegDefs need to appear outside the statement block (begin/end block). @@ -1546,7 +1796,7 @@ class VerilogFunction : public VastNode { // least common denominator. StatementBlock* statement_block_; - std::vector argument_defs_; + std::vector argument_defs_; // The RegDefs of reg's defined in the function. These are emitted before the // statement block. @@ -1574,7 +1824,7 @@ using ModuleMember = std::variant; + Typedef*, Enum*, Cover*, ConcurrentAssertion*, ModuleSection*>; // A ModuleSection is a container of ModuleMembers used to organize the contents // of a module. A Module contains a single top-level ModuleSection which may @@ -1654,8 +1904,8 @@ class Module : public VastNode { // Adds a reg/wire definition to the module with the given type and, for regs, // initialized with the given value. Returns a reference to the definition. - LogicRef* AddReg(std::string_view name, DataType* type, - const SourceInfo& loc, Expression* init = nullptr, + LogicRef* AddReg(std::string_view name, DataType* type, const SourceInfo& loc, + Expression* init = nullptr, ModuleSection* section = nullptr); LogicRef* AddWire(std::string_view name, DataType* type, const SourceInfo& loc, ModuleSection* section = nullptr); @@ -1666,6 +1916,9 @@ class Module : public VastNode { ParameterRef* AddParameter(std::string_view name, Expression* rhs, const SourceInfo& loc); + ParameterRef* AddParameter(Def* def, Expression* rhs, const SourceInfo& loc); + + Typedef* AddTypedef(Def* def, const SourceInfo& loc); // Adds a previously constructed VAST construct to the module. template @@ -1854,6 +2107,9 @@ class VerilogFile { BinaryInfix* Mul(Expression* lhs, Expression* rhs, const SourceInfo& loc) { return Make(loc, lhs, "*", rhs, /*precedence=*/10); } + BinaryInfix* Power(Expression* lhs, Expression* rhs, const SourceInfo& loc) { + return Make(loc, lhs, "**", rhs, /*precedence=*/11); + } BinaryInfix* BitwiseOr(Expression* lhs, Expression* rhs, const SourceInfo& loc) { return Make(loc, lhs, "|", rhs, /*precedence=*/3); diff --git a/xls/codegen/vast_test.cc b/xls/codegen/vast_test.cc index e168359945..f4b342a25a 100644 --- a/xls/codegen/vast_test.cc +++ b/xls/codegen/vast_test.cc @@ -26,6 +26,7 @@ #include "absl/strings/str_cat.h" #include "xls/common/status/matchers.h" #include "xls/ir/bits.h" +#include "xls/ir/format_preference.h" #include "xls/ir/number_parser.h" #include "xls/ir/source_location.h" @@ -63,52 +64,141 @@ TEST_P(VastTest, DataTypes) { LineInfo line_info; DataType* scalar = f.ScalarType(SourceInfo()); - EXPECT_EQ(scalar->EmitWithIdentifier(&line_info, "foo"), " foo"); + EXPECT_FALSE(scalar->IsUserDefined()); EXPECT_THAT(scalar->WidthAsInt64(), IsOkAndHolds(1)); EXPECT_THAT(scalar->FlatBitCountAsInt64(), IsOkAndHolds(1)); EXPECT_EQ(scalar->width(), std::nullopt); EXPECT_FALSE(scalar->is_signed()); + EXPECT_EQ(scalar->EmitWithIdentifier(&line_info, "foo"), " foo"); EXPECT_EQ(line_info.LookupNode(scalar), std::make_optional(std::vector{LineSpan(0, 0)})); + EXPECT_EQ(scalar->Emit(&line_info), ""); // A width 1 data type returned from BitVectorType should be a scalar. DataType* u1 = f.BitVectorType(1, SourceInfo()); - EXPECT_EQ(u1->EmitWithIdentifier(nullptr, "foo"), " foo"); + EXPECT_FALSE(u1->IsUserDefined()); EXPECT_THAT(u1->WidthAsInt64(), IsOkAndHolds(1)); EXPECT_THAT(u1->FlatBitCountAsInt64(), IsOkAndHolds(1)); EXPECT_EQ(u1->width(), std::nullopt); EXPECT_FALSE(u1->is_signed()); + EXPECT_EQ(u1->EmitWithIdentifier(nullptr, "foo"), " foo"); + EXPECT_EQ(u1->Emit(nullptr), ""); DataType* s1 = f.BitVectorType(1, SourceInfo(), /*is_signed=*/true); + EXPECT_FALSE(s1->IsUserDefined()); EXPECT_EQ(s1->EmitWithIdentifier(nullptr, "foo"), " signed [0:0] foo"); EXPECT_THAT(s1->WidthAsInt64(), IsOkAndHolds(1)); EXPECT_THAT(s1->FlatBitCountAsInt64(), IsOkAndHolds(1)); EXPECT_TRUE(s1->is_signed()); DataType* u2 = f.BitVectorType(2, SourceInfo()); + EXPECT_FALSE(u2->IsUserDefined()); EXPECT_EQ(u2->EmitWithIdentifier(nullptr, "foo"), " [1:0] foo"); EXPECT_THAT(u2->WidthAsInt64(), IsOkAndHolds(2)); EXPECT_THAT(u2->FlatBitCountAsInt64(), IsOkAndHolds(2)); EXPECT_FALSE(u2->is_signed()); DataType* u32 = f.BitVectorType(32, SourceInfo()); + EXPECT_FALSE(u32->IsUserDefined()); EXPECT_EQ(u32->EmitWithIdentifier(nullptr, "foo"), " [31:0] foo"); EXPECT_THAT(u32->WidthAsInt64(), IsOkAndHolds(32)); EXPECT_THAT(u32->FlatBitCountAsInt64(), IsOkAndHolds(32)); EXPECT_FALSE(u32->is_signed()); DataType* s32 = f.BitVectorType(32, SourceInfo(), /*is_signed=*/true); + EXPECT_FALSE(s32->IsUserDefined()); EXPECT_EQ(s32->EmitWithIdentifier(nullptr, "foo"), " signed [31:0] foo"); EXPECT_THAT(s32->WidthAsInt64(), IsOkAndHolds(32)); EXPECT_THAT(s32->FlatBitCountAsInt64(), IsOkAndHolds(32)); EXPECT_TRUE(s32->is_signed()); DataType* packed_array = f.PackedArrayType(10, {3, 2}, SourceInfo()); + EXPECT_FALSE(packed_array->IsUserDefined()); + EXPECT_EQ(packed_array->EmitWithIdentifier(nullptr, "foo"), + " [9:0][2:0][1:0] foo"); + EXPECT_THAT(packed_array->WidthAsInt64(), IsOkAndHolds(10)); + EXPECT_THAT(packed_array->FlatBitCountAsInt64(), IsOkAndHolds(60)); + EXPECT_FALSE(packed_array->is_signed()); + EXPECT_EQ(packed_array->Emit(nullptr), " [9:0][2:0][1:0]"); + + Enum* enum_def = f.Make(SourceInfo(), DataKind::kLogic, + f.BitVectorType(32, SourceInfo())); + EXPECT_TRUE(enum_def->IsUserDefined()); + EnumMemberRef* foo_ref = + enum_def->AddMember("foo", f.PlainLiteral(3, SourceInfo()), SourceInfo()); + EnumMemberRef* bar_ref = + enum_def->AddMember("bar", f.PlainLiteral(1, SourceInfo()), SourceInfo()); + EXPECT_EQ(enum_def->Emit(nullptr), R"(enum logic [31:0] { + foo = 3, + bar = 1 +})"); + EXPECT_EQ(enum_def->EmitWithIdentifier(nullptr, "var"), R"(enum logic [31:0] { + foo = 3, + bar = 1 +} var)"); + EXPECT_EQ(foo_ref->Emit(nullptr), "foo"); + EXPECT_EQ(bar_ref->Emit(nullptr), "bar"); + + Enum* untyped_enum_def = f.Make(SourceInfo(), DataKind::kUntypedEnum, + f.IntegerType(SourceInfo())); + untyped_enum_def->AddMember("a", f.PlainLiteral(3, SourceInfo()), + SourceInfo()); + EXPECT_EQ(untyped_enum_def->Emit(nullptr), R"(enum { + a = 3 +})"); + + Typedef* type_def = f.Make( + SourceInfo(), + f.Make(SourceInfo(), "my_enum_t", DataKind::kUser, enum_def)); + EXPECT_EQ(type_def->Emit(nullptr), R"(typedef enum logic [31:0] { + foo = 3, + bar = 1 +} my_enum_t;)"); + TypedefType* type_def_type = f.Make(SourceInfo(), type_def); + EXPECT_TRUE(type_def_type->IsUserDefined()); + EXPECT_EQ(type_def_type->Emit(nullptr), "my_enum_t"); + EXPECT_EQ(type_def_type->EmitWithIdentifier(nullptr, "x"), "my_enum_t x"); + + std::vector struct_members = { + f.Make(SourceInfo(), "elem1", DataKind::kLogic, + f.ScalarType(SourceInfo())), + f.Make(SourceInfo(), "foo_bar", DataKind::kInteger, + f.IntegerType(SourceInfo())), + f.Make(SourceInfo(), "Bv", DataKind::kLogic, + f.BitVectorType(16, SourceInfo()))}; + DataType* struct_type = f.Make(SourceInfo(), struct_members); + EXPECT_TRUE(struct_type->IsUserDefined()); + EXPECT_EQ(struct_type->Emit(nullptr), R"(struct packed { + logic elem1; + integer foo_bar; + logic [15:0] Bv; +})"); + + std::vector dims = {f.PlainLiteral(5, SourceInfo()), + f.PlainLiteral(2, SourceInfo())}; + DataType* packed_array_of_structs_with_dims_as_max = f.Make( + SourceInfo(), struct_type, dims, /*dims_are_max=*/true); + EXPECT_EQ(packed_array_of_structs_with_dims_as_max->Emit(nullptr), + R"(struct packed { + logic elem1; + integer foo_bar; + logic [15:0] Bv; +} [5:0][2:0])"); + EXPECT_TRUE(packed_array_of_structs_with_dims_as_max->IsUserDefined()); + EXPECT_EQ(packed_array_of_structs_with_dims_as_max->EmitWithIdentifier( + nullptr, "foo"), + R"(struct packed { + logic elem1; + integer foo_bar; + logic [15:0] Bv; +} [5:0][2:0] foo)"); + EXPECT_EQ(packed_array->EmitWithIdentifier(nullptr, "foo"), " [9:0][2:0][1:0] foo"); EXPECT_THAT(packed_array->WidthAsInt64(), IsOkAndHolds(10)); EXPECT_THAT(packed_array->FlatBitCountAsInt64(), IsOkAndHolds(60)); EXPECT_FALSE(packed_array->is_signed()); + EXPECT_EQ(packed_array->Emit(nullptr), " [9:0][2:0][1:0]"); DataType* spacked_array = f.PackedArrayType(10, {3, 2}, SourceInfo(), /*is_signed=*/true); @@ -117,7 +207,7 @@ TEST_P(VastTest, DataTypes) { EXPECT_THAT(spacked_array->WidthAsInt64(), IsOkAndHolds(10)); EXPECT_THAT(spacked_array->FlatBitCountAsInt64(), IsOkAndHolds(60)); EXPECT_TRUE(spacked_array->is_signed()); - + EXPECT_EQ(spacked_array->Emit(nullptr), " signed [9:0][2:0][1:0]"); DataType* unpacked_array = f.UnpackedArrayType(10, {3, 2}, SourceInfo()); if (f.use_system_verilog()) { EXPECT_EQ(unpacked_array->EmitWithIdentifier(nullptr, "foo"), @@ -145,6 +235,15 @@ TEST_P(VastTest, DataTypes) { StatusIs(absl::StatusCode::kFailedPrecondition, HasSubstr("Width is not a literal"))); EXPECT_FALSE(bv->is_signed()); + + // Bit vector with max as size expr. + DataType* bv_max = f.Make( + SourceInfo(), + /*size_expr=*/ + f.Mul(f.PlainLiteral(10, SourceInfo()), f.PlainLiteral(5, SourceInfo()), + SourceInfo()), + /*is_signed=*/false, /*size_expr_is_max=*/true); + EXPECT_EQ(bv_max->EmitWithIdentifier(nullptr, "foo"), " [10 * 5:0] foo"); } TEST_P(VastTest, ModuleWithManyVariableDefinitions) { @@ -417,6 +516,27 @@ TEST_P(VastTest, Literals) { EXPECT_FALSE(all_ones->IsLiteralWithValue(0)); EXPECT_TRUE(all_ones->IsLiteralWithValue(15)); EXPECT_FALSE(all_ones->IsLiteralWithValue(-1)); + + // Literals with the precise controls used when originating from SystemVerilog + // source code. + EXPECT_EQ( + f.Make(SourceInfo(), UBits(15, 4), FormatPreference::kHex, + /*declared_bit_count=*/32, + /*emit_bit_count=*/true, /*declared_as_signed=*/false) + ->Emit(nullptr), + "32'hf"); + EXPECT_EQ( + f.Make(SourceInfo(), UBits(15, 4), FormatPreference::kHex, + /*declared_bit_count=*/32, + /*emit_bit_count=*/true, /*declared_as_signed=*/true) + ->Emit(nullptr), + "32'shf"); + EXPECT_EQ(f.Make( + SourceInfo(), UBits(15, 4), FormatPreference::kUnsignedDecimal, + /*declared_bit_count=*/32, + /*emit_bit_count=*/false, /*declared_as_signed=*/false) + ->Emit(nullptr), + "15"); } TEST_P(VastTest, Precedence) { @@ -445,6 +565,8 @@ TEST_P(VastTest, Precedence) { f.Add(a, f.Mul(b, c, SourceInfo()), SourceInfo())->Emit(nullptr)); EXPECT_EQ("a * (b + c)", f.Mul(a, f.Add(b, c, SourceInfo()), SourceInfo())->Emit(nullptr)); + EXPECT_EQ("a ** (b * c)", + f.Power(a, f.Mul(b, c, SourceInfo()), SourceInfo())->Emit(nullptr)); EXPECT_EQ( "a | (a + b || b)", @@ -497,6 +619,16 @@ TEST_P(VastTest, NestedUnaryOps) { ->Emit(nullptr)); } +TEST_P(VastTest, ReturnStatement) { + VerilogFile f(GetFileType()); + EXPECT_EQ( + f.Make( + SourceInfo(), f.Mul(f.PlainLiteral(2, SourceInfo()), + f.PlainLiteral(10, SourceInfo()), SourceInfo())) + ->Emit(nullptr), + "return 2 * 10;"); +} + TEST_P(VastTest, Case) { VerilogFile f(GetFileType()); Module* m = f.AddModule("top", SourceInfo()); @@ -556,7 +688,6 @@ TEST_P(VastTest, Casez) { endcase)"); } - TEST_P(VastTest, CaseWithHighZ) { VerilogFile f(GetFileType()); Module* m = f.AddModule("top", SourceInfo()); @@ -570,13 +701,11 @@ TEST_P(VastTest, CaseWithHighZ) { Case* case_statement = ac->statements()->Add(SourceInfo(), my_state); FourValueBinaryLiteral* msb_set = f.Make( SourceInfo(), std::vector({FourValueBit::kOne, FourValueBit::kHighZ})); - StatementBlock* one_block = - case_statement->AddCaseArm(msb_set); + StatementBlock* one_block = case_statement->AddCaseArm(msb_set); one_block->Add(SourceInfo(), thing_next, thing); FourValueBinaryLiteral* lsb_unset = f.Make( SourceInfo(), std::vector({FourValueBit::kUnknown, FourValueBit::kZero})); - StatementBlock* zero_block = - case_statement->AddCaseArm(lsb_unset); + StatementBlock* zero_block = case_statement->AddCaseArm(lsb_unset); zero_block->Add(SourceInfo(), thing_next, thing); StatementBlock* default_block = case_statement->AddCaseArm(DefaultSentinel()); default_block->Add(SourceInfo(), thing_next, @@ -644,7 +773,8 @@ TEST_P(VastTest, AlwaysFlopTestSyncReset) { m->AddReg("b_next", f.BitVectorType(8, SourceInfo()), SourceInfo()); AlwaysFlop* af = m->Add( - SourceInfo(), clk, Reset{rst, /*async*/ false, /*active_low*/ false}); + SourceInfo(), clk, + Reset{.signal = rst, .asynchronous = false, .active_low = false}); af->AddRegister(a, a_next, SourceInfo(), /*reset_value=*/f.Literal(42, 8, SourceInfo())); af->AddRegister(b, b_next, SourceInfo()); @@ -676,7 +806,8 @@ TEST_P(VastTest, AlwaysFlopTestAsyncResetActiveLow) { m->AddReg("b_next", f.BitVectorType(8, SourceInfo()), SourceInfo()); AlwaysFlop* af = m->Add( - SourceInfo(), clk, Reset{rst, /*async*/ true, /*active_low*/ true}); + SourceInfo(), clk, + Reset{.signal = rst, .asynchronous = true, .active_low = true}); af->AddRegister(a, a_next, SourceInfo(), /*reset_value=*/f.Literal(42, 8, SourceInfo())); af->AddRegister(b, b_next, SourceInfo()); @@ -819,19 +950,19 @@ TEST_P(VastTest, InstantiationTest) { auto* tx_byte_def = f.Make(SourceInfo(), "my_tx_byte", f.BitVectorType(8, SourceInfo())); auto* tx_byte_ref = f.Make(SourceInfo(), tx_byte_def); - auto* instantiation = - f.Make(SourceInfo(), - /*module_name=*/"uart_transmitter", - /*instance_name=*/"tx", - /*parameters=*/ - std::vector{ - {"ClocksPerBaud", default_clocks_per_baud}, - }, - /*connections=*/ - std::vector{ - {"clk", clk_ref}, - {"tx_byte", tx_byte_ref}, - }); + auto* instantiation = f.Make( + SourceInfo(), + /*module_name=*/"uart_transmitter", + /*instance_name=*/"tx", + /*parameters=*/ + std::vector{ + {.port_name = "ClocksPerBaud", .expression = default_clocks_per_baud}, + }, + /*connections=*/ + std::vector{ + {.port_name = "clk", .expression = clk_ref}, + {.port_name = "tx_byte", .expression = tx_byte_ref}, + }); EXPECT_EQ(instantiation->Emit(nullptr), R"(uart_transmitter #( @@ -854,15 +985,15 @@ TEST_P(VastTest, TemplateInstantiationTest) { const std::string_view code_template = "foo {fn} (.x({a}), .out({return}))"; - auto* instantiation = - f.Make(SourceInfo(), - /*instance_name=*/"template_inst_42", - /*code_template=*/code_template, - /*connections=*/ - std::vector{ - {"a", a_ref}, - {"return", ret_ref}, - }); + auto* instantiation = f.Make( + SourceInfo(), + /*instance_name=*/"template_inst_42", + /*code_template=*/code_template, + /*connections=*/ + std::vector{ + {.port_name = "a", .expression = a_ref}, + {.port_name = "return", .expression = ret_ref}, + }); EXPECT_EQ(instantiation->Emit(nullptr), "foo template_inst_42 (.x(i_a), .out(o_ret));"); @@ -891,6 +1022,9 @@ TEST_P(VastTest, ParameterAndLocalParam) { m->AddParameter("ClocksPerBaud", f.Make(SourceInfo(), "DEFAULT_CLOCKS_PER_BAUD"), SourceInfo()); + m->AddParameter(f.Make(SourceInfo(), "ParamWithDef", DataKind::kLogic, + f.BitVectorType(16, SourceInfo())), + f.PlainLiteral(5, SourceInfo()), SourceInfo()); LocalParam* p = m->Add(SourceInfo()); LocalParamItemRef* idle = p->AddItem("StateIdle", f.Literal(0, 2, SourceInfo()), SourceInfo()); @@ -908,6 +1042,7 @@ TEST_P(VastTest, ParameterAndLocalParam) { EXPECT_EQ(m->Emit(nullptr), R"(module top; parameter ClocksPerBaud = `DEFAULT_CLOCKS_PER_BAUD; + parameter logic [15:0] ParamWithDef = 5; localparam StateIdle = 2'h0, StateGotByte = 2'h1, @@ -1255,6 +1390,9 @@ TEST_P(VastTest, VerilogFunction) { func->AddArgument("foo", f.BitVectorType(32, SourceInfo()), SourceInfo()); LogicRef* bar = func->AddArgument("bar", f.BitVectorType(3, SourceInfo()), SourceInfo()); + func->AddArgument(f.Make(SourceInfo(), "baz", DataKind::kInteger, + f.IntegerType(SourceInfo())), + SourceInfo()); func->AddStatement(SourceInfo(), func->return_value_ref(), f.Shll(foo, bar, SourceInfo())); @@ -1268,7 +1406,7 @@ TEST_P(VastTest, VerilogFunction) { f.Literal(UBits(2, 3), SourceInfo())})); EXPECT_EQ(m->Emit(nullptr), R"(module top; - function automatic [41:0] func (input reg [31:0] foo, input reg [2:0] bar); + function automatic [41:0] func (input reg [31:0] foo, input reg [2:0] bar, input integer baz); begin func = foo << bar; end