Skip to content

Commit

Permalink
refactor: move TLB management to interpret.cpp
Browse files Browse the repository at this point in the history
  • Loading branch information
diegonehab committed Jan 15, 2025
1 parent b142dbf commit 1e69b2e
Show file tree
Hide file tree
Showing 26 changed files with 1,386 additions and 1,074 deletions.
11 changes: 6 additions & 5 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -362,20 +362,21 @@ LIBCARTESI_OBJS:= \
base64.o \
interpret.o \
virtual-machine.o \
uarch-machine.o \
uarch-step.o \
uarch-reset-state.o \
sha3.o \
machine-merkle-tree.o \
pristine-merkle-tree.o \
uarch-interpret.o \
machine-c-api.o \
uarch-pristine-ram.o \
uarch-pristine-state-hash.o \
uarch-pristine-hash.o \
send-cmio-response.o \
replay-step-state-access-interop.o

#send-cmio-response.o \
#uarch-machine.o \
#uarch-interpret.o \
#uarch-step.o \
#uarch-reset-state.o \
CARTESI_CLUA_OBJS:= \
clua.o \
clua-i-virtual-machine.o \
Expand Down
17 changes: 15 additions & 2 deletions src/find-pma-entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ namespace cartesi {
/// \tparam STATE_ACCESS Class of machine state accessor object.
/// \param a Machine state accessor object.
/// \param paddr Target physical address of word.
/// \param index Receives index where PMA entry was found.
/// \returns PMA entry where word falls, or empty sentinel.
template <typename T, typename STATE_ACCESS>
auto &find_pma_entry(STATE_ACCESS &a, uint64_t paddr) {
uint64_t index = 0;
auto &find_pma_entry(STATE_ACCESS &a, uint64_t paddr, uint64_t &index) {
index = 0;
while (true) {
auto &pma = a.read_pma_entry(index);
const auto length = pma.get_length();
Expand All @@ -53,6 +54,18 @@ auto &find_pma_entry(STATE_ACCESS &a, uint64_t paddr) {
}
}

/// \brief Returns PMAs entry where a word falls.
/// \tparam T uint8_t, uint16_t, uint32_t, or uint64_t.
/// \tparam STATE_ACCESS Class of machine state accessor object.
/// \param a Machine state accessor object.
/// \param paddr Target physical address of word.
/// \returns PMA entry where word falls, or empty sentinel.
template <typename T, typename STATE_ACCESS>
FORCE_INLINE auto &find_pma_entry(STATE_ACCESS &a, uint64_t paddr) {
uint64_t index = 0;
return find_pma_entry<T>(a, paddr, index);
}

} // namespace cartesi

#endif // FIND_PMA_ENTRY_H
199 changes: 107 additions & 92 deletions src/i-state-access.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,25 @@
#include <utility>

#include "meta.h"
#include "shadow-tlb.h"
#include "tlb.h"

namespace cartesi {

// Forward declarations
enum class bracket_type;

// Type trait that should return the pma_entry type for a state access class
template <typename STATE_ACCESS>
struct i_state_access_pma_entry {};
template <typename STATE_ACCESS>
using i_state_access_pma_entry_t = typename i_state_access_pma_entry<STATE_ACCESS>::type;

// Type trait that should return the fast_addr type for a state access class
template <typename STATE_ACCESS>
struct i_state_access_fast_addr {};
template <typename STATE_ACCESS>
using i_state_access_fast_addr_t = typename i_state_access_fast_addr<STATE_ACCESS>::type;

/// \class i_state_access
/// \brief Interface for machine state access.
/// \details \{
Expand All @@ -53,7 +65,7 @@ enum class bracket_type;
/// Methods are provided to read and write each state component.
/// \}
/// \tparam DERIVED Derived class implementing the interface. (An example of CRTP.)
template <typename DERIVED, typename PMA_ENTRY_TYPE>
template <typename DERIVED>
class i_state_access { // CRTP

/// \brief Returns object cast as the derived class
Expand All @@ -67,6 +79,9 @@ class i_state_access { // CRTP
}

public:
using pma_entry = i_state_access_pma_entry_t<DERIVED>;
using fast_addr = i_state_access_fast_addr_t<DERIVED>;

/// \brief Returns machine state for direct access.
auto &get_naked_state() {
return derived().do_get_naked_state();
Expand Down Expand Up @@ -588,21 +603,9 @@ class i_state_access { // CRTP
return derived().do_read_htif_iyield();
}

/// \brief Poll for external interrupts.
/// \param mcycle Current machine mcycle.
/// \param mcycle_max Maximum mcycle to wait for interrupts.
/// \returns A pair, the first value is the new machine mcycle advanced by the relative elapsed time while
/// polling, the second value is a boolean that is true when the poll is stopped due do an external interrupt
/// request.
/// \details When mcycle_max is greater than mcycle, this function will sleep until an external interrupt
/// is triggered or mcycle_max relative elapsed time is reached.
std::pair<uint64_t, bool> poll_external_interrupts(uint64_t mcycle, uint64_t mcycle_max) {
return derived().do_poll_external_interrupts(mcycle, mcycle_max);
}

/// \brief Reads PMA entry at a given index.
/// \param index Index of PMA
PMA_ENTRY_TYPE &read_pma_entry(uint64_t index) {
pma_entry &read_pma_entry(uint64_t index) {
return derived().do_read_pma_entry(index);
}

Expand All @@ -628,120 +631,132 @@ class i_state_access { // CRTP
return derived().do_write_memory(paddr, data, length);
}

/// \brief Write a data buffer to memory padded with 0
/// \param paddr Destination physical address.
/// \param data Pointer to source data buffer.
/// \param data_length Length of data buffer.
/// \param write_length_log2_size Log2 size of the total write length.
void write_memory_with_padding(uint64_t paddr, const unsigned char *data, uint64_t data_length,
int write_length_log2_size) {
return derived().do_write_memory_with_padding(paddr, data, data_length, write_length_log2_size);
}

/// \brief Reads a word from memory.
/// \tparam T Type of word to read.
/// \param paddr Target physical address.
/// \param hpage Pointer to page start in host memory.
/// \param hoffset Offset in page (must be aligned to sizeof(T)).
/// \tparam T Type of word to read, potentially unaligned.
/// \tparam A Type to which \p paddr and \p haddr are known to be aligned.
/// \param faddr Implementation-defined fast address.
/// \param pval Pointer to word receiving value.
template <typename T>
void read_memory_word(uint64_t paddr, const unsigned char *hpage, uint64_t hoffset, T *pval) {
/// \warning T must not cross page boundary starting from \p faddr
/// \warning T may or may not cross a Merkle tree word boundary starting from \p faddr!
template <typename T, typename A = T>
void read_memory_word(fast_addr faddr, uint64_t pma_index, T *pval) {
static_assert(std::is_integral<T>::value && sizeof(T) <= sizeof(uint64_t), "unsupported type");
return derived().template do_read_memory_word<T>(paddr, hpage, hoffset, pval);
return derived().template do_read_memory_word<T, A>(faddr, pma_index, pval);
}

/// \brief Writes a word to memory.
/// \tparam T Type of word to write.
/// \param paddr Target physical address.
/// \param hpage Pointer to page start in host memory.
/// \param hoffset Offset in page (must be aligned to sizeof(T)).
/// \tparam A Type to which \p paddr and \p haddr are known to be aligned.
/// \param faddr Implementation-defined fast address.
/// \param val Value to be written.
template <typename T>
void write_memory_word(uint64_t paddr, unsigned char *hpage, uint64_t hoffset, T val) {
/// \details \p haddr is ONLY valid when there is a host machine.
/// It should never be referenced outside of this context.
/// \warning T must not cross page boundary starting from \p faddr
/// \warning T may or may not cross a Merkle tree word boundary starting from \p faddr!
template <typename T, typename A = T>
void write_memory_word(fast_addr faddr, uint64_t pma_index, T val) {
static_assert(std::is_integral<T>::value && sizeof(T) <= sizeof(uint64_t), "unsupported type");
return derived().template do_write_memory_word<T>(paddr, hpage, hoffset, val);
return derived().template do_write_memory_word<T, A>(faddr, pma_index, val);
}

auto get_host_memory(PMA_ENTRY_TYPE &pma) {
return derived().do_get_host_memory(pma);
template <TLB_set_use USE>
fast_addr read_tlb_vp_offset(uint64_t slot_index) {
return derived().template do_read_tlb_vp_offset<USE>(slot_index);
}

/// \brief Try to translate a virtual address to a host pointer through the TLB.
/// \tparam ETYPE TLB entry type.
/// \tparam T Type of word that would be read with the pointer.
/// \param vaddr Target virtual address.
/// \param phptr Pointer to host pointer receiving value.
/// \returns True if successful (TLB hit), false otherwise.
template <TLB_entry_type ETYPE, typename T>
bool translate_vaddr_via_tlb(uint64_t vaddr, unsigned char **phptr) {
return derived().template do_translate_vaddr_via_tlb<ETYPE, T>(vaddr, phptr);
template <TLB_set_use USE>
void write_tlb_vp_offset(uint64_t slot_index, fast_addr vp_offset) {
return derived().template do_write_tlb_vp_offset<USE>(slot_index, vp_offset);
}

/// \brief Try to read a word from memory through the TLB.
/// \tparam ETYPE TLB entry type.
/// \tparam T Type of word to read.
/// \param vaddr Target virtual address.
/// \param pval Pointer to word receiving value.
/// \returns True if successful (TLB hit), false otherwise.
template <TLB_entry_type ETYPE, typename T>
bool read_memory_word_via_tlb(uint64_t vaddr, T *pval) {
static_assert(std::is_integral<T>::value && sizeof(T) <= sizeof(uint64_t), "unsupported type");
return derived().template do_read_memory_word_via_tlb<ETYPE, T>(vaddr, pval);
template <TLB_set_use USE>
uint64_t read_tlb_vaddr_page(uint64_t slot_index) {
return derived().template do_read_tlb_vaddr_page<USE>(slot_index);
}

/// \brief Try to write a word to memory through the TLB.
/// \tparam ETYPE TLB entry type.
/// \tparam T Type of word to write.
/// \param vaddr Target virtual address.
/// \param val Value to be written.
/// \returns True if successful (TLB hit), false otherwise.
template <TLB_entry_type ETYPE, typename T>
bool write_memory_word_via_tlb(uint64_t vaddr, T val) {
static_assert(std::is_integral<T>::value && sizeof(T) <= sizeof(uint64_t), "unsupported type");
return derived().template do_write_memory_word_via_tlb<ETYPE, T>(vaddr, val);
template <TLB_set_use USE>
void write_tlb_vaddr_page(uint64_t slot_index, uint64_t vaddr_page) {
return derived().template do_write_tlb_vaddr_page<USE>(slot_index, vaddr_page);
}

/// \brief Replaces an entry in the TLB.
/// \tparam ETYPE TLB entry type to replace.
/// \param vaddr Target virtual address.
/// \param paddr Target physical address.
/// \param pma PMA entry for the physical address.
/// \returns Pointer to page start in host memory.
template <TLB_entry_type ETYPE>
unsigned char *replace_tlb_entry(uint64_t vaddr, uint64_t paddr, PMA_ENTRY_TYPE &pma) {
return derived().template do_replace_tlb_entry<ETYPE>(vaddr, paddr, pma);
template <TLB_set_use USE>
uint64_t read_tlb_pma_index(uint64_t slot_index) {
return derived().template do_read_tlb_pma_index<USE>(slot_index);
}

template <TLB_set_use USE>
void write_tlb_pma_index(uint64_t slot_index, uint64_t pma_index) {
return derived().template do_write_tlb_pma_index<USE>(slot_index, pma_index);
}

/// \brief Invalidates all TLB entries of a type.
/// \tparam ETYPE TLB entry type to flush.
template <TLB_entry_type ETYPE>
void flush_tlb_type() {
return derived().template do_flush_tlb_type<ETYPE>();
/// \brief Converts a target physical address to the implementation-defined address
/// \param paddr Target physical address to convert
/// \param pma_index Index of PMA where address falls
/// \returns Correspnding implementation-defined address
fast_addr get_faddr(uint64_t paddr, uint64_t pma_index) const {
return derived().do_get_faddr(paddr, pma_index);
}

/// \brief Invalidates all TLB entries of all types.
void flush_all_tlb() {
derived().template flush_tlb_type<TLB_CODE>();
derived().template flush_tlb_type<TLB_READ>();
derived().template flush_tlb_type<TLB_WRITE>();
/// \brief Marks a page as dirty
/// \param faddr Implementation-defined fast address.
/// \param pma_index Index of PMA where page falls
/// \details When there is a host machine, the Merkle tree only updates the hashes for pages that
/// have been modified. Pages can only be written to if they appear in the write TLB. Therefore,
/// the Merkle tree only considers the pages that are currently in the write TLB and those that
/// have been marked dirty. When a page leaves the write TLB, it is marked dirty.
/// If the state belongs to a host machine, then this call MUST be forwarded to machine::mark_dirty_page();
void mark_dirty_page(fast_addr faddr, uint64_t pma_index) {
return derived().do_mark_dirty_page(faddr, pma_index);
}

/// \brief Invalidates TLB entries for a specific virtual address.
/// \param vaddr Target virtual address.
void flush_tlb_vaddr(uint64_t vaddr) {
return derived().do_flush_tlb_vaddr(vaddr);
// ---
// These methods ONLY need to be implemented when state belongs to non-reproducible host machine
// ---

/// \brief Poll for external interrupts.
/// \param mcycle Current machine mcycle.
/// \param mcycle_max Maximum mcycle to wait for interrupts.
/// \returns A pair, the first value is the new machine mcycle advanced by the relative elapsed time while
/// polling, the second value is a boolean that is true when the poll is stopped due do an external interrupt
/// request.
/// \details When mcycle_max is greater than mcycle, this function will sleep until an external interrupt
/// is triggered or mcycle_max relative elapsed time is reached.
std::pair<uint64_t, bool> poll_external_interrupts(uint64_t mcycle, uint64_t mcycle_max) {
return derived().do_poll_external_interrupts(mcycle, mcycle_max);
}

/// \brief Returns true if soft yield HINT instruction is enabled at runtime
bool get_soft_yield() {
return derived().do_get_soft_yield();
}

/// \brief Write a data buffer to memory padded with 0
/// \param paddr Destination physical address.
/// \param data Pointer to source data buffer.
/// \param data_length Length of data buffer.
/// \param write_length_log2_size Log2 size of the total write length.
void write_memory_with_padding(uint64_t paddr, const unsigned char *data, uint64_t data_length,
int write_length_log2_size) {
return derived().do_write_memory_with_padding(paddr, data, data_length, write_length_log2_size);
}

#ifdef DUMP_COUNTERS
auto &get_statistics() {
return derived().do_get_statistics();
}
#endif

protected:
/// \brief Default implementation when state does not belong to non-reproducible host machine
bool do_get_soft_yield() { // NOLINT(readability-convert-member-functions-to-static)
return false;
}

/// \brief Default implementation when state does not belong to non-reproducible host machine
std::pair<uint64_t, bool> do_poll_external_interrupts(uint64_t mcycle,
uint64_t /* mcycle_max */) { // NOLINT(readability-convert-member-functions-to-static)
return {mcycle, false};
}
};

/// \brief SFINAE test implementation of the i_state_access interface
Expand Down
Loading

0 comments on commit 1e69b2e

Please sign in to comment.