Skip to content

File instr_load.h

File List > projects > simtix > src > simtix > sm > instr_load.h

Go to the documentation of this file

#pragma once

#include <simtix/mem.h>

#include <cstdint>
#include <utility>
#include <vector>

#include "sm/fu/lsu/base.h"
#include "sm/instr.h"
#include "sm/thread.h"
#include "sm/warp.h"

namespace simtix {

class InstrLoad : public Instr {
 public:
  using Op = void (InstrLoad::*)();

  // Implementation of Instr's virtual methods
  void Decode() override;

  void Issue() override;

  void OperandCollect() override;

  void Execute() override;

  void Commit() override;

  void Reset() override;

  void Assign(const Instr *other) override;

  bool CanIssue() const override;

  bool CanExecute() const override { return rs1_data_ready_; }

  bool CanCommit() const override {
    return executed_ && num_pending_load_ == 0;
  }

  bool CanRetire() const override { return committed_; }

  bool exception_valid() const override { return illegal_ || access_fault_; }

 protected:
  // Protected constructor
  InstrLoad(Warp *warp, uint32_t iword, uint64_t wpc);

  void Reinitialize(Warp *warp, uint32_t iword, uint64_t wpc) override;

  // Operation
  Op op_ = nullptr;

  bool rs1_data_ready_ = false;
  bool executed_ = false;
  bool committed_ = false;

  int64_t imm_ = 0;

  std::vector<int64_t> rs1_data_;
  std::vector<int64_t> rd_data_;
  std::vector<uint8_t> buf_;
  uint32_t num_pending_load_ = 0;
  bool access_fault_ = false;
  uint64_t fault_addr_ = 0;

  template <std::size_t N, bool Sext>
  inline void VectorLoad() {
    num_pending_load_ = active_threads_.size();
    for (Thread *t : active_threads_) {
      uint32_t tid = t->tid();
      // Address calculation.
      uint64_t addr = rs1_data_[tid] + imm_;

      // Use tid to compute the buffer index.
      uint8_t *bytes = buf_.data() + tid * 8;

      auto on_resp = [tid, addr, bytes, this](auto status) -> bool {
        // Ignore all responses if this load instruction has all requests
        // finished.
        if (num_pending_load_ == 0) {
          return true;
        }

        // If the response status is not okay, halt all pending loads by
        // reseting num_pending_load_.
        if (status != mem::MemoryInterface::RespStatus::kOkay) {
          access_fault_ = true;
          fault_addr_ = addr;
          num_pending_load_ = 0;
          return true;
        }

        // Assemble bytes into a 64-bit int.
        int64_t tmp = 0;
        for (int j = N - 1; j >= 0; --j) {
          tmp = tmp << 8;
          tmp |= bytes[j];
        }

        // Do sign extension if needed.
        if constexpr (Sext) {
          if ((bytes[N - 1] & 0x80) != 0) {
            constexpr int shamt = 64 - 8 * N;
            tmp = tmp << shamt;
            tmp = tmp >> shamt;
          }
        }

        --num_pending_load_;
        rd_data_[tid] = tmp;
        return true;
      };

      MemoryInterface::Payload payload = {.id = {.wid = warp_->wid()},
                                          .addr = addr,
                                          .data = bytes,
                                          .strb = nullptr,
                                          .size = N};

      // Read the data in the address of rs1_data from memory.
      warp_->lsu()->PushReadRequest(tid, payload, std::move(on_resp));
    }
  }

 private:
  // Operations defined in this Opcode
  void ld_() { VectorLoad<8, true>(); }

  void lw_() { VectorLoad<4, true>(); }

  void lwu_() { VectorLoad<4, false>(); }

  void lh_() { VectorLoad<2, true>(); }

  void lhu_() { VectorLoad<2, false>(); }

  void lb_() { VectorLoad<1, true>(); }

  void lbu_() { VectorLoad<1, false>(); }

  void nop_() {}

  // Make InstrPool friend so that they can call our protected constructor
  template <class InstrType>
  friend class InstrPool;
};

}  // namespace simtix