Skip to content

File sm.h

File List > include > simtix > sm.h

Go to the documentation of this file

#pragma once

#include <simtix/clocked.h>
#include <simtix/mem.h>
#include <simtix/opencl.h>
#include <simtix/param.h>
#include <simtix/statistics.h>
#include <simtix/trace.h>

#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>

namespace simtix {

class BaseSM : public sim::Clocked {
 public:
  enum class Status : uint8_t { kIdle = 0, kRunning, kCompleted, kFault };

  // Fault status, only valid when status_ == kFault
  struct FaultStatus {
    uint32_t ewid;  // Exception wid
    uint64_t mcause;
    uint64_t mepc;
    uint64_t mtval;
  };

  explicit BaseSM(const std::string &name)
      : sim::Clocked(name, sim::kSmTickPri) {}
  virtual ~BaseSM() = default;

  virtual void AttachIMem(mem::MemoryInterface *imem) = 0;

  virtual void AttachDMem(mem::MemoryInterface *dmem) = 0;

  virtual int Process(const opencl::WorkGroup &wg) = 0;

  virtual void Reset() = 0;

  virtual uint32_t cid() const = 0;
  virtual uint64_t mcycle() const = 0;
  virtual const std::vector<uint64_t> &minstrets() const = 0;
  virtual Status status() const = 0;
  virtual FaultStatus fault_status() const = 0;
  const virtual std::shared_ptr<stat::Group> stat() const = 0;
};

class AtomicSM : public BaseSM {
 public:
  explicit AtomicSM(const std::string &name, uint32_t cid,
                    const ArchParam &p = kDefaultArchParam);

  ~AtomicSM();

  void AttachIMem(mem::MemoryInterface *imem) override;
  void AttachDMem(mem::MemoryInterface *dmem) override;
  int Process(const opencl::WorkGroup &wg) override;
  void Reset() override;

  uint32_t cid() const override;
  uint64_t mcycle() const override;
  const std::vector<uint64_t> &minstrets() const override;
  Status status() const override;
  FaultStatus fault_status() const override;
  const std::shared_ptr<stat::Group> stat() const override;

 protected:
  void Tick() override;

  bool HasPendingTasks() override;

  class Impl;
  std::unique_ptr<Impl> impl_;
};

namespace pipelined {
class PipelinedSM : public BaseSM {
 public:
  class ParamBuilder;
  class Param {
   public:
    static ParamBuilder Build();
    friend class ParamBuilder;

    DECL_PARAM_W_GETTER(size_t, kInstrQueueCapacity);
    DECL_PARAM_W_GETTER(size_t, kInstrBufferCapacity);
    DECL_PARAM_W_GETTER(size_t, kOutstandingInstrFetches);
    DECL_PARAM_W_GETTER(size_t, kOutstandingLoadStores);
    DECL_PARAM_W_GETTER(size_t, kOperandCollectBufferSize);
    DECL_PARAM_W_GETTER(size_t, kCommitBufferSize);
    DECL_PARAM_W_GETTER(uint32_t, kFetchWidth);
    DECL_PARAM_W_GETTER(uint32_t, kDecodeWidth);
    DECL_PARAM_W_GETTER(uint32_t, kScheduleWidth);
    DECL_PARAM_W_GETTER(uint32_t, kOperandCollectWidth);
    DECL_PARAM_W_GETTER(uint32_t, kExecuteWidth);
    DECL_PARAM_W_GETTER(uint32_t, kCommitWidth);
    DECL_PARAM_W_GETTER(uint32_t, kMemPorts);
    DECL_PARAM_W_GETTER(size_t, kCoalescingGranularity);
    DECL_PARAM_W_GETTER(uint32_t, kIAluLatency);
    DECL_PARAM_W_GETTER(uint32_t, kIMulLatency);
    DECL_PARAM_W_GETTER(uint32_t, kIDivLatency);
    DECL_PARAM_W_GETTER(bool, kSwizzle);
    DECL_PARAM_W_GETTER(size_t, kSharedPorts);
    DECL_PARAM_W_GETTER(size_t, kReadPorts);
    DECL_PARAM_W_GETTER(size_t, kWritePorts);
    DECL_PARAM_W_GETTER(uint32_t, kRegfileBanks);
    DECL_PARAM_W_GETTER(std::optional<mem::NBHBCache::Param>, kICacheParam);
    DECL_PARAM_W_GETTER(std::optional<mem::NBHBCache::Param>, kDCacheParam);

   private:
    Param()
        : kInstrQueueCapacity_(2),
          kInstrBufferCapacity_(4),
          kOutstandingInstrFetches_(8),
          kOutstandingLoadStores_(8),
          kOperandCollectBufferSize_(4),
          kCommitBufferSize_(4),
          kFetchWidth_(2),
          kDecodeWidth_(2),
          kScheduleWidth_(2),
          kOperandCollectWidth_(2),
          kExecuteWidth_(2),
          kCommitWidth_(2),
          kMemPorts_(4),
          kCoalescingGranularity_(64),
          kIAluLatency_(1),
          kIMulLatency_(2),
          kIDivLatency_(8),
          kSwizzle_(true),
          kSharedPorts_(1),
          kReadPorts_(0),
          kWritePorts_(0),
          kRegfileBanks_(4),
          kICacheParam_(std::nullopt),
          kDCacheParam_(std::nullopt) {}
  };

  class ParamBuilder {
   private:
    Param param_;

   public:
    ParamBuilder &instr_queue_capacity(size_t v) {
      param_.kInstrQueueCapacity_ = v;
      return *this;
    }
    ParamBuilder &instr_buffer_capacity(size_t v) {
      param_.kInstrBufferCapacity_ = v;
      return *this;
    }
    ParamBuilder &outstanding_instr_fetches(size_t v) {
      param_.kOutstandingInstrFetches_ = v;
      return *this;
    }
    ParamBuilder &outstanding_load_stores(size_t v) {
      param_.kOutstandingLoadStores_ = v;
      return *this;
    }
    ParamBuilder &operand_collect_buffer_size(size_t v) {
      param_.kOperandCollectBufferSize_ = v;
      return *this;
    }
    ParamBuilder &commit_buffer_size(size_t v) {
      param_.kCommitBufferSize_ = v;
      return *this;
    }
    ParamBuilder &fetch_width(uint32_t v) {
      param_.kFetchWidth_ = v;
      return *this;
    }
    ParamBuilder &decode_width(uint32_t v) {
      param_.kDecodeWidth_ = v;
      return *this;
    }
    ParamBuilder &schedule_width(uint32_t v) {
      param_.kScheduleWidth_ = v;
      return *this;
    }
    ParamBuilder &operand_collect_width(uint32_t v) {
      param_.kOperandCollectWidth_ = v;
      return *this;
    }
    ParamBuilder &execute_width(uint32_t v) {
      param_.kExecuteWidth_ = v;
      return *this;
    }
    ParamBuilder &commit_width(uint32_t v) {
      param_.kCommitWidth_ = v;
      return *this;
    }
    ParamBuilder &mem_ports(uint32_t v) {
      param_.kMemPorts_ = v;
      return *this;
    }
    ParamBuilder &coalescing_granularity(size_t v) {
      param_.kCoalescingGranularity_ = v;
      return *this;
    }
    ParamBuilder &ialu_latency(uint32_t v) {
      param_.kIAluLatency_ = v;
      return *this;
    }
    ParamBuilder &imul_latency(uint32_t v) {
      param_.kIMulLatency_ = v;
      return *this;
    }
    ParamBuilder &idiv_latency(uint32_t v) {
      param_.kIDivLatency_ = v;
      return *this;
    }
    ParamBuilder &swizzle(bool v) {
      param_.kSwizzle_ = v;
      return *this;
    }
    ParamBuilder &shared_ports(size_t v) {
      param_.kSharedPorts_ = v;
      return *this;
    }
    ParamBuilder &read_ports(size_t v) {
      param_.kReadPorts_ = v;
      return *this;
    }
    ParamBuilder &write_ports(size_t v) {
      param_.kWritePorts_ = v;
      return *this;
    }
    ParamBuilder &regfile_banks(uint32_t v) {
      param_.kRegfileBanks_ = v;
      return *this;
    }
    ParamBuilder &icache_param(std::optional<mem::NBHBCache::Param> v) {
      param_.kICacheParam_ = v;
      return *this;
    }
    ParamBuilder &dcache_param(std::optional<mem::NBHBCache::Param> v) {
      param_.kDCacheParam_ = v;
      return *this;
    }
    operator Param() {
      if (param_.kInstrQueueCapacity_ < param_.kFetchWidth_) {
        DPRINTF(Pipelined,
                "Warning: InstrQueueCapacity(%zu) is less than FetchWidth(%u). "
                "Adjusting InstrQueueCapacity to %u.\n",
                param_.kInstrQueueCapacity_, param_.kFetchWidth_,
                param_.kFetchWidth_);
        param_.kInstrQueueCapacity_ = param_.kFetchWidth_;
      }
      if (param_.kInstrBufferCapacity_ < param_.kFetchWidth_ * 2) {
        DPRINTF(Pipelined,
                "Warning: InstrBufferCapacity(%zu) is less than 2 * "
                "FetchWidth(%u). "
                "Adjusting InstrBufferCapacity to %u.\n",
                param_.kInstrBufferCapacity_, param_.kFetchWidth_,
                param_.kFetchWidth_ * 2);
        param_.kInstrBufferCapacity_ = param_.kFetchWidth_ * 2;
      }
      return param_;
    }
  };

  inline static const Param kDefaultParam = Param::Build();

  explicit PipelinedSM(const std::string &name, uint32_t cid,
                       const ArchParam &p = kDefaultArchParam,
                       const Param &pp = kDefaultParam);

  ~PipelinedSM();

  void AttachICache(std::shared_ptr<mem::CacheInterface> icache);
  void AttachDCache(std::shared_ptr<mem::CacheInterface> dcache);

  void AttachIMem(mem::MemoryInterface *imem) override;
  void AttachDMem(mem::MemoryInterface *dmem) override;
  int Process(const opencl::WorkGroup &wg) override;
  void Reset() override;

  uint32_t cid() const override;
  uint64_t mcycle() const override;
  const std::vector<uint64_t> &minstrets() const override;
  Status status() const override;
  FaultStatus fault_status() const override;
  const std::shared_ptr<stat::Group> stat() const override;

 protected:
  void Tick() override;

  bool HasPendingTasks() override;

  class Impl;
  std::unique_ptr<Impl> impl_;
};

inline PipelinedSM::ParamBuilder PipelinedSM::Param::Build() {
  return PipelinedSM::ParamBuilder();
}

}  // namespace pipelined

}  // namespace simtix

namespace fmt {

// fmt support for BaseSM::Status
template <>
struct formatter<simtix::BaseSM::Status> : formatter<string_view> {
  template <typename FormatContext>
  auto format(simtix::BaseSM::Status s, FormatContext &ctx) const {  // NOLINT
    string_view name = "Unknown";
    switch (s) {
      case simtix::BaseSM::Status::kIdle:
        name = "Idle";
        break;
      case simtix::BaseSM::Status::kRunning:
        name = "Running";
        break;
      case simtix::BaseSM::Status::kCompleted:
        name = "Completed";
        break;
      case simtix::BaseSM::Status::kFault:
        name = "Fault";
        break;
    }
    return formatter<string_view>::format(name, ctx);
  }
};

}  // namespace fmt