adds WT cache functionality as mixin

This commit is contained in:
Eyck Jentzsch 2023-04-27 12:20:30 +02:00
parent 00b0f101ac
commit 1672b01e62
4 changed files with 215 additions and 12 deletions

View File

@ -422,6 +422,15 @@ protected:
feature_config cfg; feature_config cfg;
unsigned mcause_max_irq{(FEAT&features_e::FEAT_CLIC)?4096:16}; unsigned mcause_max_irq{(FEAT&features_e::FEAT_CLIC)?4096:16};
inline bool debug_mode_active() {return this->reg.PRIV&0x4;} inline bool debug_mode_active() {return this->reg.PRIV&0x4;}
std::pair<std::function<mem_read_f>, std::function<mem_write_f>>
replace_mem_access(std::function<mem_read_f> rd, std::function<mem_write_f> wr){
std::pair<std::function<mem_read_f>, std::function<mem_write_f>> ret{hart_mem_rd_delegate, hart_mem_wr_delegate};
hart_mem_rd_delegate = rd;
hart_mem_wr_delegate = wr;
return ret;
}
std::function<mem_read_f> hart_mem_rd_delegate;
std::function<mem_write_f> hart_mem_wr_delegate;
}; };
template <typename BASE, features_e FEAT> template <typename BASE, features_e FEAT>
@ -543,6 +552,12 @@ riscv_hart_m_p<BASE, FEAT>::riscv_hart_m_p(feature_config cfg)
csr_wr_cb[dcsr] = &this_class::write_dcsr_dcsr; csr_wr_cb[dcsr] = &this_class::write_dcsr_dcsr;
csr_rd_cb[dcsr] = &this_class::read_dcsr_reg; csr_rd_cb[dcsr] = &this_class::read_dcsr_reg;
} }
hart_mem_rd_delegate = [this](phys_addr_t a, unsigned l, uint8_t* const d) -> iss::status {
return this->read_mem(a, l, d);
};
hart_mem_wr_delegate = [this](phys_addr_t a, unsigned l, uint8_t const* const d) -> iss::status {
return this->write_mem(a, l, d);
};
} }
template <typename BASE, features_e FEAT> std::pair<uint64_t, bool> riscv_hart_m_p<BASE, FEAT>::load_file(std::string name, int type) { template <typename BASE, features_e FEAT> std::pair<uint64_t, bool> riscv_hart_m_p<BASE, FEAT>::load_file(std::string name, int type) {
@ -663,9 +678,9 @@ iss::status riscv_hart_m_p<BASE, FEAT>::read(const address_type type, const acce
auto idx = std::distance(std::begin(memfn_range), it); auto idx = std::distance(std::begin(memfn_range), it);
res = memfn_read[idx](phys_addr, length, data); res = memfn_read[idx](phys_addr, length, data);
} else } else
res = read_mem( phys_addr, length, data); res = hart_mem_rd_delegate( phys_addr, length, data);
} else { } else {
res = read_mem( phys_addr, length, data); res = hart_mem_rd_delegate( phys_addr, length, data);
} }
if (unlikely(res != iss::Ok)){ if (unlikely(res != iss::Ok)){
this->trap_state = (1UL << 31) | (5 << 16); // issue trap 5 (load access fault this->trap_state = (1UL << 31) | (5 << 16); // issue trap 5 (load access fault

View File

@ -845,7 +845,7 @@ template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_reg(unsigned
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_cycle(unsigned addr, reg_t &val) { template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_cycle(unsigned addr, reg_t &val) {
auto cycle_val = this->icount + cycle_offset; auto cycle_val = this->icount + cycle_offset;
if (addr == mcycle) { if (addr == mcycle) {
val = static_cast<reg_t>(cycle_val); val = static_cast<reg_t>(cycle_val);
@ -856,7 +856,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_cycle(unsigned a
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_cycle(unsigned addr, reg_t val) { template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_cycle(unsigned addr, reg_t val) {
if (sizeof(typename traits<BASE>::reg_t) != 4) { if (sizeof(typename traits<BASE>::reg_t) != 4) {
if (addr == mcycleh) if (addr == mcycleh)
return iss::Err; return iss::Err;
@ -872,7 +872,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_cycle(unsigned
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_instret(unsigned addr, reg_t &val) { template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_instret(unsigned addr, reg_t &val) {
if ((addr&0xff) == (minstret&0xff)) { if ((addr&0xff) == (minstret&0xff)) {
val = static_cast<reg_t>(this->reg.instret); val = static_cast<reg_t>(this->reg.instret);
} else if ((addr&0xff) == (minstreth&0xff)) { } else if ((addr&0xff) == (minstreth&0xff)) {
@ -882,7 +882,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_instret(unsigned
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_instret(unsigned addr, reg_t val) { template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::write_instret(unsigned addr, reg_t val) {
if (sizeof(typename traits<BASE>::reg_t) != 4) { if (sizeof(typename traits<BASE>::reg_t) != 4) {
if ((addr&0xff) == (minstreth&0xff)) if ((addr&0xff) == (minstreth&0xff))
return iss::Err; return iss::Err;
@ -898,7 +898,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::write_instret(unsigne
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_time(unsigned addr, reg_t &val) { template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_time(unsigned addr, reg_t &val) {
uint64_t time_val = this->icount / (100000000 / 32768 - 1); //-> ~3052; uint64_t time_val = this->icount / (100000000 / 32768 - 1); //-> ~3052;
if (addr == time) { if (addr == time) {
val = static_cast<reg_t>(time_val); val = static_cast<reg_t>(time_val);
@ -909,7 +909,7 @@ template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_time(unsigned ad
return iss::Ok; return iss::Ok;
} }
template <typename BASE> iss::status riscv_hart_m_p<BASE>::read_tvec(unsigned addr, reg_t &val) { template <typename BASE> iss::status riscv_hart_msu_vp<BASE>::read_tvec(unsigned addr, reg_t &val) {
val = csr[addr] & ~2; val = csr[addr] & ~2;
return iss::Ok; return iss::Ok;
} }

View File

@ -449,6 +449,16 @@ protected:
feature_config cfg; feature_config cfg;
unsigned mcause_max_irq{(FEAT&features_e::FEAT_CLIC)?4096:16}; unsigned mcause_max_irq{(FEAT&features_e::FEAT_CLIC)?4096:16};
inline bool debug_mode_active() {return this->reg.PRIV&0x4;} inline bool debug_mode_active() {return this->reg.PRIV&0x4;}
std::pair<std::function<mem_read_f>, std::function<mem_write_f>>
replace_mem_access(std::function<mem_read_f> rd, std::function<mem_write_f> wr){
std::pair<std::function<mem_read_f>, std::function<mem_write_f>> ret{hart_mem_rd_delegate, hart_mem_wr_delegate};
hart_mem_rd_delegate = rd;
hart_mem_wr_delegate = wr;
return ret;
}
std::function<mem_read_f> hart_mem_rd_delegate;
std::function<mem_write_f> hart_mem_wr_delegate;
}; };
template <typename BASE, features_e FEAT> template <typename BASE, features_e FEAT>
@ -612,6 +622,12 @@ riscv_hart_mu_p<BASE, FEAT>::riscv_hart_mu_p(feature_config cfg)
csr_wr_cb[dcsr] = &this_class::write_dcsr_dcsr; csr_wr_cb[dcsr] = &this_class::write_dcsr_dcsr;
csr_rd_cb[dcsr] = &this_class::read_dcsr_reg; csr_rd_cb[dcsr] = &this_class::read_dcsr_reg;
} }
hart_mem_rd_delegate = [this](phys_addr_t a, unsigned l, uint8_t* const d) -> iss::status {
return this->read_mem(a, l, d);
};
hart_mem_wr_delegate = [this](phys_addr_t a, unsigned l, uint8_t const* const d) -> iss::status {
return this->write_mem(a, l, d);
};
} }
template <typename BASE, features_e FEAT> std::pair<uint64_t, bool> riscv_hart_mu_p<BASE, FEAT>::load_file(std::string name, int type) { template <typename BASE, features_e FEAT> std::pair<uint64_t, bool> riscv_hart_mu_p<BASE, FEAT>::load_file(std::string name, int type) {
@ -829,9 +845,9 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::read(const address_type type, const acc
auto idx = std::distance(std::begin(memfn_range), it); auto idx = std::distance(std::begin(memfn_range), it);
res = memfn_read[idx](phys_addr, length, data); res = memfn_read[idx](phys_addr, length, data);
} else } else
res = read_mem( phys_addr, length, data); res = hart_mem_rd_delegate( phys_addr, length, data);
} else { } else {
res = read_mem( phys_addr, length, data); res = hart_mem_rd_delegate( phys_addr, length, data);
} }
if (unlikely(res != iss::Ok)){ if (unlikely(res != iss::Ok)){
this->trap_state = (1UL << 31) | (5 << 16); // issue trap 5 (load access fault this->trap_state = (1UL << 31) | (5 << 16); // issue trap 5 (load access fault
@ -930,9 +946,9 @@ iss::status riscv_hart_mu_p<BASE, FEAT>::write(const address_type type, const ac
auto idx = std::distance(std::begin(memfn_range), it); auto idx = std::distance(std::begin(memfn_range), it);
res = memfn_write[idx]( phys_addr, length, data); res = memfn_write[idx]( phys_addr, length, data);
} else } else
res = write_mem( phys_addr, length, data); res = hart_mem_wr_delegate( phys_addr, length, data);
} else { } else {
res = write_mem( phys_addr, length, data); res = hart_mem_wr_delegate( phys_addr, length, data);
} }
if (unlikely(res != iss::Ok)) { if (unlikely(res != iss::Ok)) {
this->trap_state = (1UL << 31) | (7 << 16); // issue trap 7 (Store/AMO access fault) this->trap_state = (1UL << 31) | (7 << 16); // issue trap 7 (Store/AMO access fault)

172
src/iss/arch/wt_cache.h Normal file
View File

@ -0,0 +1,172 @@
/*******************************************************************************
* Copyright (C) 2023 MINRES Technologies GmbH
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Contributors:
* eyck@minres.com - initial implementation
******************************************************************************/
#ifndef _RISCV_HART_M_P_WT_CACHE_H
#define _RISCV_HART_M_P_WT_CACHE_H
#include <iss/vm_types.h>
#include <util/ities.h>
#include <vector>
#include <map>
#include <memory>
namespace iss {
namespace arch {
namespace cache {
enum class state { INVALID, VALID};
struct line {
uint64_t tag_addr{0};
state st{state::INVALID};
std::vector<uint8_t> data;
line(unsigned line_sz): data(line_sz) {}
};
struct set {
std::vector<line> ways;
set(unsigned ways_count, line const& l): ways(ways_count, l) {}
};
struct cache {
std::vector<set> sets;
cache(unsigned size, unsigned line_sz, unsigned ways) {
line const ref_line{line_sz};
set const ref_set{ways, ref_line};
sets.resize(size/(ways*line_sz), ref_set);
}
};
struct wt_policy {
bool is_cacheline_hit(cache& c );
};
}
// write thru, allocate on read, direct mapped or set-associative with round-robin replacement policy
template <typename BASE> class wt_cache : public BASE {
public:
using base_class = BASE;
using this_class = wt_cache<BASE>;
using reg_t = typename BASE::reg_t;
using mem_read_f = typename BASE::mem_read_f;
using mem_write_f = typename BASE::mem_write_f;
using phys_addr_t = typename BASE::phys_addr_t;
wt_cache();
virtual ~wt_cache() = default;
unsigned size{4096};
unsigned line_sz{32};
unsigned ways{1};
uint64_t io_address{0xf0000000};
uint64_t io_addr_mask{0xf0000000};
protected:
iss::status read_cache(phys_addr_t addr, unsigned, uint8_t *const);
iss::status write_cache(phys_addr_t addr, unsigned, uint8_t const *const);
std::function<mem_read_f> cache_mem_rd_delegate;
std::function<mem_write_f> cache_mem_wr_delegate;
std::unique_ptr<cache::cache> dcache_ptr;
std::unique_ptr<cache::cache> icache_ptr;
size_t get_way_select() {
return 0;
}
};
template<typename BASE>
inline wt_cache<BASE>::wt_cache() {
auto cb = base_class::replace_mem_access(
[this](phys_addr_t a, unsigned l, uint8_t* const d) -> iss::status { return read_cache(a, l,d);},
[this](phys_addr_t a, unsigned l, uint8_t const* const d) -> iss::status { return write_cache(a, l,d);});
cache_mem_rd_delegate = cb.first;
cache_mem_wr_delegate = cb.second;
}
template<typename BASE>
iss::status iss::arch::wt_cache<BASE>::read_cache(phys_addr_t a, unsigned l, uint8_t* const d) {
if(!icache_ptr) {
icache_ptr.reset(new cache::cache(size, line_sz, ways));
dcache_ptr.reset(new cache::cache(size, line_sz, ways));
}
if((a.val&io_addr_mask) != io_address) {
auto set_addr=(a.val&(size-1))>>util::ilog2(line_sz*ways);
auto tag_addr=a.val>>util::ilog2(line_sz);
auto& set = (a.access==access_type::FETCH?icache_ptr:dcache_ptr)->sets[set_addr];
for(auto& cl: set.ways) {
if(cl.st==cache::state::VALID && cl.tag_addr==tag_addr) {
auto start_addr = a.val&(line_sz-1);
for(auto i = 0U; i<l; ++i)
d[i] = cl.data[start_addr+i];
return iss::Ok;
}
}
auto& cl = set.ways[get_way_select()];
phys_addr_t cl_addr{a};
cl_addr.val=tag_addr<<util::ilog2(line_sz);
cache_mem_rd_delegate(cl_addr, line_sz, cl.data.data());
cl.tag_addr=tag_addr;
cl.st=cache::state::VALID;
auto start_addr = a.val&(line_sz-1);
for(auto i = 0U; i<l; ++i)
d[i] = cl.data[start_addr+i];
return iss::Ok;
} else
return cache_mem_rd_delegate(a, l, d);
}
template<typename BASE>
iss::status iss::arch::wt_cache<BASE>::write_cache(phys_addr_t a, unsigned l, const uint8_t* const d) {
if(!dcache_ptr)
dcache_ptr.reset(new cache::cache(size, line_sz, ways));
auto res = cache_mem_wr_delegate(a, l, d);
if(res == iss::Ok && ((a.val&io_addr_mask) != io_address)) {
auto set_addr=(a.val&(size-1))>>util::ilog2(line_sz*ways);
auto tag_addr=a.val>>util::ilog2(line_sz);
auto& set = dcache_ptr->sets[set_addr];
for(auto& cl: set.ways) {
if(cl.st==cache::state::VALID && cl.tag_addr==tag_addr) {
auto start_addr = a.val&(line_sz-1);
for(auto i = 0U; i<l; ++i)
cl.data[start_addr+1] = d[i];
break;
}
}
}
return res;
}
} // namespace arch
} // namespace iss
#endif /* _RISCV_HART_M_P_H */