--- /dev/null
+#include "cachesim.h"
+#include <cstdlib>
+#include <iostream>
+#include <iomanip>
+
+cache_sim_t::cache_sim_t(size_t _sets, size_t _ways, size_t _linesz, const char* _name)
+ : sets(_sets), ways(_ways), linesz(_linesz), name(_name)
+{
+ init();
+}
+
+static void help()
+{
+ std::cerr << "Cache configurations must be of the form" << std::endl;
+ std::cerr << " sets:ways:blocksize" << std::endl;
+ std::cerr << "where sets, ways, and blocksize are positive integers, with" << std::endl;
+ std::cerr << "sets and blocksize both powers of two and blocksize at least 8." << std::endl;
+ exit(1);
+}
+
+cache_sim_t::cache_sim_t(const char* config, const char* _name)
+ : name(_name)
+{
+ const char* wp = strchr(config, ':');
+ if (!wp++) help();
+ const char* bp = strchr(wp, ':');
+ if (!bp++) help();
+
+ sets = atoi(std::string(config, wp).c_str());
+ ways = atoi(std::string(wp, bp).c_str());
+ linesz = atoi(bp);
+
+ init();
+}
+
+void cache_sim_t::init()
+{
+ if(sets == 0 || (sets & (sets-1)))
+ help();
+ if(linesz < 8 || (linesz & (linesz-1)))
+ help();
+
+ idx_shift = 0;
+ for (size_t x = linesz; x; x >>= 1)
+ idx_shift++;
+
+ tags = new uint64_t[sets*ways]();
+ read_accesses = 0;
+ read_misses = 0;
+ bytes_read = 0;
+ write_accesses = 0;
+ write_misses = 0;
+ bytes_written = 0;
+ writebacks = 0;
+
+ miss_handler = NULL;
+}
+
+cache_sim_t::cache_sim_t(const cache_sim_t& rhs)
+ : sets(rhs.sets), ways(rhs.ways), linesz(rhs.linesz),
+ idx_shift(rhs.idx_shift), name(rhs.name)
+{
+ tags = new uint64_t[sets*ways];
+ memcpy(tags, rhs.tags, sets*ways*sizeof(uint64_t));
+}
+
+cache_sim_t::~cache_sim_t()
+{
+ print_stats();
+ delete [] tags;
+}
+
+void cache_sim_t::print_stats()
+{
+ if(read_accesses + write_accesses == 0)
+ return;
+
+ float mr = 100.0f*(read_misses+write_misses)/(read_accesses+write_accesses);
+
+ std::cout << std::setprecision(3) << std::fixed;
+ std::cout << name << " ";
+ std::cout << "Bytes Read: " << bytes_read << std::endl;
+ std::cout << name << " ";
+ std::cout << "Bytes Written: " << bytes_written << std::endl;
+ std::cout << name << " ";
+ std::cout << "Read Accesses: " << read_accesses << std::endl;
+ std::cout << name << " ";
+ std::cout << "Write Accesses: " << write_accesses << std::endl;
+ std::cout << name << " ";
+ std::cout << "Read Misses: " << read_misses << std::endl;
+ std::cout << name << " ";
+ std::cout << "Write Misses: " << write_misses << std::endl;
+ std::cout << name << " ";
+ std::cout << "Writebacks: " << writebacks << std::endl;
+ std::cout << name << " ";
+ std::cout << "Miss Rate: " << mr << '%' << std::endl;
+}
+
+void cache_sim_t::access(uint64_t addr, size_t bytes, bool store)
+{
+ store ? write_accesses++ : read_accesses++;
+ (store ? bytes_written : bytes_read) += bytes;
+
+ size_t idx = (addr >> idx_shift) & (sets-1);
+ size_t tag = (addr >> idx_shift) | VALID;
+ size_t* set_tags = &tags[idx*ways];
+
+ for(size_t i = 0; i < ways; i++)
+ {
+ if(tag == (set_tags[i] & ~DIRTY)) // hit
+ {
+ if(store)
+ set_tags[i] |= DIRTY;
+ return;
+ }
+ }
+
+ store ? write_misses++ : read_misses++;
+
+ size_t way = lfsr.next() % ways;
+ if((set_tags[way] & (VALID | DIRTY)) == (VALID | DIRTY))
+ {
+ uint64_t dirty_addr = (set_tags[way] & ~(VALID | DIRTY)) << idx_shift;
+ if (miss_handler) miss_handler->access(dirty_addr, linesz, true);
+ writebacks++;
+ }
+ if (miss_handler) miss_handler->access(addr & ~(linesz-1), linesz, false);
+ set_tags[way] = tag | (store ? DIRTY : 0);
+}
--- /dev/null
+#ifndef _RISCV_CACHE_SIM_H
+#define _RISCV_CACHE_SIM_H
+
+#include "memtracer.h"
+#include <cstring>
+#include <string>
+#include <stdint.h>
+
+class lfsr_t
+{
+ public:
+ lfsr_t() : reg(1) {}
+ lfsr_t(const lfsr_t& lfsr) : reg(lfsr.reg) {}
+ uint32_t next() { return reg = (reg>>1)^(-(reg&1) & 0xd0000001); }
+ private:
+ uint32_t reg;
+};
+
+class cache_sim_t
+{
+ public:
+ cache_sim_t(const char* config, const char* name);
+ cache_sim_t(size_t sets, size_t ways, size_t linesz, const char* name);
+ cache_sim_t(const cache_sim_t& rhs);
+ ~cache_sim_t();
+
+ void access(uint64_t addr, size_t bytes, bool store);
+ void print_stats();
+ void set_miss_handler(cache_sim_t* mh) { miss_handler = mh; }
+
+ private:
+ lfsr_t lfsr;
+ cache_sim_t* miss_handler;
+
+ size_t sets;
+ size_t ways;
+ size_t linesz;
+ size_t idx_shift;
+
+ uint64_t* tags;
+
+ uint64_t read_accesses;
+ uint64_t read_misses;
+ uint64_t bytes_read;
+ uint64_t write_accesses;
+ uint64_t write_misses;
+ uint64_t bytes_written;
+ uint64_t writebacks;
+
+ std::string name;
+
+ static const uint64_t VALID = 1ULL << 63;
+ static const uint64_t DIRTY = 1ULL << 62;
+
+ void init();
+};
+
+class unified_cache_sim_t : public memtracer_t
+{
+ public:
+ unified_cache_sim_t(const char* config) : cache(config, "U$") {}
+ bool interested_in_range(size_t begin, size_t end, bool store, bool fetch)
+ {
+ return true;
+ }
+ void trace(uint64_t addr, size_t bytes, bool store, bool fetch)
+ {
+ cache.access(addr, bytes, store);
+ }
+ private:
+ cache_sim_t cache;
+};
+
+class icache_sim_t : public memtracer_t, public cache_sim_t
+{
+ public:
+ icache_sim_t(const char* config) : cache_sim_t(config, "I$") {}
+ bool interested_in_range(size_t begin, size_t end, bool store, bool fetch)
+ {
+ return fetch;
+ }
+ void trace(uint64_t addr, size_t bytes, bool store, bool fetch)
+ {
+ if (fetch) access(addr, bytes, false);
+ }
+};
+
+class dcache_sim_t : public memtracer_t, public cache_sim_t
+{
+ public:
+ dcache_sim_t(const char* config) : cache_sim_t(config, "D$") {}
+ bool interested_in_range(size_t begin, size_t end, bool store, bool fetch)
+ {
+ return !fetch;
+ }
+ void trace(uint64_t addr, size_t bytes, bool store, bool fetch)
+ {
+ if (!fetch) access(addr, bytes, store);
+ }
+};
+
+#endif
+++ /dev/null
-#include "icsim.h"
-#include <stdexcept>
-#include <iostream>
-#include <iomanip>
-
-icsim_t::icsim_t(size_t _sets, size_t _ways, size_t _linesz, const char* _name)
- : sets(_sets), ways(_ways), linesz(_linesz), idx_mask(_sets-1), name(_name)
-{
- if(sets == 0 || (sets & (sets-1)))
- throw std::logic_error("sets not a power of 2");
- if(linesz == 0 || (linesz & (linesz-1)))
- throw std::logic_error("linesz not a power of 2");
-
- idx_shift = 0;
- while(_linesz >>= 1)
- idx_shift++;
-
- tags = new uint64_t[sets*ways];
- memset(tags, 0, sets*ways*sizeof(uint64_t));
-
- read_accesses = 0;
- read_misses = 0;
- bytes_read = 0;
- write_accesses = 0;
- write_misses = 0;
- bytes_written = 0;
- writebacks = 0;
-}
-
-icsim_t::icsim_t(const icsim_t& rhs)
- : sets(rhs.sets), ways(rhs.ways), linesz(rhs.linesz),
- idx_shift(rhs.idx_shift), idx_mask(rhs.idx_mask), name(rhs.name)
-{
- tags = new uint64_t[sets*ways];
- memcpy(tags, rhs.tags, sets*ways*sizeof(uint64_t));
-}
-
-icsim_t::~icsim_t()
-{
- delete [] tags;
-}
-
-void icsim_t::print_stats()
-{
- if(read_accesses + write_accesses == 0)
- return;
-
- float mr = 100.0f*(read_misses+write_misses)/(read_accesses+write_accesses);
-
- std::cout << std::setprecision(3) << std::fixed;
- std::cout << name << " ";
- std::cout << "Bytes Read: " << bytes_read << std::endl;
- std::cout << name << " ";
- std::cout << "Bytes Written: " << bytes_written << std::endl;
- std::cout << name << " ";
- std::cout << "Read Accesses: " << read_accesses << std::endl;
- std::cout << name << " ";
- std::cout << "Write Accesses: " << write_accesses << std::endl;
- std::cout << name << " ";
- std::cout << "Read Misses: " << read_misses << std::endl;
- std::cout << name << " ";
- std::cout << "Write Misses: " << write_misses << std::endl;
- std::cout << name << " ";
- std::cout << "Writebacks: " << writebacks << std::endl;
- std::cout << name << " ";
- std::cout << "Miss Rate: " << mr << '%' << std::endl;
-
- float cr = read_accesses == 0 ? 0.0f : 100.0f*bytes_read/(4*read_accesses);
- if(name == "I$")
- {
- std::cout << name << " ";
- std::cout << "RVC compression ratio: " << cr << '%' << std::endl;
- }
-}
-
-void icsim_t::tick(uint64_t pc, int insnlen, bool store)
-{
- store ? write_accesses++ : read_accesses++;
- (store ? bytes_written : bytes_read) += insnlen;
-
- size_t idx = (pc >> idx_shift) & idx_mask;
- size_t tag = (pc >> idx_shift) | VALID;
-
- for(size_t i = 0; i < ways; i++)
- {
- if(tag == (tags[idx + i*sets] & ~DIRTY)) // hit
- {
- if(store)
- tags[idx + i*sets] |= DIRTY;
- return;
- }
- }
-
- store ? write_misses++ : read_misses++;
-
- size_t way = lfsr.next() % ways;
- if((tags[idx + way*sets] & (VALID | DIRTY)) == (VALID | DIRTY))
- writebacks++;
- tags[idx + way*sets] = tag;
-}
+++ /dev/null
-#ifndef _RISCV_ICSIM_H
-#define _RISCV_ICSIM_H
-
-// this file models a simple cache to estimate hit/miss rates.
-// it is currently unused.
-
-#include <cstring>
-#include <string>
-#include <stdint.h>
-
-class lfsr_t
-{
-public:
- lfsr_t() : reg(1) {}
- lfsr_t(const lfsr_t& lfsr) : reg(lfsr.reg) {}
- uint32_t next() { return reg = (reg>>1)^(-(reg&1) & 0xd0000001); }
-private:
- uint32_t reg;
-};
-
-class icsim_t
-{
-public:
- icsim_t(size_t sets, size_t ways, size_t linesz, const char* name);
- icsim_t(const icsim_t& rhs);
- ~icsim_t();
-
- void tick(uint64_t pc, int insnlen, bool store);
- void print_stats();
-private:
- lfsr_t lfsr;
-
- size_t sets;
- size_t ways;
- size_t linesz;
- size_t idx_shift;
- size_t idx_mask;
-
- uint64_t* tags;
-
- uint64_t read_accesses;
- uint64_t read_misses;
- uint64_t bytes_read;
- uint64_t write_accesses;
- uint64_t write_misses;
- uint64_t bytes_written;
- uint64_t writebacks;
-
- std::string name;
-
- static const uint64_t VALID = 1ULL << 63;
- static const uint64_t DIRTY = 1ULL << 62;
-};
-
-#endif
--- /dev/null
+#ifndef _MEMTRACER_H
+#define _MEMTRACER_H
+
+#include <stdint.h>
+#include <string.h>
+#include <vector>
+
+class memtracer_t
+{
+ public:
+ memtracer_t() : link(NULL) {}
+ virtual ~memtracer_t() {}
+
+ virtual bool interested_in_range(uint64_t begin, uint64_t end, bool store, bool fetch) = 0;
+ virtual void trace(uint64_t addr, size_t bytes, bool store, bool fetch) = 0;
+
+ protected:
+
+ private:
+ memtracer_t* link;
+};
+
+class memtracer_list_t : public memtracer_t
+{
+ public:
+ bool interested_in_range(uint64_t begin, uint64_t end, bool store, bool fetch)
+ {
+ for (std::vector<memtracer_t*>::iterator it = list.begin(); it != list.end(); ++it)
+ if ((*it)->interested_in_range(begin, end, store, fetch))
+ return true;
+ return false;
+ }
+ void trace(uint64_t addr, size_t bytes, bool store, bool fetch)
+ {
+ for (std::vector<memtracer_t*>::iterator it = list.begin(); it != list.end(); ++it)
+ (*it)->trace(addr, bytes, store, fetch);
+ }
+ void hook(memtracer_t* h)
+ {
+ list.push_back(h);
+ }
+ private:
+ std::vector<memtracer_t*> list;
+};
+
+#endif
memset(icache_tag, -1, sizeof(icache_tag));
}
-void* mmu_t::refill(reg_t addr, bool store, bool fetch)
+void* mmu_t::refill(reg_t addr, reg_t bytes, bool store, bool fetch)
{
reg_t idx = (addr >> PGSHIFT) % TLB_ENTRIES;
reg_t expected_tag = addr & ~(PGSIZE-1);
throw store ? trap_store_access_fault : trap_load_access_fault;
}
- tlb_load_tag[idx] = (pte_perm & PTE_UR) ? expected_tag : -1;
- tlb_store_tag[idx] = (pte_perm & PTE_UW) ? expected_tag : -1;
- tlb_insn_tag[idx] = (pte_perm & PTE_UX) ? expected_tag : -1;
- tlb_data[idx] = (long)(pte >> PTE_PPN_SHIFT << PGSHIFT) + (long)mem;
+ reg_t pgoff = addr & (PGSIZE-1);
+ reg_t pgbase = pte >> PTE_PPN_SHIFT << PGSHIFT;
+ reg_t paddr = pgbase + pgoff;
- return (void*)(((long)addr & (PGSIZE-1)) + tlb_data[idx]);
+ if (unlikely(tracer.interested_in_range(pgbase, pgbase + PGSIZE, store, fetch)))
+ tracer.trace(paddr, bytes, store, fetch);
+ else
+ {
+ tlb_load_tag[idx] = (pte_perm & PTE_UR) ? expected_tag : -1;
+ tlb_store_tag[idx] = (pte_perm & PTE_UW) ? expected_tag : -1;
+ tlb_insn_tag[idx] = (pte_perm & PTE_UX) ? expected_tag : -1;
+ tlb_data[idx] = (char*)mem + pgbase;
+ }
+
+ return (char*)mem + paddr;
}
pte_t mmu_t::walk(reg_t addr)
return pte;
}
+
+void mmu_t::register_memtracer(memtracer_t* t)
+{
+ flush_tlb();
+ tracer.hook(t);
+}
#include "common.h"
#include "config.h"
#include "processor.h"
+#include "memtracer.h"
+#include <vector>
class processor_t;
badvaddr = addr; \
throw trap_load_address_misaligned; \
} \
- void* paddr = translate(addr, false, false); \
+ void* paddr = translate(addr, sizeof(type##_t), false, false); \
return *(type##_t*)paddr; \
}
badvaddr = addr; \
throw trap_store_address_misaligned; \
} \
- void* paddr = translate(addr, true, false); \
+ void* paddr = translate(addr, sizeof(type##_t), true, false); \
*(type##_t*)paddr = val; \
}
#ifdef RISCV_ENABLE_RVC
if(addr % 4 == 2 && rvc) // fetch across word boundary
{
- void* addr_lo = translate(addr, false, true);
+ void* addr_lo = translate(addr, 2, false, true);
insn.bits = *(uint16_t*)addr_lo;
*func = processor_t::dispatch_table
if(!INSN_IS_RVC(insn.bits))
{
- void* addr_hi = translate(addr+2, false, true);
+ void* addr_hi = translate(addr+2, 2, false, true);
insn.bits |= (uint32_t)*(uint16_t*)addr_hi << 16;
}
}
return data;
// the processor guarantees alignment based upon rvc mode
- void* paddr = translate(addr, false, true);
+ void* paddr = translate(addr, sizeof(insn_t), false, true);
insn = *(insn_t*)paddr;
+ *func = processor_t::dispatch_table
+ [insn.bits % processor_t::DISPATCH_TABLE_SIZE];
- icache_tag[idx] = addr;
- icache_data[idx] = insn;
- icache_func[idx] = *func = processor_t::dispatch_table
- [insn.bits % processor_t::DISPATCH_TABLE_SIZE];
+ if (!tracer.interested_in_range(addr, addr + sizeof(insn_t), false, true))
+ {
+ icache_tag[idx] = addr;
+ icache_data[idx] = insn;
+ icache_func[idx] = *func;
+ }
}
return insn;
void flush_tlb();
void flush_icache();
+ void register_memtracer(memtracer_t*);
+
private:
char* mem;
size_t memsz;
reg_t badvaddr;
-
reg_t ptbr;
bool supervisor;
bool vm_enabled;
+ memtracer_list_t tracer;
// implement a TLB for simulator performance
static const reg_t TLB_ENTRIES = 256;
- long tlb_data[TLB_ENTRIES];
+ char* tlb_data[TLB_ENTRIES];
reg_t tlb_insn_tag[TLB_ENTRIES];
reg_t tlb_load_tag[TLB_ENTRIES];
reg_t tlb_store_tag[TLB_ENTRIES];
reg_t icache_tag[ICACHE_ENTRIES];
// finish translation on a TLB miss and upate the TLB
- void* refill(reg_t addr, bool store, bool fetch);
+ void* refill(reg_t addr, reg_t bytes, bool store, bool fetch);
// perform a page table walk for a given virtual address
pte_t walk(reg_t addr);
// translate a virtual address to a physical address
- void* translate(reg_t addr, bool store, bool fetch)
+ void* translate(reg_t addr, reg_t bytes, bool store, bool fetch)
{
reg_t idx = (addr >> PGSHIFT) % TLB_ENTRIES;
reg_t* tlb_tag = fetch ? tlb_insn_tag : store ? tlb_store_tag :tlb_load_tag;
reg_t expected_tag = addr & ~(PGSIZE-1);
if(likely(tlb_tag[idx] == expected_tag))
- return (void*)(((long)addr & (PGSIZE-1)) + tlb_data[idx]);
+ return ((uintptr_t)addr & (PGSIZE-1)) + tlb_data[idx];
- return refill(addr, store, fetch);
+ return refill(addr, bytes, store, fetch);
}
friend class processor_t;
bool running() { return run; }
void set_pcr(int which, reg_t val);
reg_t get_pcr(int which);
+ mmu_t* get_mmu() { return &mmu; }
private:
sim_t& sim;
#include "sim.h"
#include "htif.h"
+#include "cachesim.h"
+#include <fesvr/option_parser.h>
#include <stdio.h>
#include <stdlib.h>
+#include <getopt.h>
#include <vector>
#include <string>
+#include <memory>
static void help()
{
fprintf(stderr, "usage: riscv-isa-run [host options] <target program> [target options]\n");
fprintf(stderr, "Host Options:\n");
- fprintf(stderr, " -p <n> Simulate <n> processors\n");
- fprintf(stderr, " -m <n> Provide <n> MB of target memory\n");
- fprintf(stderr, " -d Interactive debug mode\n");
- fprintf(stderr, " -h Print this help message\n");
+ fprintf(stderr, " -p <n> Simulate <n> processors\n");
+ fprintf(stderr, " -m <n> Provide <n> MB of target memory\n");
+ fprintf(stderr, " -d Interactive debug mode\n");
+ fprintf(stderr, " -h Print this help message\n");
+ fprintf(stderr, " -h Print this help message\n");
+ fprintf(stderr, " --ic=<S>:<W>:<B> Instantiate a cache model with S sets,\n");
+ fprintf(stderr, " --dc=<S>:<W>:<B> W ways, and B-byte blocks (with S and\n");
+ fprintf(stderr, " --l2=<S>:<W>:<B> B both powers of 2).\n");
exit(1);
}
int main(int argc, char** argv)
{
bool debug = false;
- int nprocs = 1;
- int mem_mb = 0;
+ size_t nprocs = 1;
+ size_t mem_mb = 0;
+ std::unique_ptr<icache_sim_t> ic;
+ std::unique_ptr<dcache_sim_t> dc;
+ std::unique_ptr<cache_sim_t> l2;
- // parse command-line arguments
- for(int c; (c = getopt(argc,argv,"hdp:m:")) != -1; )
- {
- switch(c)
- {
- case 'd':
- debug = true;
- break;
- case 'p':
- nprocs = atoi(optarg);
- break;
- case 'm':
- mem_mb = atoi(optarg);
- break;
- default:
- fprintf(stderr, "unknown option: -%c", optopt);
- case 'h':
- help();
- }
- }
+ option_parser_t parser;
+ parser.help(&help);
+ parser.option('d', 0, 0, [&](const char* s){debug = true;});
+ parser.option('p', 0, 1, [&](const char* s){nprocs = atoi(s);});
+ parser.option('m', 0, 1, [&](const char* s){mem_mb = atoi(s);});
+ parser.option(0, "ic", 1, [&](const char* s){ic.reset(new icache_sim_t(s));});
+ parser.option(0, "dc", 1, [&](const char* s){dc.reset(new dcache_sim_t(s));});
+ parser.option(0, "l2", 1, [&](const char* s){l2.reset(new cache_sim_t(s, "L2$"));});
- if (optind == argc)
+ auto argv1 = parser.parse(argv);
+ if (!*argv1)
help();
-
- std::vector<std::string> htif_args(argv + optind, argv + argc);
+ std::vector<std::string> htif_args(argv1, (const char*const*)argv + argc);
sim_t s(nprocs, mem_mb, htif_args);
+
+ if (ic && l2) ic->set_miss_handler(&*l2);
+ if (dc && l2) dc->set_miss_handler(&*l2);
+ for (size_t i = 0; i < nprocs; i++)
+ {
+ if (ic) s.get_core(i)->get_mmu()->register_memtracer(&*ic);
+ if (dc) s.get_core(i)->get_mmu()->register_memtracer(&*dc);
+ }
+
s.run(debug);
}
opcodes.h \
insn_header.h \
dispatch.h \
+ cachesim.h \
+ memtracer.h \
NDISPATCH := 10
DISPATCH_SRCS := \
sim.cc \
interactive.cc \
trap.cc \
- icsim.cc \
+ cachesim.cc \
mmu.cc \
disasm.cc \
$(DISPATCH_SRCS) \
// returns the number of processors in this simulator
size_t num_cores() { return procs.size(); }
+ processor_t* get_core(size_t i) { return procs[i]; }
// read one of the system control registers
reg_t get_scr(int which);