// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#ifndef OPENTITAN_HW_DV_VERILATOR_CPP_SV_SCOPED_H_
#define OPENTITAN_HW_DV_VERILATOR_CPP_SV_SCOPED_H_

#include <cassert>
#include <stdexcept>
#include <string>
#include <svdpi.h>

/**
 * Wrapper and guard class for SV Scope
 *
 * Call the constructor with a string for the scope that should be used. If
 * this string starts with '.', it is taken to be a name relative to the
 * current scope. Multiple periods at the start of the string move up in the
 * tree (like Python imports).
 *
 * If the resolved scope cannot be found, the constructor leaves the current
 * scope unchanged and throws an SVScoped::Error detailing the non-existent
 * scope it tried to use.
 *
 * For example, if the current scope has name "TOP.foo.bar", then the string
 * ".baz" resolves to the scope with name "TOP.foo.bar.baz". The string "..baz"
 * resolves to the scope with name "TOP.foo.baz". The string "qux" resolves to
 * the scope with name "qux".
 *
 * This guard restores the previous scope at destruction.
 */
class SVScoped {
 public:
  SVScoped(const std::string &name);
  ~SVScoped() { svSetScope(prev_scope_); }

  class Error : public std::exception {
   public:
    Error(const std::string &scope_name);
    const char *what() const noexcept override { return msg_.c_str(); }

    std::string scope_name_;

   private:
    std::string msg_;
  };

  // helper function to join two, possibly relative, scopes correctly.
  static std::string join_sv_scopes(const std::string &a, const std::string &b);

 private:
  svScope prev_scope_;
};

#endif  // OPENTITAN_HW_DV_VERILATOR_CPP_SV_SCOPED_H_
