Browse Source

Merge pull request #575 from zeldin/big-endian

Big endian target support
pull/592/head
Andrew Waterman 6 years ago
committed by GitHub
parent
commit
6fe352d543
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      config.h.in
  2. 16
      configure
  3. 23
      fesvr/elfloader.cc
  4. 8
      fesvr/htif.cc
  5. 26
      fesvr/htif.h
  6. 48
      fesvr/memif.cc
  7. 52
      fesvr/memif.h
  8. 18
      fesvr/syscall.cc
  9. 64
      riscv/byteorder.h
  10. 8
      riscv/encoding.h
  11. 11
      riscv/mmu.cc
  12. 51
      riscv/mmu.h
  13. 12
      riscv/processor.cc
  14. 5
      riscv/riscv.ac
  15. 49
      riscv/sim.cc
  16. 2
      riscv/sim.h

9
config.h.in

@ -15,6 +15,9 @@
/* Default value for --varch switch */
#undef DEFAULT_VARCH
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
#undef DISASM_ENABLED
/* Executable name of device-tree-compiler */
#undef DTC
@ -84,6 +87,9 @@
/* Enable hardware management of PTE accessed and dirty bits */
#undef RISCV_ENABLE_DIRTY
/* Enable support for running target in either endianness */
#undef RISCV_ENABLE_DUAL_ENDIAN
/* Enable PC histogram generation */
#undef RISCV_ENABLE_HISTOGRAM
@ -93,6 +99,9 @@
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
#undef SOFTFLOAT_ENABLED
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
#undef SPIKE_DASM_ENABLED
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
#undef SPIKE_MAIN_ENABLED

16
configure

@ -709,6 +709,7 @@ enable_commitlog
enable_histogram
enable_dirty
enable_misaligned
enable_dual_endian
'
ac_precious_vars='build_alias
host_alias
@ -1357,6 +1358,8 @@ Optional Features:
bits
--enable-misaligned Enable hardware support for misaligned loads and
stores
--enable-dual-endian Enable support for running target in either
endianness
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@ -4960,6 +4963,19 @@ if test "x$enable_misaligned" = "xyes"; then :
$as_echo "#define RISCV_ENABLE_MISALIGNED /**/" >>confdefs.h
fi
# Check whether --enable-dual-endian was given.
if test "${enable_dual_endian+set}" = set; then :
enableval=$enable_dual_endian;
fi
if test "x$enable_dual_endian" = "xyes"; then :
$as_echo "#define RISCV_ENABLE_DUAL_ENDIAN /**/" >>confdefs.h
fi

23
fesvr/elfloader.cc

@ -31,7 +31,7 @@ std::map<std::string, uint64_t> load_elf(const char* fn, memif_t* memif, reg_t*
assert(size >= sizeof(Elf64_Ehdr));
const Elf64_Ehdr* eh64 = (const Elf64_Ehdr*)buf;
assert(IS_ELF32(*eh64) || IS_ELF64(*eh64));
assert(IS_ELFLE(*eh64));
assert(IS_ELFLE(*eh64) || IS_ELFBE(*eh64));
assert(IS_ELF_EXEC(*eh64));
assert(IS_ELF_RISCV(*eh64) || IS_ELF_EM_NONE(*eh64));
assert(IS_ELF_VCURRENT(*eh64));
@ -83,10 +83,23 @@ std::map<std::string, uint64_t> load_elf(const char* fn, memif_t* memif, reg_t*
} \
} while(0)
if (IS_ELF32(*eh64))
LOAD_ELF(Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Sym, from_le);
else
LOAD_ELF(Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Sym, from_le);
if (IS_ELFLE(*eh64)) {
memif->set_target_endianness(memif_endianness_little);
if (IS_ELF32(*eh64))
LOAD_ELF(Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Sym, from_le);
else
LOAD_ELF(Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Sym, from_le);
} else {
#ifndef RISCV_ENABLE_DUAL_ENDIAN
throw std::invalid_argument("Specified ELF is big endian. Configure with --enable-dual-endian to enable support");
#else
memif->set_target_endianness(memif_endianness_big);
if (IS_ELF32(*eh64))
LOAD_ELF(Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Sym, from_be);
else
LOAD_ELF(Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Sym, from_be);
#endif
}
munmap(buf, size);

8
fesvr/htif.cc

@ -216,8 +216,8 @@ int htif_t::run()
while (!signal_exit && exitcode == 0)
{
if (auto tohost = from_le(mem.read_uint64(tohost_addr))) {
mem.write_uint64(tohost_addr, 0);
if (auto tohost = from_target(mem.read_uint64(tohost_addr))) {
mem.write_uint64(tohost_addr, target_endian<uint64_t>::zero);
command_t cmd(mem, tohost, fromhost_callback);
device_list.handle_command(cmd);
} else {
@ -226,8 +226,8 @@ int htif_t::run()
device_list.tick();
if (!fromhost_queue.empty() && mem.read_uint64(fromhost_addr) == 0) {
mem.write_uint64(fromhost_addr, to_le(fromhost_queue.front()));
if (!fromhost_queue.empty() && !mem.read_uint64(fromhost_addr)) {
mem.write_uint64(fromhost_addr, to_target(fromhost_queue.front()));
fromhost_queue.pop();
}
}

26
fesvr/htif.h

@ -6,9 +6,11 @@
#include "memif.h"
#include "syscall.h"
#include "device.h"
#include "byteorder.h"
#include <string.h>
#include <map>
#include <vector>
#include <assert.h>
class htif_t : public chunked_memif_t
{
@ -27,6 +29,30 @@ class htif_t : public chunked_memif_t
virtual memif_t& memif() { return mem; }
template<typename T> inline T from_target(target_endian<T> n) const
{
#ifdef RISCV_ENABLE_DUAL_ENDIAN
memif_endianness_t endianness = get_target_endianness();
assert(endianness == memif_endianness_little || endianness == memif_endianness_big);
return endianness == memif_endianness_big? n.from_be() : n.from_le();
#else
return n.from_le();
#endif
}
template<typename T> inline target_endian<T> to_target(T n) const
{
#ifdef RISCV_ENABLE_DUAL_ENDIAN
memif_endianness_t endianness = get_target_endianness();
assert(endianness == memif_endianness_little || endianness == memif_endianness_big);
return endianness == memif_endianness_big? target_endian<T>::to_be(n) : target_endian<T>::to_le(n);
#else
return target_endian<T>::to_le(n);
#endif
}
protected:
virtual void reset() = 0;

48
fesvr/memif.cc

@ -94,90 +94,90 @@ void memif_t::write(addr_t addr, size_t len, const void* bytes)
throw std::runtime_error("misaligned address"); \
this->write(addr, sizeof(val), &val)
uint8_t memif_t::read_uint8(addr_t addr)
target_endian<uint8_t> memif_t::read_uint8(addr_t addr)
{
uint8_t val;
target_endian<uint8_t> val;
MEMIF_READ_FUNC;
}
int8_t memif_t::read_int8(addr_t addr)
target_endian<int8_t> memif_t::read_int8(addr_t addr)
{
int8_t val;
target_endian<int8_t> val;
MEMIF_READ_FUNC;
}
void memif_t::write_uint8(addr_t addr, uint8_t val)
void memif_t::write_uint8(addr_t addr, target_endian<uint8_t> val)
{
MEMIF_WRITE_FUNC;
}
void memif_t::write_int8(addr_t addr, int8_t val)
void memif_t::write_int8(addr_t addr, target_endian<int8_t> val)
{
MEMIF_WRITE_FUNC;
}
uint16_t memif_t::read_uint16(addr_t addr)
target_endian<uint16_t> memif_t::read_uint16(addr_t addr)
{
uint16_t val;
target_endian<uint16_t> val;
MEMIF_READ_FUNC;
}
int16_t memif_t::read_int16(addr_t addr)
target_endian<int16_t> memif_t::read_int16(addr_t addr)
{
int16_t val;
target_endian<int16_t> val;
MEMIF_READ_FUNC;
}
void memif_t::write_uint16(addr_t addr, uint16_t val)
void memif_t::write_uint16(addr_t addr, target_endian<uint16_t> val)
{
MEMIF_WRITE_FUNC;
}
void memif_t::write_int16(addr_t addr, int16_t val)
void memif_t::write_int16(addr_t addr, target_endian<int16_t> val)
{
MEMIF_WRITE_FUNC;
}
uint32_t memif_t::read_uint32(addr_t addr)
target_endian<uint32_t> memif_t::read_uint32(addr_t addr)
{
uint32_t val;
target_endian<uint32_t> val;
MEMIF_READ_FUNC;
}
int32_t memif_t::read_int32(addr_t addr)
target_endian<int32_t> memif_t::read_int32(addr_t addr)
{
int32_t val;
target_endian<int32_t> val;
MEMIF_READ_FUNC;
}
void memif_t::write_uint32(addr_t addr, uint32_t val)
void memif_t::write_uint32(addr_t addr, target_endian<uint32_t> val)
{
MEMIF_WRITE_FUNC;
}
void memif_t::write_int32(addr_t addr, int32_t val)
void memif_t::write_int32(addr_t addr, target_endian<int32_t> val)
{
MEMIF_WRITE_FUNC;
}
uint64_t memif_t::read_uint64(addr_t addr)
target_endian<uint64_t> memif_t::read_uint64(addr_t addr)
{
uint64_t val;
target_endian<uint64_t> val;
MEMIF_READ_FUNC;
}
int64_t memif_t::read_int64(addr_t addr)
target_endian<int64_t> memif_t::read_int64(addr_t addr)
{
int64_t val;
target_endian<int64_t> val;
MEMIF_READ_FUNC;
}
void memif_t::write_uint64(addr_t addr, uint64_t val)
void memif_t::write_uint64(addr_t addr, target_endian<uint64_t> val)
{
MEMIF_WRITE_FUNC;
}
void memif_t::write_int64(addr_t addr, int64_t val)
void memif_t::write_int64(addr_t addr, target_endian<int64_t> val)
{
MEMIF_WRITE_FUNC;
}

52
fesvr/memif.h

@ -5,11 +5,18 @@
#include <stdint.h>
#include <stddef.h>
#include "byteorder.h"
typedef uint64_t reg_t;
typedef int64_t sreg_t;
typedef reg_t addr_t;
typedef enum {
memif_endianness_undecided,
memif_endianness_little,
memif_endianness_big
} memif_endianness_t;
class chunked_memif_t
{
public:
@ -19,6 +26,11 @@ public:
virtual size_t chunk_align() = 0;
virtual size_t chunk_max_size() = 0;
virtual void set_target_endianness(memif_endianness_t endianness) {}
virtual memif_endianness_t get_target_endianness() const {
return memif_endianness_undecided;
}
};
class memif_t
@ -32,28 +44,36 @@ public:
virtual void write(addr_t addr, size_t len, const void* bytes);
// read and write 8-bit words
virtual uint8_t read_uint8(addr_t addr);
virtual int8_t read_int8(addr_t addr);
virtual void write_uint8(addr_t addr, uint8_t val);
virtual void write_int8(addr_t addr, int8_t val);
virtual target_endian<uint8_t> read_uint8(addr_t addr);
virtual target_endian<int8_t> read_int8(addr_t addr);
virtual void write_uint8(addr_t addr, target_endian<uint8_t> val);
virtual void write_int8(addr_t addr, target_endian<int8_t> val);
// read and write 16-bit words
virtual uint16_t read_uint16(addr_t addr);
virtual int16_t read_int16(addr_t addr);
virtual void write_uint16(addr_t addr, uint16_t val);
virtual void write_int16(addr_t addr, int16_t val);
virtual target_endian<uint16_t> read_uint16(addr_t addr);
virtual target_endian<int16_t> read_int16(addr_t addr);
virtual void write_uint16(addr_t addr, target_endian<uint16_t> val);
virtual void write_int16(addr_t addr, target_endian<int16_t> val);
// read and write 32-bit words
virtual uint32_t read_uint32(addr_t addr);
virtual int32_t read_int32(addr_t addr);
virtual void write_uint32(addr_t addr, uint32_t val);
virtual void write_int32(addr_t addr, int32_t val);
virtual target_endian<uint32_t> read_uint32(addr_t addr);
virtual target_endian<int32_t> read_int32(addr_t addr);
virtual void write_uint32(addr_t addr, target_endian<uint32_t> val);
virtual void write_int32(addr_t addr, target_endian<int32_t> val);
// read and write 64-bit words
virtual uint64_t read_uint64(addr_t addr);
virtual int64_t read_int64(addr_t addr);
virtual void write_uint64(addr_t addr, uint64_t val);
virtual void write_int64(addr_t addr, int64_t val);
virtual target_endian<uint64_t> read_uint64(addr_t addr);
virtual target_endian<int64_t> read_int64(addr_t addr);
virtual void write_uint64(addr_t addr, target_endian<uint64_t> val);
virtual void write_int64(addr_t addr, target_endian<int64_t> val);
// endianness
virtual void set_target_endianness(memif_endianness_t endianness) {
cmemif->set_target_endianness(endianness);
}
virtual memif_endianness_t get_target_endianness() const {
return cmemif->get_target_endianness();
}
protected:
chunked_memif_t* cmemif;

18
fesvr/syscall.cc

@ -299,22 +299,22 @@ reg_t syscall_t::sys_getcwd(reg_t pbuf, reg_t size, reg_t a2, reg_t a3, reg_t a4
reg_t syscall_t::sys_getmainvars(reg_t pbuf, reg_t limit, reg_t a2, reg_t a3, reg_t a4, reg_t a5, reg_t a6)
{
std::vector<std::string> args = htif->target_args();
std::vector<uint64_t> words(args.size() + 3);
words[0] = to_le(args.size());
words[args.size()+1] = 0; // argv[argc] = NULL
words[args.size()+2] = 0; // envp[0] = NULL
std::vector<target_endian<uint64_t>> words(args.size() + 3);
words[0] = htif->to_target<uint64_t>(args.size());
words[args.size()+1] = target_endian<uint64_t>::zero; // argv[argc] = NULL
words[args.size()+2] = target_endian<uint64_t>::zero; // envp[0] = NULL
size_t sz = (args.size() + 3) * sizeof(words[0]);
for (size_t i = 0; i < args.size(); i++)
{
words[i+1] = to_le(sz + pbuf);
words[i+1] = htif->to_target<uint64_t>(sz + pbuf);
sz += args[i].length() + 1;
}
std::vector<char> bytes(sz);
memcpy(&bytes[0], &words[0], sizeof(words[0]) * words.size());
for (size_t i = 0; i < args.size(); i++)
strcpy(&bytes[from_le(words[i+1]) - pbuf], args[i].c_str());
strcpy(&bytes[htif->from_target(words[i+1]) - pbuf], args[i].c_str());
if (bytes.size() > limit)
return -ENOMEM;
@ -340,14 +340,14 @@ reg_t syscall_t::sys_chdir(reg_t path, reg_t a1, reg_t a2, reg_t a3, reg_t a4, r
void syscall_t::dispatch(reg_t mm)
{
reg_t magicmem[8];
target_endian<reg_t> magicmem[8];
memif->read(mm, sizeof(magicmem), magicmem);
reg_t n = from_le(magicmem[0]);
reg_t n = htif->from_target(magicmem[0]);
if (n >= table.size() || !table[n])
throw std::runtime_error("bad syscall #" + std::to_string(n));
magicmem[0] = to_le((this->*table[n])(from_le(magicmem[1]), from_le(magicmem[2]), from_le(magicmem[3]), from_le(magicmem[4]), from_le(magicmem[5]), from_le(magicmem[6]), from_le(magicmem[7])));
magicmem[0] = htif->to_target((this->*table[n])(htif->from_target(magicmem[1]), htif->from_target(magicmem[2]), htif->from_target(magicmem[3]), htif->from_target(magicmem[4]), htif->from_target(magicmem[5]), htif->from_target(magicmem[6]), htif->from_target(magicmem[7])));
memif->write(mm, sizeof(magicmem), magicmem);
}

64
riscv/byteorder.h

@ -27,4 +27,68 @@ template<typename T> static inline T from_be(T n) { return swap(n); }
template<typename T> static inline T to_be(T n) { return swap(n); }
#endif
// Wrapper to mark a value as target endian, to guide conversion code
template<typename T> class base_endian {
protected:
T value;
base_endian(T n) : value(n) {}
public:
// Setting to and testing against zero never needs swapping
base_endian() : value(0) {}
bool operator!() { return !value; }
// Bitwise logic operations can be performed without swapping
base_endian& operator|=(const base_endian& rhs) { value |= rhs.value; return *this; }
base_endian& operator&=(const base_endian& rhs) { value &= rhs.value; return *this; }
base_endian& operator^=(const base_endian& rhs) { value ^= rhs.value; return *this; }
inline T from_be() { return ::from_be(value); }
inline T from_le() { return ::from_le(value); }
};
template<typename T> class target_endian : public base_endian<T> {
protected:
target_endian(T n) : base_endian<T>(n) {}
public:
target_endian() {}
static inline target_endian to_be(T n) { return target_endian(::to_be(n)); }
static inline target_endian to_le(T n) { return target_endian(::to_le(n)); }
// Useful values over which swapping is identity
static const target_endian zero;
static const target_endian all_ones;
};
template<typename T> const target_endian<T> target_endian<T>::zero = target_endian(T(0));
template<typename T> const target_endian<T> target_endian<T>::all_ones = target_endian(~T(0));
// Specializations with implicit conversions (no swap information needed)
template<> class target_endian<uint8_t> : public base_endian<uint8_t> {
public:
target_endian() {}
target_endian(uint8_t n) : base_endian<uint8_t>(n) {}
operator uint8_t() { return value; }
static inline target_endian to_be(uint8_t n) { return target_endian(n); }
static inline target_endian to_le(uint8_t n) { return target_endian(n); }
};
template<> class target_endian<int8_t> : public base_endian<int8_t> {
public:
target_endian() {}
target_endian(int8_t n) : base_endian<int8_t>(n) {}
operator int8_t() { return value; }
static inline target_endian to_be(int8_t n) { return target_endian(n); }
static inline target_endian to_le(int8_t n) { return target_endian(n); }
};
#endif

8
riscv/encoding.h

@ -9,7 +9,7 @@
#define MSTATUS_MIE 0x00000008
#define MSTATUS_UPIE 0x00000010
#define MSTATUS_SPIE 0x00000020
#define MSTATUS_HPIE 0x00000040
#define MSTATUS_UBE 0x00000040
#define MSTATUS_MPIE 0x00000080
#define MSTATUS_SPP 0x00000100
#define MSTATUS_VS 0x00000600
@ -25,14 +25,20 @@
#define MSTATUS32_SD 0x80000000
#define MSTATUS_UXL 0x0000000300000000
#define MSTATUS_SXL 0x0000000C00000000
#define MSTATUS_SBE 0x0000001000000000
#define MSTATUS_MBE 0x0000002000000000
#define MSTATUS_GVA 0x0000004000000000
#define MSTATUS_MPV 0x0000008000000000
#define MSTATUS64_SD 0x8000000000000000
#define MSTATUSH_SBE 0x00000010
#define MSTATUSH_MBE 0x00000020
#define SSTATUS_UIE 0x00000001
#define SSTATUS_SIE 0x00000002
#define SSTATUS_UPIE 0x00000010
#define SSTATUS_SPIE 0x00000020
#define SSTATUS_UBE 0x00000040
#define SSTATUS_SPP 0x00000100
#define SSTATUS_VS 0x00000600
#define SSTATUS_FS 0x00006000

11
riscv/mmu.cc

@ -6,6 +6,9 @@
mmu_t::mmu_t(simif_t* sim, processor_t* proc)
: sim(sim), proc(proc),
#ifdef RISCV_ENABLE_DUAL_ENDIAN
target_big_endian(false),
#endif
check_triggers_fetch(false),
check_triggers_load(false),
check_triggers_store(false),
@ -319,7 +322,7 @@ reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_ty
throw_access_exception(gva, trap_type);
}
reg_t pte = vm.ptesize == 4 ? from_le(*(uint32_t*)ppte) : from_le(*(uint64_t*)ppte);
reg_t pte = vm.ptesize == 4 ? from_target(*(target_endian<uint32_t>*)ppte) : from_target(*(target_endian<uint64_t>*)ppte);
reg_t ppn = pte >> PTE_PPN_SHIFT;
if (PTE_TABLE(pte)) { // next level of page table
@ -341,7 +344,7 @@ reg_t mmu_t::s2xlate(reg_t gva, reg_t gpa, access_type type, access_type trap_ty
if ((pte & ad) != ad) {
if (!pmp_ok(pte_paddr, vm.ptesize, STORE, PRV_S))
throw_access_exception(gva, trap_type);
*(uint32_t*)ppte |= to_le((uint32_t)ad);
*(target_endian<uint32_t>*)ppte |= to_target((uint32_t)ad);
}
#else
// take exception if access or possibly dirty bit is not set.
@ -392,7 +395,7 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode, bool virt, bool mxr)
if (!ppte || !pmp_ok(pte_paddr, vm.ptesize, LOAD, PRV_S))
throw_access_exception(addr, type);
reg_t pte = vm.ptesize == 4 ? from_le(*(uint32_t*)ppte) : from_le(*(uint64_t*)ppte);
reg_t pte = vm.ptesize == 4 ? from_target(*(target_endian<uint32_t>*)ppte) : from_target(*(target_endian<uint64_t>*)ppte);
reg_t ppn = pte >> PTE_PPN_SHIFT;
if (PTE_TABLE(pte)) { // next level of page table
@ -414,7 +417,7 @@ reg_t mmu_t::walk(reg_t addr, access_type type, reg_t mode, bool virt, bool mxr)
if ((pte & ad) != ad) {
if (!pmp_ok(pte_paddr, vm.ptesize, STORE, PRV_S))
throw_access_exception(addr, type);
*(uint32_t*)ppte |= to_le((uint32_t)ad);
*(target_endian<uint32_t>*)ppte |= to_target((uint32_t)ad);
}
#else
// take exception if access or possibly dirty bit is not set.

51
riscv/mmu.h

@ -101,10 +101,10 @@ public:
size_t size = sizeof(type##_t); \
if (likely(tlb_load_tag[vpn % TLB_ENTRIES] == vpn)) { \
if (proc) READ_MEM(addr, size); \
return from_le(*(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr)); \
return from_target(*(target_endian<type##_t>*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr)); \
} \
if (unlikely(tlb_load_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { \
type##_t data = from_le(*(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr)); \
type##_t data = from_target(*(target_endian<type##_t>*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr)); \
if (!matched_trigger) { \
matched_trigger = trigger_exception(OPERATION_LOAD, addr, data); \
if (matched_trigger) \
@ -113,12 +113,12 @@ public:
if (proc) READ_MEM(addr, size); \
return data; \
} \
type##_t res; \
target_endian<type##_t> res; \
load_slow_path(addr, sizeof(type##_t), (uint8_t*)&res, (xlate_flags)); \
if (proc) READ_MEM(addr, size); \
if (xlate_flags) \
flush_tlb(); \
return from_le(res); \
return from_target(res); \
}
// load value from memory at aligned address; zero extend to register width
@ -165,7 +165,7 @@ public:
size_t size = sizeof(type##_t); \
if (likely(tlb_store_tag[vpn % TLB_ENTRIES] == vpn)) { \
if (proc) WRITE_MEM(addr, val, size); \
*(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = to_le(val); \
*(target_endian<type##_t>*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = to_target(val); \
} \
else if (unlikely(tlb_store_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) { \
if (!matched_trigger) { \
@ -174,11 +174,11 @@ public:
throw *matched_trigger; \
} \
if (proc) WRITE_MEM(addr, val, size); \
*(type##_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = to_le(val); \
*(target_endian<type##_t>*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr) = to_target(val); \
} \
else { \
type##_t le_val = to_le(val); \
store_slow_path(addr, sizeof(type##_t), (const uint8_t*)&le_val, (xlate_flags)); \
target_endian<type##_t> target_val = to_target(val); \
store_slow_path(addr, sizeof(type##_t), (const uint8_t*)&target_val, (xlate_flags)); \
if (proc) WRITE_MEM(addr, val, size); \
} \
if (xlate_flags) \
@ -342,6 +342,30 @@ public:
#endif
}
void set_target_big_endian(bool enable)
{
#ifdef RISCV_ENABLE_DUAL_ENDIAN
target_big_endian = enable;
#else
assert(enable == false);
#endif
}
bool is_target_big_endian()
{
return target_big_endian;
}
template<typename T> inline T from_target(target_endian<T> n) const
{
return target_big_endian? n.from_be() : n.from_le();
}
template<typename T> inline target_endian<T> to_target(T n) const
{
return target_big_endian? target_endian<T>::to_be(n) : target_endian<T>::to_le(n);
}
private:
simif_t* sim;
processor_t* proc;
@ -393,10 +417,10 @@ private:
result = tlb_data[vpn % TLB_ENTRIES];
}
if (unlikely(tlb_insn_tag[vpn % TLB_ENTRIES] == (vpn | TLB_CHECK_TRIGGERS))) {
uint16_t* ptr = (uint16_t*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr);
int match = proc->trigger_match(OPERATION_EXECUTE, addr, from_le(*ptr));
target_endian<uint16_t>* ptr = (target_endian<uint16_t>*)(tlb_data[vpn % TLB_ENTRIES].host_offset + addr);
int match = proc->trigger_match(OPERATION_EXECUTE, addr, from_target(*ptr));
if (match >= 0) {
throw trigger_matched_t(match, OPERATION_EXECUTE, addr, from_le(*ptr));
throw trigger_matched_t(match, OPERATION_EXECUTE, addr, from_target(*ptr));
}
}
return result;
@ -424,6 +448,11 @@ private:
reg_t pmp_homogeneous(reg_t addr, reg_t len);
reg_t pmp_ok(reg_t addr, reg_t len, access_type type, reg_t mode);
#ifdef RISCV_ENABLE_DUAL_ENDIAN
bool target_big_endian;
#else
static const bool target_big_endian = false;
#endif
bool check_triggers_fetch;
bool check_triggers_load;
bool check_triggers_store;

12
riscv/processor.cc

@ -446,6 +446,10 @@ void processor_t::enable_log_commits()
void processor_t::reset()
{
state.reset(max_isa);
#ifdef RISCV_ENABLE_DUAL_ENDIAN
if (mmu->is_target_big_endian())
state.mstatus |= MSTATUS_UBE | MSTATUS_SBE | MSTATUS_MBE;
#endif
state.mideleg = supports_extension('H') ? MIDELEG_FORCED_MASK : 0;
@ -1411,8 +1415,8 @@ reg_t processor_t::get_csr(int which, insn_t insn, bool write, bool peek)
ret(state.mcounteren);
case CSR_MCOUNTINHIBIT: ret(0);
case CSR_SSTATUS: {
reg_t mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_SPP | SSTATUS_FS
| (supports_extension('V') ? SSTATUS_VS : 0)
reg_t mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UBE | SSTATUS_SPP
| SSTATUS_FS | (supports_extension('V') ? SSTATUS_VS : 0)
| SSTATUS_XS | SSTATUS_SUM | SSTATUS_MXR | SSTATUS_UXL;
reg_t sstatus = state.mstatus & mask;
if ((sstatus & SSTATUS_FS) == SSTATUS_FS ||
@ -1486,6 +1490,10 @@ reg_t processor_t::get_csr(int which, insn_t insn, bool write, bool peek)
}
}
case CSR_MSTATUS: ret(state.mstatus);
case CSR_MSTATUSH:
if (xlen == 32)
ret((state.mstatus >> 32) & (MSTATUSH_SBE | MSTATUSH_MBE));
break;
case CSR_MIP: ret(state.mip);
case CSR_MIE: ret(state.mie);
case CSR_MEPC: ret(state.mepc & pc_alignment_mask());

5
riscv/riscv.ac

@ -45,3 +45,8 @@ AC_ARG_ENABLE([misaligned], AS_HELP_STRING([--enable-misaligned], [Enable hardwa
AS_IF([test "x$enable_misaligned" = "xyes"], [
AC_DEFINE([RISCV_ENABLE_MISALIGNED],,[Enable hardware support for misaligned loads and stores])
])
AC_ARG_ENABLE([dual-endian], AS_HELP_STRING([--enable-dual-endian], [Enable support for running target in either endianness]))
AS_IF([test "x$enable_dual_endian" = "xyes"], [
AC_DEFINE([RISCV_ENABLE_DUAL_ENDIAN],,[Enable support for running target in either endianness])
])

49
riscv/sim.cc

@ -254,8 +254,22 @@ void sim_t::set_rom()
(uint32_t) (start_pc & 0xffffffff),
(uint32_t) (start_pc >> 32)
};
for(int i = 0; i < reset_vec_size; i++)
reset_vec[i] = to_le(reset_vec[i]);
if (get_target_endianness() == memif_endianness_big) {
int i;
// Instuctions are little endian
for (i = 0; reset_vec[i] != 0; i++)
reset_vec[i] = to_le(reset_vec[i]);
// Data is big endian
for (; i < reset_vec_size; i++)
reset_vec[i] = to_be(reset_vec[i]);
// Correct the high/low order of 64-bit start PC
if (get_core(0)->get_xlen() != 32)
std::swap(reset_vec[reset_vec_size-2], reset_vec[reset_vec_size-1]);
} else {
for (int i = 0; i < reset_vec_size; i++)
reset_vec[i] = to_le(reset_vec[i]);
}
std::vector<char> rom((char*)reset_vec, (char*)reset_vec + sizeof(reset_vec));
@ -315,16 +329,41 @@ void sim_t::idle()
void sim_t::read_chunk(addr_t taddr, size_t len, void* dst)
{
assert(len == 8);
auto data = to_le(debug_mmu->load_uint64(taddr));
auto data = debug_mmu->to_target(debug_mmu->load_uint64(taddr));
memcpy(dst, &data, sizeof data);
}
void sim_t::write_chunk(addr_t taddr, size_t len, const void* src)
{
assert(len == 8);
uint64_t data;
target_endian<uint64_t> data;
memcpy(&data, src, sizeof data);
debug_mmu->store_uint64(taddr, from_le(data));
debug_mmu->store_uint64(taddr, debug_mmu->from_target(data));
}
void sim_t::set_target_endianness(memif_endianness_t endianness)
{
#ifdef RISCV_ENABLE_DUAL_ENDIAN
assert(endianness == memif_endianness_little || endianness == memif_endianness_big);
bool enable = endianness == memif_endianness_big;
debug_mmu->set_target_big_endian(enable);
for (size_t i = 0; i < procs.size(); i++) {
procs[i]->get_mmu()->set_target_big_endian(enable);
procs[i]->reset();
}
#else
assert(endianness == memif_endianness_little);
#endif
}
memif_endianness_t sim_t::get_target_endianness() const
{
#ifdef RISCV_ENABLE_DUAL_ENDIAN
return debug_mmu->is_target_big_endian()? memif_endianness_big : memif_endianness_little;
#else
return memif_endianness_little;
#endif
}
void sim_t::proc_reset(unsigned id)

2
riscv/sim.h

@ -138,6 +138,8 @@ private:
void write_chunk(addr_t taddr, size_t len, const void* src);
size_t chunk_align() { return 8; }
size_t chunk_max_size() { return 8; }
void set_target_endianness(memif_endianness_t endianness);
memif_endianness_t get_target_endianness() const;
public:
// Initialize this after procs, because in debug_module_t::reset() we

Loading…
Cancel
Save