vortex/sim/common/mem.h
2024-06-30 00:54:22 -04:00

541 lines
12 KiB
C++

// Copyright © 2019-2023
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <cstdint>
#include <vector>
#include <map>
#include <unordered_map>
#include <cstdint>
#include <unordered_set>
#include <stdexcept>
#include "VX_config.h"
#ifdef VM_ENABLE
#include <unordered_set>
#include <stdexcept>
#include <cassert>
#endif
namespace vortex {
#ifdef VM_ENABLE
// VA MODE
#define BARE 0x0
#define SV32 0x1
#define SV39 0x8
enum ACCESS_TYPE {
LOAD,
STORE,
FETCH
};
class SATP_t
{
private:
uint64_t address;
uint16_t asid;
uint8_t mode;
uint64_t ppn;
uint64_t satp;
uint64_t bits(uint64_t input, uint8_t s_idx, uint8_t e_idx)
{
return (input>> s_idx) & (((uint64_t)1 << (e_idx - s_idx + 1)) - 1);
}
bool bit(uint64_t input , uint8_t idx)
{
return (input ) & ((uint64_t)1 << idx);
}
public:
SATP_t(uint64_t satp) : satp(satp)
{
#ifdef XLEN_32
mode = bit(satp, 31);
asid = bits(satp, 22, 30);
ppn = bits(satp, 0,21);
#else
mode = bits(satp, 60,63);
asid = bits(satp, 44, 59);
ppn = bits(satp, 0,43);
#endif
address = ppn << MEM_PAGE_LOG2_SIZE;
}
SATP_t(uint64_t address, uint16_t asid) : address(address), asid(asid)
{
#ifdef XLEN_32
assert((address >> 32) == 0 && "Upper 32 bits are not zero!");
#endif
mode= VM_ADDR_MODE;
// asid = 0 ;
ppn = address >> MEM_PAGE_LOG2_SIZE;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
#ifdef XLEN_32
satp = (((uint64_t)mode << 31) | ((uint64_t)asid << 22) | ppn);
#else
satp = (((uint64_t)mode << 60) | ((uint64_t)asid << 44) | ppn);
#endif
#pragma GCC diagnostic pop
}
uint8_t get_mode()
{
return mode;
}
uint16_t get_asid()
{
return asid;
}
uint64_t get_base_ppn()
{
return ppn;
}
uint64_t get_satp()
{
return satp;
}
};
class Page_Fault_Exception : public std::runtime_error /* or logic_error */
{
public:
Page_Fault_Exception(const std::string& what = "") : std::runtime_error(what) {}
uint64_t addr;
ACCESS_TYPE type;
};
#endif
struct BadAddress {};
struct OutOfRange {};
class MemDevice {
public:
virtual ~MemDevice() {}
virtual uint64_t size() const = 0;
virtual void read(void* data, uint64_t addr, uint64_t size) = 0;
virtual void write(const void* data, uint64_t addr, uint64_t size) = 0;
};
///////////////////////////////////////////////////////////////////////////////
class RamMemDevice : public MemDevice {
public:
RamMemDevice(uint64_t size, uint32_t wordSize);
RamMemDevice(const char* filename, uint32_t wordSize);
~RamMemDevice() {}
void read(void* data, uint64_t addr, uint64_t size) override;
void write(const void* data, uint64_t addr, uint64_t size) override;
virtual uint64_t size() const {
return contents_.size();
};
protected:
std::vector<uint8_t> contents_;
uint32_t wordSize_;
};
///////////////////////////////////////////////////////////////////////////////
class RomMemDevice : public RamMemDevice {
public:
RomMemDevice(const char *filename, uint32_t wordSize)
: RamMemDevice(filename, wordSize)
{}
RomMemDevice(uint64_t size, uint32_t wordSize)
: RamMemDevice(size, wordSize)
{}
~RomMemDevice();
void write(const void* data, uint64_t addr, uint64_t size) override;
};
///////////////////////////////////////////////////////////////////////////////
class MemoryUnit {
public:
// HW: Expand PageFault struct to contain access_type info for debug purposes
struct PageFault {
PageFault(uint64_t a, bool nf)
: faultAddr(a)
, notFound(nf)
// , access_type(ACCESS_TYPE::LOAD)
{}
uint64_t faultAddr;
bool notFound;
// ACCESS_TYPE access_type;
};
#ifdef VM_ENABLE
MemoryUnit(uint64_t pageSize = MEM_PAGE_SIZE);
~MemoryUnit(){
if ( this->satp_ != NULL)
delete this->satp_;
};
#else
MemoryUnit(uint64_t pageSize = 0);
#endif
void attach(MemDevice &m, uint64_t start, uint64_t end);
#ifdef VM_ENABLE
void read(void* data, uint64_t addr, uint32_t size, ACCESS_TYPE type = ACCESS_TYPE::LOAD);
void write(const void* data, uint64_t addr, uint32_t size, ACCESS_TYPE type = ACCESS_TYPE::STORE);
#else
void read(void* data, uint64_t addr, uint32_t size, bool sup);
void write(const void* data, uint64_t addr, uint32_t size, bool sup);
#endif
void amo_reserve(uint64_t addr);
bool amo_check(uint64_t addr);
#ifdef VM_ENABLE
void tlbAdd(uint64_t virt, uint64_t phys, uint32_t flags, uint64_t size_bits);
uint8_t is_satp_unset();
uint64_t get_satp();
uint8_t get_mode();
uint64_t get_base_ppn();
void set_satp(uint64_t satp);
#else
void tlbAdd(uint64_t virt, uint64_t phys, uint32_t flags);
#endif
void tlbRm(uint64_t vaddr);
void tlbFlush() {
tlb_.clear();
}
private:
struct amo_reservation_t {
uint64_t addr;
bool valid;
};
class ADecoder {
public:
ADecoder() {}
void read(void* data, uint64_t addr, uint64_t size);
void write(const void* data, uint64_t addr, uint64_t size);
void map(uint64_t start, uint64_t end, MemDevice &md);
private:
struct mem_accessor_t {
MemDevice* md;
uint64_t addr;
};
struct entry_t {
MemDevice* md;
uint64_t start;
uint64_t end;
};
bool lookup(uint64_t addr, uint32_t wordSize, mem_accessor_t*);
std::vector<entry_t> entries_;
};
struct TLBEntry {
TLBEntry() {}
#ifdef VM_ENABLE
TLBEntry(uint32_t pfn, uint32_t flags, uint64_t size_bits)
: pfn(pfn)
, flags(flags)
, mru_bit(true)
, size_bits (size_bits)
{
d = bit(7);
a = bit(6);
g = bit(5);
u = bit(4);
x = bit(3);
w = bit(2);
r = bit(1);
v = bit(0);
}
bool bit(uint8_t idx)
{
return (flags) & (1 << idx);
}
uint32_t pfn;
uint32_t flags;
bool mru_bit;
uint64_t size_bits;
bool d, a, g, u, x, w, r, v;
#else
TLBEntry(uint32_t pfn, uint32_t flags)
: pfn(pfn)
, flags(flags)
{}
uint32_t pfn;
uint32_t flags;
#endif
};
#ifdef VM_ENABLE
std::pair<bool, uint64_t> tlbLookup(uint64_t vAddr, ACCESS_TYPE type, uint64_t* size_bits);
bool need_trans(uint64_t dev_pAddr);
uint64_t vAddr_to_pAddr(uint64_t vAddr, ACCESS_TYPE type);
uint64_t get_pte_address(uint64_t base_ppn, uint64_t vpn);
std::pair<uint64_t, uint8_t> page_table_walk(uint64_t vAddr_bits, ACCESS_TYPE type, uint64_t* size_bits);
#else
uint64_t toPhyAddr(uint64_t vAddr, uint32_t flagMask);
TLBEntry tlbLookup(uint64_t vAddr, uint32_t flagMask);
#endif
std::unordered_map<uint64_t, TLBEntry> tlb_;
uint64_t pageSize_;
ADecoder decoder_;
#ifndef VM_ENABLE
bool enableVM_;
#endif
amo_reservation_t amo_reservation_;
#ifdef VM_ENABLE
std::unordered_set<uint64_t> unique_translations;
uint64_t TLB_HIT, TLB_MISS, TLB_EVICT, PTW, PERF_UNIQUE_PTW;
SATP_t *satp_;
#endif
};
///////////////////////////////////////////////////////////////////////////////
class ACLManager {
public:
void set(uint64_t addr, uint64_t size, int flags);
bool check(uint64_t addr, uint64_t size, int flags) const;
private:
struct acl_entry_t {
uint64_t end;
int32_t flags;
};
std::map<uint64_t, acl_entry_t> acl_map_;
};
///////////////////////////////////////////////////////////////////////////////
class RAM : public MemDevice {
public:
RAM(uint64_t capacity, uint32_t page_size);
RAM(uint64_t capacity) : RAM(capacity, capacity) {}
~RAM();
void clear();
uint64_t size() const override;
void read(void* data, uint64_t addr, uint64_t size) override;
void write(const void* data, uint64_t addr, uint64_t size) override;
void loadBinImage(const char* filename, uint64_t destination);
void loadHexImage(const char* filename);
uint8_t& operator[](uint64_t address) {
return *this->get(address);
}
const uint8_t& operator[](uint64_t address) const {
return *this->get(address);
}
void set_acl(uint64_t addr, uint64_t size, int flags);
void enable_acl(bool enable) {
check_acl_ = enable;
}
private:
uint8_t *get(uint64_t address) const;
uint64_t capacity_;
uint32_t page_bits_;
mutable std::unordered_map<uint64_t, uint8_t*> pages_;
mutable uint8_t* last_page_;
mutable uint64_t last_page_index_;
ACLManager acl_mngr_;
bool check_acl_;
};
#ifdef VM_ENABLE
class PTE_t
{
private:
uint64_t address;
uint64_t bits(uint64_t input, uint8_t s_idx, uint8_t e_idx)
{
return (input>> s_idx) & (((uint64_t)1 << (e_idx - s_idx + 1)) - 1);
}
bool bit(uint64_t input, uint8_t idx)
{
return (input) & ((uint64_t)1 << idx);
}
public:
#if VM_ADDR_MODE == SV39
bool N;
uint8_t PBMT;
#endif
uint64_t ppn;
uint32_t rsw;
uint32_t flags;
uint8_t level;
bool d, a, g, u, x, w, r, v;
uint64_t pte_bytes;
void set_flags (uint32_t flag)
{
this->flags = flag;
d = bit(flags,7);
a = bit(flags,6);
g = bit(flags,5);
u = bit(flags,4);
x = bit(flags,3);
w = bit(flags,2);
r = bit(flags,1);
v = bit(flags,0);
}
PTE_t(uint64_t address, uint32_t flags) : address(address)
{
#if VM_ADDR_MODE == SV39
N = 0;
PBMT = 0;
level = 3;
ppn = address >> MEM_PAGE_LOG2_SIZE;
// Reserve for Super page support
// ppn = new uint32_t [level];
// ppn[2]=bits(address,28,53);
// ppn[1]=bits(address,19,27);
// ppn[0]=bits(address,10,18);
set_flags(flags);
// pte_bytes = (N << 63) | (PBMT << 61) | (ppn <<10) | flags ;
pte_bytes = (ppn <<10) | flags ;
#else // if VM_ADDR_MODE == SV32
assert((address>> 32) == 0 && "Upper 32 bits are not zero!");
level = 2;
ppn = address >> MEM_PAGE_LOG2_SIZE;
// Reserve for Super page support
// ppn = new uint32_t[level];
// ppn[1]=bits(address,20,31);
// ppn[0]=bits(address,10,19);
set_flags(flags);
pte_bytes = ppn <<10 | flags ;
#endif
}
PTE_t(uint64_t pte_bytes) : pte_bytes(pte_bytes)
{
#if VM_ADDR_MODE == SV39
N = bit(pte_bytes,63);
PBMT = bits(pte_bytes,61,62);
level = 3;
ppn=bits(pte_bytes,10,53);
address = ppn << MEM_PAGE_LOG2_SIZE;
// Reserve for Super page support
// ppn = new uint32_t [level];
// ppn[2]=bits(pte_bytes,28,53);
// ppn[1]=bits(pte_bytes,19,27);
// ppn[0]=bits(pte_bytes,10,18);
#else //#if VM_ADDR_MODE == SV32
assert((pte_bytes >> 32) == 0 && "Upper 32 bits are not zero!");
level = 2;
ppn=bits(pte_bytes,10, 31);
address = ppn << MEM_PAGE_LOG2_SIZE;
// Reserve for Super page support
// ppn = new uint32_t[level];
// ppn[1]=bits(address, 20,31);
// ppn[0]=bits(address, 10,19);
#endif
rsw = bits(pte_bytes,8,9);
set_flags((uint32_t)(bits(pte_bytes,0,7)));
}
~PTE_t()
{
// Reserve for Super page support
// delete ppn;
}
};
class vAddr_t
{
private:
uint64_t address;
uint64_t bits(uint8_t s_idx, uint8_t e_idx)
{
return (address>> s_idx) & (((uint64_t)1 << (e_idx - s_idx + 1)) - 1);
}
bool bit( uint8_t idx)
{
return (address) & ((uint64_t)1 << idx);
}
public:
uint64_t *vpn;
uint64_t pgoff;
uint8_t level;
vAddr_t(uint64_t address) : address(address)
{
#if VM_ADDR_MODE == SV39
level = 3;
vpn = new uint64_t [level];
vpn[2] = bits(30,38);
vpn[1] = bits(21,29);
vpn[0] = bits(12,20);
pgoff = bits(0,11);
#else //#if VM_ADDR_MODE == SV32
assert((address>> 32) == 0 && "Upper 32 bits are not zero!");
level = 2;
vpn = new uint64_t [level];
vpn[1] = bits(22,31);
vpn[0] = bits(12,21);
pgoff = bits(0,11);
#endif
}
~vAddr_t()
{
delete vpn;
}
};
#endif
} // namespace vortex