Skip to content

Commit

Permalink
slang-reflect: Add support to reflect Unions (#1173)
Browse files Browse the repository at this point in the history
This commit adds support to slang-reflect to support unions.

As SystemVerilog structs are not 1:1 translatable to C++, because, for
example, a C++ struct might not be stored as an array of bits as it is
done in SystemVerilog.

The SystemVerilog union is reflected as a struct
with a single member with the data of the union. Helper functions are
generated to retrieve the different members of the union with the
corresponding type.
  • Loading branch information
Sustrak authored Nov 14, 2024
1 parent 41ee213 commit 5a5d423
Show file tree
Hide file tree
Showing 20 changed files with 445 additions and 196 deletions.
3 changes: 3 additions & 0 deletions include/slang/ast/types/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ class SLANG_EXPORT Type : public Symbol {
/// Indicates whether this is a dynamic array, associative array, or a queue.
bool isDynamicallySizedArray() const;

/// Indicates whether this is a packed or unpacked union.
bool isUnion() const;

/// Indicates whether this is a tagged union, packed or unpacked.
bool isTaggedUnion() const;

Expand Down
11 changes: 11 additions & 0 deletions source/ast/types/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,17 @@ bool Type::isHandleType() const {
}
}

bool Type::isUnion() const {
const Type& ct = getCanonicalType();
switch (ct.kind) {
case SymbolKind::UnpackedUnionType:
case SymbolKind::PackedUnionType:
return true;
default:
return false;
}
}

bool Type::isTaggedUnion() const {
auto& ct = getCanonicalType();
switch (ct.kind) {
Expand Down
5 changes: 3 additions & 2 deletions tools/reflect/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
# ~~~

add_library(
slang_reflect_obj_lib OBJECT src/SvStruct.cpp src/SvType.cpp src/SvEnum.cpp
src/SvTypeReflector.cpp src/SvLocalParam.cpp)
slang_reflect_obj_lib OBJECT
src/SvStruct.cpp src/SvType.cpp src/SvEnum.cpp src/SvTypeReflector.cpp
src/SvLocalParam.cpp src/SvUnion.cpp)

target_include_directories(slang_reflect_obj_lib PUBLIC include ../../include)
target_link_libraries(slang_reflect_obj_lib PUBLIC slang::slang)
Expand Down
16 changes: 8 additions & 8 deletions tools/reflect/include/ASTVisitors.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,25 @@

#pragma once

#include <iostream>
#include <ranges>

#include "slang/ast/ASTVisitor.h"
#include "slang/ast/Compilation.h"
#include "slang/syntax/SyntaxVisitor.h"

class PublicDirectiveVisitor : public slang::syntax::SyntaxVisitor<PublicDirectiveVisitor> {
public:
explicit PublicDirectiveVisitor(slang::parsing::TokenKind tokenKind) : tokenKind(tokenKind) {}
explicit PublicDirectiveVisitor(const slang::parsing::TokenKind tokenKind) :
tokenKind(tokenKind) {}

void visitToken(slang::parsing::Token token) {
void visitToken(const slang::parsing::Token token) {
if (token.kind == tokenKind) {
auto blockComments = token.trivia() | std::views::filter([](auto& v) {
return v.kind == slang::parsing::TriviaKind::BlockComment;
});

for (auto& blockComment : blockComments) {
isPublic = std::find(publicDirectives.begin(), publicDirectives.end(),
blockComment.getRawText()) != publicDirectives.end();
isPublic = std::ranges::find(publicDirectives, blockComment.getRawText()) !=
publicDirectives.end();
}
}
}
Expand All @@ -38,6 +37,7 @@ class PublicDirectiveVisitor : public slang::syntax::SyntaxVisitor<PublicDirecti
bool isPublic{false};
slang::parsing::TokenKind tokenKind;

constexpr static const std::array<std::string_view, 3> publicDirectives = {
"/* public */", "/*verilator public*/", "/* verilator public */"};
constexpr static std::array<std::string_view, 3> publicDirectives = {"/* public */",
"/*verilator public*/",
"/* verilator public */"};
};
22 changes: 11 additions & 11 deletions tools/reflect/include/CppEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
#include <sstream>
#include <vector>

#include "slang/util/SmallVector.h"
#include "slang/util/Util.h"

namespace fs = std::filesystem;

class HppFile {
public:
explicit HppFile(std::string_view name, bool noSystemC) : fileName(std::string(name) + ".h") {
explicit HppFile(const std::string_view name, const bool noSystemC) :
fileName(std::string(name) + ".h") {
includes.emplace_back("ostream");
includes.emplace_back("cstddef");
includes.emplace_back("cstdint");
Expand All @@ -35,11 +35,11 @@ class HppFile {

void add(std::string&& code) { hpp << code; }
void addInclude(std::string&& code) {
if (std::find(includes.begin(), includes.end(), code) == includes.end())
if (std::ranges::find(includes, code) == includes.end())
includes.emplace_back(code);
}
void addIncludeHeader(std::string_view code) {
if (std::find(headers.begin(), headers.end(), code) == headers.end())
if (std::ranges::find(headers, code) == headers.end())
headers.emplace_back(code);
}
void addWithIndent(std::string&& code) { hpp << indent(currentIndent) << code; }
Expand All @@ -49,7 +49,7 @@ class HppFile {
currentIndent--;
}

std::string emit() {
std::string emit() const {
auto includesTransform = std::views::transform(includes, [](const auto& inc) {
return fmt::format("#include <{}>", inc);
});
Expand All @@ -61,7 +61,7 @@ class HppFile {
hpp.str());
}

void emitToFile(const fs::path& path) {
void emitToFile(const fs::path& path) const {
auto outFile = std::ofstream(path / fileName);
outFile << emit();
}
Expand All @@ -73,7 +73,7 @@ class HppFile {
std::string fileName;
uint32_t currentIndent{0};

std::string indent(uint64_t blocks) {
static std::string indent(const uint64_t blocks) {
std::string ret;
for (auto i = 0; i < blocks * 4; i++)
ret += " ";
Expand All @@ -83,21 +83,21 @@ class HppFile {

class CppEmitter {
public:
explicit CppEmitter(bool noSystemC) : noSystemC(noSystemC) {}
explicit CppEmitter(const bool noSystemC) : noSystemC(noSystemC) {}

[[nodiscard]] HppFile& newNamespace(std::string_view name) {
[[nodiscard]] HppFile& newNamespace(const std::string_view name) {
hppFiles.push_back(HppFile(name, noSystemC));
return hppFiles.back();
}

std::string emit() {
std::string emit() const {
std::stringstream ret;
for (auto& hpp : hppFiles)
ret << hpp.emit();
return std::move(ret.str());
}

void emitToFile(const fs::path& path) {
void emitToFile(const fs::path& path) const {
for (auto& hpp : hppFiles)
hpp.emitToFile(path);
}
Expand Down
7 changes: 3 additions & 4 deletions tools/reflect/include/SvEnum.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@

#include "slang/ast/types/AllTypes.h"

class SvEnum : public SvGeneric {
class SvEnum final : public SvGeneric {
public:
explicit SvEnum(const slang::ast::TypeAliasType& type) :
SvGeneric(SvGeneric::Kind::Enum), type(type) {}
explicit SvEnum(const slang::ast::TypeAliasType& type) : SvGeneric(Kind::Enum), type(type) {}

void toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool noSystemC) const override;
void toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool) const override;

private:
const slang::ast::TypeAliasType& type;
Expand Down
24 changes: 12 additions & 12 deletions tools/reflect/include/SvGeneric.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,36 @@
#pragma once

#include "CppEmitter.h"

#include "slang/ast/types/AllTypes.h"
#include <unordered_map>

using SvAliases = std::unordered_map<std::string_view, std::string_view>;

class SvGeneric {
public:
enum class Kind { Struct, Enum, LocalParam };
explicit SvGeneric(Kind kind) : kind(kind) {}
enum class Kind { Struct, Enum, LocalParam, Union };
explicit SvGeneric(const Kind kind) : kind(kind) {}

virtual void toCpp(HppFile&, std::string_view, const SvAliases&, bool noSystemC) const = 0;

bool isStruct() const { return kind == Kind::Struct; }
bool isEnum() const { return kind == Kind::Enum; }
bool isLocalParam() const { return kind == Kind::LocalParam; }
[[nodiscard]] bool isStruct() const { return kind == Kind::Struct; }
[[nodiscard]] bool isEnum() const { return kind == Kind::Enum; }
[[nodiscard]] bool isLocalParam() const { return kind == Kind::LocalParam; }
[[nodiscard]] bool isUnion() const { return kind == Kind::Union; }

virtual ~SvGeneric() = default;

protected:
Kind kind;

static std::string_view resolveAlias(std::string_view typeName, const SvAliases& aliases) {
if (auto alias = aliases.find(typeName); alias != aliases.end())
[[nodiscard]] static std::string_view resolveAlias(const std::string_view& typeName,
const SvAliases& aliases) {
if (const auto& alias = aliases.find(typeName); alias != aliases.end())
return alias->second;
return typeName;
}

static bool isCppReserved(std::string_view name) {
return std::find(cppReservedKeywords.begin(), cppReservedKeywords.end(), name) !=
cppReservedKeywords.end();
[[nodiscard]] static bool isCppReserved(const std::string_view name) {
return std::ranges::find(cppReservedKeywords, name) != cppReservedKeywords.end();
}

static constexpr std::array cppReservedKeywords = {"alignas",
Expand Down
5 changes: 2 additions & 3 deletions tools/reflect/include/SvLocalParam.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@
#pragma once

#include "SvGeneric.h"
#include "SvType.h"
#include <fmt/format.h>

#include "slang/ast/symbols/ParameterSymbols.h"

class SvLocalParam : public SvGeneric {
class SvLocalParam final : public SvGeneric {
public:
explicit SvLocalParam(const slang::ast::ParameterSymbol& parameter) :
SvGeneric(SvGeneric::Kind::LocalParam), parameter(parameter) {}
SvGeneric(Kind::LocalParam), parameter(parameter) {}

void toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool) const override;

Expand Down
5 changes: 3 additions & 2 deletions tools/reflect/include/SvStruct.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
#include "CppEmitter.h"
#include "SvGeneric.h"
#include "fmt/format.h"
#include <slang/ast/types/AllTypes.h>

class SvStruct : public SvGeneric {
class SvStruct final : public SvGeneric {
public:
explicit SvStruct(const slang::ast::TypeAliasType& type) :
SvGeneric(SvGeneric::Kind::Struct), type(type) {}
SvGeneric(Kind::Struct), type(type) {}

void toCpp(HppFile& hppFile, std::string_view _namespace, const SvAliases& aliases,
bool noSystemC) const override;
Expand Down
17 changes: 9 additions & 8 deletions tools/reflect/include/SvType.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@

#pragma once

#include <ostream>

#include "slang/ast/types/Type.h"

namespace CppType {
enum Type { BOOL, U32, U64, SC_BV, STRUCT, ENUM };
enum Type { BOOL, U32, U64, SC_BV, STRUCT, ENUM, UNION };

std::string toString(const Type& cppType);
Type fromSize(size_t size);
Expand All @@ -22,15 +20,18 @@ Type fromSize(size_t size);
class SvType {
public:
explicit SvType(const slang::ast::Type& type);
explicit SvType(const slang::ast::Type& type, std::string_view name) : SvType(type) {
explicit SvType(const slang::ast::Type& type, const std::string_view name) : SvType(type) {
this->name = name;
}

bool isStruct() const { return cppType == CppType::STRUCT; }
bool isEnum() const { return cppType == CppType::ENUM; }
bool isStructOrEnum() const { return this->isStruct() || this->isEnum(); }
[[nodiscard]] bool isStruct() const { return cppType == CppType::STRUCT; }
[[nodiscard]] bool isEnum() const { return cppType == CppType::ENUM; }
[[nodiscard]] bool isUnion() const { return cppType == CppType::UNION; }
[[nodiscard]] bool isStructEnumOrUnion() const {
return this->isStruct() || this->isEnum() || this->isUnion();
}

std::string toString() const;
[[nodiscard]] std::string toString() const;
friend std::ostream& operator<<(std::ostream& os, const SvType& type);

CppType::Type cppType;
Expand Down
12 changes: 6 additions & 6 deletions tools/reflect/include/SvTypeReflector.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@

class SvTypeReflector {
public:
explicit SvTypeReflector(std::unique_ptr<slang::ast::Compilation> compilation, bool verbose,
bool noSystemC) :
compilation(std::move(compilation)), verbose(verbose), noSystemC(noSystemC),
cppEmitter(noSystemC) {}
explicit SvTypeReflector(std::unique_ptr<slang::ast::Compilation> compilation,
const bool verbose, const bool noSystemC) :
verbose(verbose), noSystemC(noSystemC), cppEmitter(noSystemC),
compilation(std::move(compilation)) {}

void reflect();

std::string emit() { return cppEmitter.emit(); }
void emitToFile(const fs::path& path) { cppEmitter.emitToFile(path); }
std::string emit() const { return cppEmitter.emit(); }
void emitToFile(const fs::path& path) const { cppEmitter.emitToFile(path); }

private:
bool verbose;
Expand Down
23 changes: 23 additions & 0 deletions tools/reflect/include/SvUnion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//------------------------------------------------------------------------------
//! @file SvEnum.h
//! @brief Handles with SystemVerilog Enums
//
// SPDX-FileCopyrightText: Michael Popoloski
// SPDX-License-Identifier: MIT
//------------------------------------------------------------------------------

#pragma once

#include "SvGeneric.h"

#include "slang/ast/types/AllTypes.h"

class SvUnion final : public SvGeneric {
public:
explicit SvUnion(const slang::ast::TypeAliasType& type) : SvGeneric(Kind::Union), type(type) {}

void toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool noSystemC) const override;

private:
const slang::ast::TypeAliasType& type;
};
20 changes: 7 additions & 13 deletions tools/reflect/src/SvEnum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
#include "SvEnum.h"

#include <fmt/format.h>
#include <iostream>

void SvEnum::toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool) const {
auto underlyingType = [&]() {
auto underlyingType = [&] {
if (type.getBitstreamWidth() <= 8)
return "uint8_t"sv;
if (type.getBitstreamWidth() <= 16)
Expand All @@ -16,9 +15,8 @@ void SvEnum::toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool) c
return "uint32_t"sv;
if (type.getBitstreamWidth() <= 64)
return "uint64_t"sv;
else
SLANG_THROW(
std::runtime_error("Enum with $bits size bigger than 64 bits are not supported"));
SLANG_THROW(
std::runtime_error("Enum with $bits size bigger than 64 bits are not supported"));
};
//** STRUCT (ENUM) DECLARATION **//
hppFile.addWithIndent(fmt::format("struct {} {{\n", type.name));
Expand Down Expand Up @@ -62,12 +60,8 @@ void SvEnum::toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool) c
hppFile.increaseIndent();
hppFile.addWithIndent("switch (__data) {\n");
hppFile.increaseIndent();
for (const auto& member : members)
hppFile.addWithIndent(
fmt::format("case {}: type = Type::{}; break;\n", member.second, member.first));
hppFile.addWithIndent(fmt::format(
"default: throw std::runtime_error(\"Can not create {} from provided value\");\n",
type.name));
for (const auto& [name, value] : members)
hppFile.addWithIndent(fmt::format("case {}: type = Type::{}; break;\n", value, name));
hppFile.decreaseIndent();
hppFile.addWithIndent("}\n");
hppFile.decreaseIndent();
Expand All @@ -82,8 +76,8 @@ void SvEnum::toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool) c
hppFile.increaseIndent();
hppFile.addWithIndent("switch (__data.type) {\n");
hppFile.increaseIndent();
for (const auto& member : members)
hppFile.addWithIndent(fmt::format("case Type::{0}: os << \"{0}\"; break;\n", member.first));
for (const auto& name : members | std::views::keys)
hppFile.addWithIndent(fmt::format("case Type::{0}: os << \"{0}\"; break;\n", name));
hppFile.decreaseIndent();
hppFile.addWithIndent("}\n");
hppFile.addWithIndent("return os;\n");
Expand Down
Loading

0 comments on commit 5a5d423

Please sign in to comment.