Skip to content

Commit

Permalink
slang-reflect: Add support to reflect Unions
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 committed Nov 13, 2024
1 parent 5311efc commit 26f66eb
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 40 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
3 changes: 2 additions & 1 deletion tools/reflect/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

add_library(
slang_reflect_obj_lib OBJECT src/SvStruct.cpp src/SvType.cpp src/SvEnum.cpp
src/SvTypeReflector.cpp src/SvLocalParam.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
2 changes: 1 addition & 1 deletion tools/reflect/include/SvEnum.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class SvEnum final : public SvGeneric {
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
8 changes: 3 additions & 5 deletions tools/reflect/include/SvGeneric.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,15 @@ 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;

Expand Down
13 changes: 8 additions & 5 deletions tools/reflect/include/SvType.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#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 @@ -24,11 +24,14 @@ class SvType {
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
24 changes: 24 additions & 0 deletions tools/reflect/include/SvUnion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//------------------------------------------------------------------------------
//! @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;
};
6 changes: 1 addition & 5 deletions tools/reflect/src/SvEnum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,7 @@ void SvEnum::toCpp(HppFile& hppFile, std::string_view, const SvAliases&, bool) c
hppFile.addWithIndent("switch (__data) {\n");
hppFile.increaseIndent();
for (const auto& [name, value] : members)
hppFile.addWithIndent(
fmt::format("case {}: type = Type::{}; break;\n", value, name));
hppFile.addWithIndent(fmt::format(
"default: throw std::runtime_error(\"Can not create {} from provided value\");\n",
type.name));
hppFile.addWithIndent(fmt::format("case {}: type = Type::{}; break;\n", value, name));
hppFile.decreaseIndent();
hppFile.addWithIndent("}\n");
hppFile.decreaseIndent();
Expand Down
24 changes: 12 additions & 12 deletions tools/reflect/src/SvStruct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include "slang/util/OS.h"

void SvStruct::toCpp(HppFile& hppFile, const std::string_view _namespace, const SvAliases& aliases,
bool noSystemC) const {
const bool noSystemC) const {
//* STRUCT DECLARATION **/
auto structName = isCppReserved(type.name) ? fmt::format("_{}", type.name)
: std::string(type.name);
Expand All @@ -18,8 +18,8 @@ void SvStruct::toCpp(HppFile& hppFile, const std::string_view _namespace, const

std::vector<std::pair<std::string, SvType>> members;

size_t structSize = type.getBitstreamWidth();
auto cppType = CppType::fromSize(structSize);
const size_t structSize = type.getBitstreamWidth();
const auto cppType = CppType::fromSize(structSize);

if (cppType == CppType::SC_BV && noSystemC) {
slang::OS::printE(fmt::format("Headers for the struct {} can not be generated without "
Expand All @@ -45,7 +45,7 @@ void SvStruct::toCpp(HppFile& hppFile, const std::string_view _namespace, const

//** MEMBERS DECLARATION **//
for (const auto& [name, type] : members) {
if (type.isStructOrEnum() && _namespace != type._namespace) {
if (type.isStructEnumOrUnion() && _namespace != type._namespace) {
hppFile.addWithIndent(fmt::format("{}::{} {};\n", type._namespace,
type.toString(), name));
hppFile.addIncludeHeader(type._namespace);
Expand Down Expand Up @@ -94,16 +94,16 @@ void SvStruct::toCpp(HppFile& hppFile, const std::string_view _namespace, const
}
}
else {
for (const auto& [name, snd] : members)
for (const auto& [name, type] : members)
values.emplace_back(fmt::format("(__data >> {0}_s) & (~0ULL >> (64 - {1}))",
name, snd.size));
name, type.size));
}

for (auto i = 0; i < members.size(); i++) {
const auto& [name, type] = members[i];
const auto& value = values[i];

if (type.isStructOrEnum())
if (type.isStructEnumOrUnion())
if (_namespace != type._namespace)
hppFile.addWithIndent(fmt::format("{} = {}::{}({});\n", name,
type._namespace,
Expand Down Expand Up @@ -136,7 +136,7 @@ void SvStruct::toCpp(HppFile& hppFile, const std::string_view _namespace, const
value = fmt::format("__data.range({0}_s + {0}_w - 1, {0}_s).to_uint64()",
name);

if (type.isStructOrEnum())
if (type.isStructEnumOrUnion())
if (_namespace != type._namespace)
hppFile.addWithIndent(fmt::format("{} = {}::{}({});\n", name,
type._namespace,
Expand Down Expand Up @@ -164,7 +164,7 @@ void SvStruct::toCpp(HppFile& hppFile, const std::string_view _namespace, const
else {
hppFile.addWithIndent(
fmt::format("ret.range({0}_s + {0}_w - 1, {0}_s) = ", name));
if (type.isStructOrEnum() && type.size > 64)
if (type.isStructEnumOrUnion() && type.size > 64)
hppFile.add(fmt::format("sc_bv<{}>({});\n", type.size, name));
else
hppFile.add(fmt::format("{};\n", name));
Expand Down Expand Up @@ -197,7 +197,7 @@ void SvStruct::toCpp(HppFile& hppFile, const std::string_view _namespace, const
else {
hppFile.addWithIndent(
fmt::format("ret.range({0}_s + {0}_w - 1, {0}_s) = ", name));
if (type.isStructOrEnum() && type.size > 64)
if (type.isStructEnumOrUnion() && type.size > 64)
hppFile.add(fmt::format("sc_bv<{}>({});\n", type.size, name));
else
hppFile.add(fmt::format("{};\n", name));
Expand Down Expand Up @@ -241,7 +241,7 @@ void SvStruct::toCpp(HppFile& hppFile, const std::string_view _namespace, const

//* STATIC GET FUNCTIONS *//
for (const auto& [name, type] : members) {
if (type.isStructOrEnum() && _namespace != type._namespace) {
if (type.isStructEnumOrUnion() && _namespace != type._namespace) {
hppFile.addWithIndent(fmt::format("static {}::{} get_{} (const {}& __data) {{\n",
type._namespace, type.toString(),
name, cppTypeStr));
Expand All @@ -266,7 +266,7 @@ void SvStruct::toCpp(HppFile& hppFile, const std::string_view _namespace, const
type.size);
}

if (type.isStructOrEnum())
if (type.isStructEnumOrUnion())
if (_namespace != type._namespace)
hppFile.addWithIndent(fmt::format("return {}::{}({});\n", type._namespace,
type.toString(), value));
Expand Down
9 changes: 6 additions & 3 deletions tools/reflect/src/SvType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ SvType::SvType(const Type& type) {
cppType = CppType::fromSize(size);
else if (type.isEnum())
cppType = CppType::ENUM;
else if (type.isStruct() || type.isUnpackedStruct())
else if (type.isStruct())
cppType = CppType::STRUCT;
else if (type.isUnion())
cppType = CppType::UNION;
else
SLANG_UNREACHABLE;

if (this->isEnum() || this->isStruct())
if (this->isStructEnumOrUnion())
_namespace = type.getParentScope()->asSymbol().name;
}

Expand All @@ -34,7 +36,7 @@ std::string SvType::toString() const {
std::stringstream ss;
if (cppType == CppType::SC_BV)
ss << format(fmt::runtime(CppType::toString(cppType)), size);
else if (this->isEnum() || this->isStruct())
else if (this->isStructEnumOrUnion())
ss << format(fmt::runtime(CppType::toString(cppType)), name);
else
ss << CppType::toString(cppType);
Expand All @@ -52,6 +54,7 @@ std::string toString(const Type& cppType) {
case SC_BV: return "sc_bv<{}>";
case STRUCT: return "{}";
case ENUM: return "{}";
case UNION: return "{}";
}
// clang-format on
SLANG_UNREACHABLE;
Expand Down
23 changes: 15 additions & 8 deletions tools/reflect/src/SvTypeReflector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "SvGeneric.h"
#include "SvLocalParam.h"
#include "SvStruct.h"
#include "SvUnion.h"
#include "fmt/color.h"

#include "slang/util/OS.h"
Expand Down Expand Up @@ -44,6 +45,9 @@ void SvTypeReflector::reflect() {
std::make_unique<SvStruct>(type));
else if (type.isEnum())
namespaces[getNamespace(type)].members.emplace_back(std::make_unique<SvEnum>(type));
else if (type.isUnion())
namespaces[getNamespace(type)].members.emplace_back(
std::make_unique<SvUnion>(type));
if (verbose)
OS::print(fg(fmt::color::yellow_green),
fmt::format("Detected {} as public\n", type.name));
Expand All @@ -63,26 +67,29 @@ void SvTypeReflector::reflect() {
}
}));

for (const auto& [name, members] : namespaces) {
if (members.members.empty())
for (const auto& [namespaceName, namespaceMembers] : namespaces) {
if (namespaceMembers.members.empty())
continue;

//** NAMESPACE DECLARATION **//
auto& hpp = cppEmitter.newNamespace(name);
hpp.add(fmt::format("namespace {} {{\n", name));
auto& hpp = cppEmitter.newNamespace(namespaceName);
hpp.add(fmt::format("namespace {} {{\n", namespaceName));
hpp.increaseIndent();

//** NAMESPACE MEMBERS DECLARATION **//
for (const auto& generic : members.members) {
for (const auto& generic : namespaceMembers.members) {
if (generic->isStruct())
reinterpret_cast<SvStruct*>(generic.get())
->toCpp(hpp, name, members.aliases, noSystemC);
->toCpp(hpp, namespaceName, namespaceMembers.aliases, noSystemC);
else if (generic->isEnum())
reinterpret_cast<SvEnum*>(generic.get())
->toCpp(hpp, name, members.aliases, noSystemC);
->toCpp(hpp, namespaceName, namespaceMembers.aliases, noSystemC);
else if (generic->isUnion())
reinterpret_cast<SvUnion*>(generic.get())
->toCpp(hpp, namespaceName, namespaceMembers.aliases, noSystemC);
else if (generic->isLocalParam())
reinterpret_cast<SvLocalParam*>(generic.get())
->toCpp(hpp, name, members.aliases, noSystemC);
->toCpp(hpp, namespaceName, namespaceMembers.aliases, noSystemC);
}

hpp.decreaseIndent();
Expand Down
Loading

0 comments on commit 26f66eb

Please sign in to comment.