blob: d60e82700dc7fcc834a872b898141bdfd096fd29 [file] [log] [blame]
#ifndef _UHDM_AST_H_
#define _UHDM_AST_H_ 1
#include "frontends/ast/ast.h"
#include <vector>
#undef cover
#include "uhdmastshared.h"
#include <memory>
#include <uhdm/uhdm.h>
namespace systemverilog_plugin
{
class AstNodeBuilder;
class UhdmAst
{
private:
// Logging method for exclusive use of `uhdmast_assert` macro.
void uhdmast_assert_log(const char *expr_str, const char *func, const char *file, int line) const;
// Walks through one-to-many relationships from given parent
// node through the VPI interface, visiting child nodes belonging to
// ChildrenNodeTypes that are present in the given object.
void visit_one_to_many(const std::vector<int> child_node_types, vpiHandle parent_handle, const std::function<void(::Yosys::AST::AstNode *)> &f);
// Walks through one-to-one relationships from given parent
// node through the VPI interface, visiting child nodes belonging to
// ChildrenNodeTypes that are present in the given object.
void visit_one_to_one(const std::vector<int> child_node_types, vpiHandle parent_handle, const std::function<void(::Yosys::AST::AstNode *)> &f);
// Visit children of type vpiRange that belong to the given parent node.
void visit_range(vpiHandle obj_h, const std::function<void(::Yosys::AST::AstNode *)> &f);
// Visit the default expression assigned to a variable.
void visit_default_expr(vpiHandle obj_h);
// Reads location info (start/end line/column numbers, file name) from `obj_h` and sets them on `target_node`.
void apply_location_from_current_obj(::Yosys::AST::AstNode &target_node) const;
// Reads object name from `obj_h` and assigns it to `target_node`.
void apply_name_from_current_obj(::Yosys::AST::AstNode &target_node) const;
// Creates node of specified `type` with location properties read from `obj_h`.
AstNodeBuilder make_node(::Yosys::AST::AstNodeType type) const;
// Creates node of specified `type` with location properties and name read from `obj_h`.
AstNodeBuilder make_named_node(::Yosys::AST::AstNodeType type) const;
// Creates AST_IDENTIFIER node with specified `id` and location properties read from `obj_h`.
AstNodeBuilder make_ident(std::string id) const;
// Creates signed AST_CONSTANT node with specified `value` and location properties read from `obj_h`.
AstNodeBuilder make_const(int32_t value, uint8_t width = 32) const;
// Creates unsigned AST_CONSTANT node with specified `value` and location properties read from `obj_h`.
AstNodeBuilder make_const(uint32_t value, uint8_t width = 32) const;
// Create an AstNode of the specified type with metadata extracted from
// the given vpiHandle.
// OBSOLETE: use `make_node` or `make_named_node` instead.
::Yosys::AST::AstNode *make_ast_node(::Yosys::AST::AstNodeType type, std::vector<::Yosys::AST::AstNode *> children = {});
// Create an identifier AstNode
// OBSOLETE: use `make_ident` instead.
::Yosys::AST::AstNode *make_identifier(std::string name);
// Makes the passed node a cell node of the specified type
void make_cell(vpiHandle obj_h, ::Yosys::AST::AstNode *node, ::Yosys::AST::AstNode *type);
// Moves a type node to the specified node
void move_type_to_new_typedef(::Yosys::AST::AstNode *current_node, ::Yosys::AST::AstNode *type_node);
// Go up the UhdmAst to find a parent node of the specified type
::Yosys::AST::AstNode *find_ancestor(const std::unordered_set<::Yosys::AST::AstNodeType> &types);
// Reports that something went wrong with reading the UHDM file
void report_error(const char *format, ...) const;
// Processes the value connected to the specified node
::Yosys::AST::AstNode *process_value(vpiHandle obj_h);
// Transforms break and continue nodes into structures accepted by the AST frontend
void transform_breaks_continues(::Yosys::AST::AstNode *loop, ::Yosys::AST::AstNode *decl_block);
// The parent UhdmAst
UhdmAst *parent;
// Data shared between all UhdmAst objects
UhdmAstShared &shared;
// The current VPI/UHDM handle
vpiHandle obj_h = 0;
// The current Yosys AST node
::Yosys::AST::AstNode *current_node = nullptr;
// Indentation used for debug printing
std::string indent;
// Mapping of names that should be replaced to new names
std::unordered_map<std::string, std::string> node_renames;
// Functions that process specific types of nodes
void process_design();
void process_parameter();
void process_port();
void process_module();
void process_struct_typespec();
void process_union_typespec();
void process_packed_array_typespec();
void process_array_typespec();
void process_typespec_member();
void process_enum_typespec();
void process_enum_const();
void process_custom_var();
void process_int_var();
void process_real_var();
void process_array_var();
void process_packed_array_var();
void process_param_assign();
void process_cont_assign();
void process_cont_assign_net();
void process_cont_assign_var_init();
void process_assignment(const UHDM::BaseClass *object);
void process_net();
void process_packed_array_net();
void process_array_net(const UHDM::BaseClass *object);
void process_package();
void process_interface();
void process_modport();
void process_io_decl();
void process_always();
void process_event_control(const UHDM::BaseClass *object);
void process_initial();
void process_begin(bool is_named);
void process_operation(const UHDM::BaseClass *object);
void process_stream_op();
void process_list_op();
void process_cast_op();
void process_inside_op();
void process_assignment_pattern_op();
void process_tagged_pattern();
void process_bit_select();
void process_part_select();
void process_indexed_part_select();
void process_var_select();
void process_if_else();
void process_for();
void process_gen_scope_array();
void process_gen_scope();
void process_case();
void process_case_item();
void process_range(const UHDM::BaseClass *object);
void process_return();
void process_function();
void process_logic_var();
void process_sys_func_call();
// use for task calls and function calls
void process_tf_call(::Yosys::AST::AstNodeType type);
void process_immediate_assert();
void process_hier_path();
void process_logic_typespec();
void process_int_typespec();
void process_shortint_typespec();
void process_longint_typespec();
void process_time_typespec();
void process_bit_typespec();
void process_string_var();
void process_string_typespec();
void process_repeat();
void process_byte_var();
void process_byte_typespec();
void process_long_int_var();
void process_immediate_cover();
void process_immediate_assume();
void process_while();
void process_gate();
void process_primterm();
void process_type_parameter();
void simplify_parameter(::Yosys::AST::AstNode *parameter, ::Yosys::AST::AstNode *module_node = nullptr);
void process_unsupported_stmt(const UHDM::BaseClass *object, bool is_error = true);
UhdmAst(UhdmAst *p, UhdmAstShared &s, const std::string &i) : parent(p), shared(s), indent(i)
{
if (parent)
node_renames = parent->node_renames;
}
public:
UhdmAst(UhdmAstShared &s, const std::string &i = "") : UhdmAst(nullptr, s, i) {}
// Visits single VPI object and creates proper AST node
::Yosys::AST::AstNode *process_object(vpiHandle obj_h);
// Visits all VPI design objects and returns created ASTs
::Yosys::AST::AstNode *visit_designs(const std::vector<vpiHandle> &designs);
static const ::Yosys::IdString &partial();
static const ::Yosys::IdString &packed_ranges();
static const ::Yosys::IdString &unpacked_ranges();
// set this attribute to force conversion of multirange wire to single range. It is useful to force-convert some memories.
static const ::Yosys::IdString &force_convert();
static const ::Yosys::IdString &is_imported();
static const ::Yosys::IdString &is_simplified_wire();
static const ::Yosys::IdString &low_high_bound();
static const ::Yosys::IdString &is_elaborated_module();
};
// Utility for building AstNode trees.
//
// The object members that set AstNode properties return rvalue reference to *this (i.e. to the builder object), so they can be chained.
// The children list is set using call operator (`builder_object({child0, child1, ...})`).
// Build finalization is done through cast operator to either `AstNode*` or `std::unique_ptr<AstNode>`.
//
// Usage example:
//
// 1. Define one or more factory functions for creating base AstNode object:
//
// const auto make_node = [](AST::AstNodeType type) {
// auto node = std::make_unique<AST::AstNode>(type);
// // ...initialize the node if needed...
// return AstNodeBuilder(std::move(node));
// };
//
// 2. Use the factories to create a tree:
//
// // AST::AstNode *const variable_node = ...
// // AST::AstNode *const value_node = ...
// AST::AstNode *const assign = //
// (make_node(AST::AST_ASSIGN_EQ))({
// (make_node(AST::AST_IDENTIFIER).str(variable_node->str)),
// (make_node(Yosys::AST::AST_ADD))({
// (make_node(AST::AST_IDENTIFIER).str(value_node->str)),
// (make_node(AST::AST_CONSTANT).value(4)),
// }),
// });
//
// In the real code instead of custom factories illustrated in point 1 above, you probably should use predefined methods from `UhdmAst` class.
// The syntax above puts the factory call and all its method calls (but not the function call operator with the children list) in `()`. This is done
// to make `clang-format` format the code as presented. Otherwise it is heavily wrapped and a lot less readable. `()` are technically not required
// in leafs to make them format as expected, but its nice to use them for consistency.
class AstNodeBuilder
{
using AstNode = ::Yosys::AST::AstNode;
using AstNodeType = ::Yosys::AST::AstNodeType;
std::unique_ptr<AstNode> node;
public:
explicit AstNodeBuilder(AstNodeType node_type) : node(new AstNode(node_type)) {}
explicit AstNodeBuilder(std::unique_ptr<AstNode> node) : node(std::move(node)) {}
~AstNodeBuilder() { log_assert(node == nullptr); }
AstNodeBuilder(AstNodeBuilder &&) = default;
AstNodeBuilder() = delete;
AstNodeBuilder(const AstNodeBuilder &) = delete;
AstNodeBuilder &operator=(const AstNodeBuilder &) = delete;
AstNodeBuilder &operator=(AstNodeBuilder &&) = delete;
// Property setters
// Sets `AstNode::children` vector
AstNodeBuilder &&operator()(std::vector<AstNode *> children) { return node->children = std::move(children), std::move(*this); }
// Sets `AstNode::str` value.
AstNodeBuilder &&str(std::string s) { return node->str = std::move(s), std::move(*this); }
// Sets `AstNode::integer` value.
AstNodeBuilder &&integer(uint32_t v) { return node->integer = v, std::move(*this); }
// Sets `AstNode::is_signed` value.
AstNodeBuilder &&is_signed(bool v) { return node->is_signed = v, std::move(*this); }
// Sets `AstNode::is_reg` value.
AstNodeBuilder &&is_reg(bool v) { return node->is_reg = v, std::move(*this); }
// Sets `AstNode::range_valid`.
AstNodeBuilder &&range_valid(bool v) { return node->range_valid = v, std::move(*this); }
// Convenience range setters
// Sets `AstNode::range_left`, `AstNode::range_right`, `AstNode::range_valid`.
AstNodeBuilder &&range(bool v, int left = -1, int right = 0)
{
node->range_valid = v;
node->range_left = left;
node->range_right = right;
return std::move(*this);
}
// Sets `AstNode::range_left`, `AstNode::range_right`, `AstNode::range_valid = true`.
AstNodeBuilder &&range(int left, int right) { return range(true, left, right); }
// Convenience value setters, mainly for constants.
// Sets node's value.
// Sets: `AstNode::integer`, `AstNode::is_signed`, `AstNode::bits`.
AstNodeBuilder &&value(uint32_t v, bool is_signed, int width = 32)
{
log_assert(width >= 0);
node->integer = v;
node->is_signed = is_signed;
// `AstNode::mkconst_int` does this too.
for (int i = 0; i < width; i++) {
node->bits.push_back((v & 1) ? Yosys::RTLIL::State::S1 : Yosys::RTLIL::State::S0);
v = v >> 1;
}
range(width - 1, 0);
return std::move(*this);
}
// Sets node's value to signed 32 bit integer.
// Sets: `AstNode::integer`, `AstNode::is_signed`, `AstNode::bits`.
AstNodeBuilder &&value(int32_t v) { return value(v, true); }
// Sets node's value to unsigned 32 bit integer.
// Sets: `AstNode::integer`, `AstNode::is_signed`, `AstNode::bits`.
AstNodeBuilder &&value(uint32_t v) { return value(v, false); }
// Type-cast operators used for building.
operator AstNode *() { return node.release(); }
operator std::unique_ptr<AstNode>() { return std::move(node); }
};
} // namespace systemverilog_plugin
#endif