Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
7ae6f803eThis file is pinned to the selected release. Download the original to open it in your design tool.
//-----------------------------------------------------------------
// RISC-V ISA Simulator
// V1.0
// Ultra-Embedded.com
// Copyright 2014-2017
//
// admin@ultra-embedded.com
//
// License: BSD
//-----------------------------------------------------------------
//
// Copyright (c) 2014, Ultra-Embedded.com
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// - 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.
// - Neither the name of the author 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 AUTHOR 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.
//-----------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include "riscv.h"
//-----------------------------------------------------------------
// Defines:
//-----------------------------------------------------------------
#define DPRINTF(l,a) do { if (m_trace & l) printf a; } while (0)
#define TRACE_ENABLED(l) (m_trace & l)
#define INST_STAT(l)
//-----------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------
Riscv::Riscv(uint32_t baseAddr /*= 0*/, uint32_t len /*= 0*/)
{
m_mem_regions = 0;
m_stats_if = NULL;
m_console = NULL;
m_has_breakpoints = false;
// Some memory defined
if (len != 0)
create_memory(baseAddr, len);
reset(baseAddr);
}
//-----------------------------------------------------------------
// Deconstructor
//-----------------------------------------------------------------
Riscv::~Riscv()
{
int m;
for (m=0;m<m_mem_regions;m++)
{
if (m_mem[m])
delete m_mem[m];
m_mem[m] = NULL;
}
}
//-----------------------------------------------------------------
// error: Handle an error
//-----------------------------------------------------------------
bool Riscv::error(bool terminal, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
exit(-1);
return true;
}
//-----------------------------------------------------------------
// create_memory: Create a memory region
//-----------------------------------------------------------------
bool Riscv::create_memory(uint32_t baseAddr, uint32_t len, uint8_t *buf /*=NULL*/)
{
if (buf)
return attach_memory(new SimpleMemory(buf, len), baseAddr, len);
else
return attach_memory(new SimpleMemory(len), baseAddr, len);
}
//-----------------------------------------------------------------
// attach_memory: Attach a memory device to a particular region
//-----------------------------------------------------------------
bool Riscv::attach_memory(Memory *memory, uint32_t baseAddr, uint32_t len)
{
if (m_mem_regions < MAX_MEM_REGIONS)
{
m_mem_base[m_mem_regions] = baseAddr;
m_mem_size[m_mem_regions] = len;
m_mem[m_mem_regions] = memory;
m_mem[m_mem_regions]->reset();
m_mem_regions++;
return true;
}
return false;
}
//-----------------------------------------------------------------
// set_pc: Set PC
//-----------------------------------------------------------------
void Riscv::set_pc(uint32_t pc)
{
m_pc = pc;
m_pc_x = pc;
}
//-----------------------------------------------------------------
// set_register: Set register value
//-----------------------------------------------------------------
void Riscv::set_register(int r, uint32_t val)
{
if (r <= RISCV_REGNO_GPR31) m_gpr[r] = val;
else if (r == RISCV_REGNO_PC) m_pc = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_MEPC)) m_csr_mepc = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_MCAUSE)) m_csr_mcause = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_MSTATUS)) m_csr_msr = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_MTVEC)) m_csr_mevec = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_MIE)) m_csr_mie = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_MIP)) m_csr_mip = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_MTIME)) m_csr_mtime = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_MTIMEH)) m_csr_mtimecmp = val; // Non-std
else if (r == (RISCV_REGNO_CSR0 + CSR_MSCRATCH)) m_csr_mscratch = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_MIDELEG)) m_csr_mideleg = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_MEDELEG)) m_csr_medeleg = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_SEPC)) m_csr_sepc = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_STVEC)) m_csr_sevec = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_SCAUSE)) m_csr_scause = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_STVAL)) m_csr_stval = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_SATP)) m_csr_satp = val;
else if (r == (RISCV_REGNO_CSR0 + CSR_SSCRATCH)) m_csr_sscratch = val;
else if (r == RISCV_REGNO_PRIV) m_csr_mpriv = val;
}
//-----------------------------------------------------------------
// get_register: Get register value
//-----------------------------------------------------------------
uint32_t Riscv::get_register(int r)
{
if (r <= RISCV_REGNO_GPR31) return m_gpr[r];
else if (r == RISCV_REGNO_PC) return m_pc;
else if (r == (RISCV_REGNO_CSR0 + CSR_MEPC)) return m_csr_mepc;
else if (r == (RISCV_REGNO_CSR0 + CSR_MCAUSE)) return m_csr_mcause;
else if (r == (RISCV_REGNO_CSR0 + CSR_MSTATUS)) return m_csr_msr;
else if (r == (RISCV_REGNO_CSR0 + CSR_MTVEC)) return m_csr_mevec;
else if (r == (RISCV_REGNO_CSR0 + CSR_MIE)) return m_csr_mie;
else if (r == (RISCV_REGNO_CSR0 + CSR_MIP)) return m_csr_mip;
else if (r == (RISCV_REGNO_CSR0 + CSR_MTIME)) return m_csr_mtime;
else if (r == (RISCV_REGNO_CSR0 + CSR_MTIMEH)) return m_csr_mtimecmp; // Non-std
else if (r == (RISCV_REGNO_CSR0 + CSR_MSCRATCH)) return m_csr_mscratch;
else if (r == (RISCV_REGNO_CSR0 + CSR_MIDELEG)) return m_csr_mideleg;
else if (r == (RISCV_REGNO_CSR0 + CSR_MEDELEG)) return m_csr_medeleg;
else if (r == (RISCV_REGNO_CSR0 + CSR_SEPC)) return m_csr_sepc;
else if (r == (RISCV_REGNO_CSR0 + CSR_STVEC)) return m_csr_sevec;
else if (r == (RISCV_REGNO_CSR0 + CSR_SCAUSE)) return m_csr_scause;
else if (r == (RISCV_REGNO_CSR0 + CSR_STVAL)) return m_csr_stval;
else if (r == (RISCV_REGNO_CSR0 + CSR_SATP)) return m_csr_satp;
else if (r == (RISCV_REGNO_CSR0 + CSR_SSCRATCH)) return m_csr_sscratch;
else if (r == RISCV_REGNO_PRIV) return m_csr_mpriv;
return 0;
}
//-----------------------------------------------------------------
// get_break: Get breakpoint status (and clear)
//-----------------------------------------------------------------
bool Riscv::get_break(void)
{
bool brk = m_break;
m_break = false;
return brk;
}
//-----------------------------------------------------------------
// set_breakpoint: Set breakpoint on a given PC
//-----------------------------------------------------------------
bool Riscv::set_breakpoint(uint32_t pc)
{
m_breakpoints.push_back(pc);
m_has_breakpoints = true;
return true;
}
//-----------------------------------------------------------------
// set_breakpoint: Clear breakpoint on a given PC
//-----------------------------------------------------------------
bool Riscv::clr_breakpoint(uint32_t pc)
{
for (std::vector<uint32_t>::iterator it = m_breakpoints.begin() ; it != m_breakpoints.end(); ++it)
if ((*it) == pc)
{
m_breakpoints.erase(it);
m_has_breakpoints = !m_breakpoints.empty();
return true;
}
return false;
}
//-----------------------------------------------------------------
// check_breakpoint: Check if breakpoint has been hit
//-----------------------------------------------------------------
bool Riscv::check_breakpoint(uint32_t pc)
{
for (std::vector<uint32_t>::iterator it = m_breakpoints.begin() ; it != m_breakpoints.end(); ++it)
if ((*it) == pc)
return true;
return false;
}
//-----------------------------------------------------------------
// reset: Reset CPU state
//-----------------------------------------------------------------
void Riscv::reset(uint32_t start_addr)
{
m_pc = start_addr;
m_pc_x = start_addr;
for (int i=0;i<REGISTERS;i++)
m_gpr[i] = 0;
m_csr_mpriv = PRIV_MACHINE;
m_csr_msr = 0;
m_csr_mideleg = 0;
m_csr_medeleg = 0;
m_csr_mepc = 0;
m_csr_mie = 0;
m_csr_mip = 0;
m_csr_mcause = 0;
m_csr_mevec = 0;
m_csr_mtime = 0;
m_csr_mtimecmp = 0;
m_csr_mscratch = 0;
m_csr_sepc = 0;
m_csr_sevec = 0;
m_csr_scause = 0;
m_csr_stval = 0;
m_csr_satp = 0;
m_csr_sscratch = 0;
m_fault = false;
m_break = false;
m_trace = 0;
stats_reset();
}
//-----------------------------------------------------------------
// valid_addr: Check if the physical memory address is valid
//-----------------------------------------------------------------
bool Riscv::valid_addr(uint32_t address)
{
for (int j=0;j<m_mem_regions;j++)
if (address >= m_mem_base[j] && address < (m_mem_base[j] + m_mem_size[j]))
return true;
return false;
}
//-----------------------------------------------------------------
// write: Write a byte to memory (physical address)
//-----------------------------------------------------------------
void Riscv::write(uint32_t address, uint8_t data)
{
for (int j=0;j<m_mem_regions;j++)
if (address >= m_mem_base[j] && address < (m_mem_base[j] + m_mem_size[j]))
{
m_mem[j]->store(address - m_mem_base[j], data, 1);
return ;
}
error(false, "Failed store @ 0x%08x\n", address);
}
//-----------------------------------------------------------------
// write32: Write a word to memory (physical address)
//-----------------------------------------------------------------
void Riscv::write32(uint32_t address, uint32_t data)
{
for (int j=0;j<m_mem_regions;j++)
if (address >= m_mem_base[j] && address < (m_mem_base[j] + m_mem_size[j]))
{
m_mem[j]->store(address - m_mem_base[j], data, 4);
return ;
}
error(false, "Failed store @ 0x%08x\n", address);
}
//-----------------------------------------------------------------
// read: Read a byte from memory (physical address)
//-----------------------------------------------------------------
uint8_t Riscv::read(uint32_t address)
{
for (int j=0;j<m_mem_regions;j++)
if (address >= m_mem_base[j] && address < (m_mem_base[j] + m_mem_size[j]))
return m_mem[j]->load(address - m_mem_base[j], 1, false);
return 0;
}
//-----------------------------------------------------------------
// read32: Read a word from memory (physical address)
//-----------------------------------------------------------------
uint32_t Riscv::read32(uint32_t address)
{
for (int j=0;j<m_mem_regions;j++)
if (address >= m_mem_base[j] && address < (m_mem_base[j] + m_mem_size[j]))
return m_mem[j]->load(address - m_mem_base[j], 4, false);
return 0;
}
//-----------------------------------------------------------------
// get_opcode: Get instruction from address
//-----------------------------------------------------------------
uint32_t Riscv::get_opcode(uint32_t address)
{
return read32(address);
}
#ifdef CONFIG_MMU
//-----------------------------------------------------------------
// mmu_read_word: Read a word from memory
//-----------------------------------------------------------------
int Riscv::mmu_read_word(uint32_t address, uint32_t *val)
{
int m;
*val = 0;
for (m=0;m<m_mem_regions;m++)
if (address >= m_mem_base[m] && address < (m_mem_base[m] + m_mem_size[m]))
{
*val = m_mem[m]->load(address - m_mem_base[m], 4, false);
return 1;
}
return 0;
}
//-----------------------------------------------------------------
// mmu_walk: Page table walker
//-----------------------------------------------------------------
uint32_t Riscv::mmu_walk(uint32_t addr)
{
int shift = 32 - MMU_VA_BITS;
uint32_t pte = 0;
DPRINTF(LOG_MMU, ("MMU: Walk %x\n", addr));
// the address must be a canonical sign-extended VA_BITS-bit number
if (((int32_t)addr << shift >> shift) != (int32_t)addr)
{
pte = 0; // Bad alignment....
error(false, "%08x: PTE Walk - Bad alignment %x\n", m_pc, addr);
}
else if ((m_csr_satp & SATP_MODE) == 0) // Bare mode
{
pte = PAGE_PRESENT | PAGE_READ | PAGE_WRITE | PAGE_EXEC | ((addr >> MMU_PGSHIFT) << MMU_PGSHIFT);
DPRINTF(LOG_MMU, ("MMU: MMU not enabled\n"));
}
else
{
uint32_t base = ((m_csr_satp >> SATP_PPN_SHIFT) & SATP_PPN_MASK) * PAGE_SIZE;
uint32_t asid = ((m_csr_satp >> SATP_ASID_SHIFT) & SATP_ASID_MASK);
DPRINTF(LOG_MMU, ("MMU: MMU enabled - base 0x%08x\n", base));
uint32_t i;
for (i=MMU_LEVELS-1; i >= 0; i--)
{
int ptshift = i * MMU_PTIDXBITS;
uint32_t idx = (addr >> (MMU_PGSHIFT+ptshift)) & ((1<<MMU_PTIDXBITS)-1);
uint32_t pte_addr = base + (idx * MMU_PTESIZE);
// Read PTE
if (!mmu_read_word(pte_addr, &pte))
{
DPRINTF(LOG_MMU, ("MMU: Cannot read PTE entry %x\n", pte_addr));
pte = 0;
break;
}
DPRINTF(LOG_MMU, ("MMU: PTE value = 0x%08x @ 0x%08x\n", pte, pte_addr));
uint32_t ppn = pte >> PAGE_PFN_SHIFT;
// Invalid mapping
if (!(pte & PAGE_PRESENT))
{
DPRINTF(LOG_MMU, ("MMU: Invalid mapping %x\n", pte_addr));
pte = 0;
break;
}
// Next level of page table
else if (!(pte & (PAGE_READ | PAGE_WRITE | PAGE_EXEC)))
{
base = ppn << MMU_PGSHIFT;
DPRINTF(LOG_MMU, ("MMU: Next level of page table %x\n", base));
}
// The actual PTE
else
{
// Keep permission bits
pte &= PAGE_FLAGS;
// if this PTE is from a larger PT, fake a leaf
// PTE so the TLB will work right
uint64_t vpn = addr >> MMU_PGSHIFT;
uint64_t value = (ppn | (vpn & ((((uint64_t)1) << ptshift) - 1))) << MMU_PGSHIFT;
// Add back in permission bits
value |= pte;
assert((value >> 32) == 0);
pte = value;
uint32_t ptd_addr = ((pte >> MMU_PGSHIFT) << MMU_PGSHIFT);
DPRINTF(LOG_MMU, ("MMU: PTE addr %x (%x)\n", ptd_addr, pte));
// fault if physical addr is out of range
if (mmu_read_word(ptd_addr, &ptd_addr))
{
DPRINTF(LOG_MMU, ("MMU: PTE entry found %x\n", pte));
}
else
{
DPRINTF(LOG_MMU, ("MMU: PTE access out of range %x\n", ((pte >> MMU_PGSHIFT) << MMU_PGSHIFT)));
pte = 0;
error(false, "%08x: PTE access out of range %x\n", m_pc, addr);
}
break;
}
}
}
return pte;
}
//-----------------------------------------------------------------
// mmu_i_translate: Translate instruction fetch
//-----------------------------------------------------------------
int Riscv::mmu_i_translate(uint32_t addr, uint32_t *physical)
{
bool page_fault = false;
// Machine - no MMU
if (m_csr_mpriv > PRIV_SUPER)
{
*physical = addr;
return 1;
}
uint32_t pte = mmu_walk(addr);
// Reserved configurations
if (((pte & (PAGE_EXEC | PAGE_READ | PAGE_WRITE)) == PAGE_WRITE) ||
((pte & (PAGE_EXEC | PAGE_READ | PAGE_WRITE)) == (PAGE_EXEC | PAGE_WRITE)))
{
page_fault = true;
}
// Supervisor mode
else if (m_csr_mpriv == PRIV_SUPER)
{
// Supervisor attempts to execute user mode page
if (pte & PAGE_USER)
{
error(false, "IMMU: Attempt to execute user page 0x%08x\n", addr);
page_fault = true;
}
// Page not executable
else if ((pte & (PAGE_EXEC)) != (PAGE_EXEC))
{
page_fault = true;
}
}
// User mode
else
{
// User mode page not executable
if ((pte & (PAGE_EXEC | PAGE_USER)) != (PAGE_EXEC | PAGE_USER))
{
page_fault = true;
}
}
if (page_fault)
{
*physical = 0xFFFFFFFF;
exception(MCAUSE_PAGE_FAULT_INST, addr, addr);
return 0;
}
uint32_t pgoff = addr & (MMU_PGSIZE-1);
uint32_t pgbase = pte >> MMU_PGSHIFT << MMU_PGSHIFT;
uint32_t paddr = pgbase + pgoff;
DPRINTF(LOG_MMU, ("IMMU: Lookup VA %x -> PA %x\n", addr, paddr));
*physical = paddr;
return 1;
}
//-----------------------------------------------------------------
// mmu_d_translate: Translate load store
//-----------------------------------------------------------------
int Riscv::mmu_d_translate(uint32_t pc, uint32_t addr, uint32_t *physical, int writeNotRead)
{
bool page_fault = false;
// Machine - no MMU
if (m_csr_mpriv > PRIV_SUPER)
{
*physical = addr;
return 1;
}
uint32_t pte = mmu_walk(addr);
// Reserved configurations
if (((pte & (PAGE_EXEC | PAGE_READ | PAGE_WRITE)) == PAGE_WRITE) ||
((pte & (PAGE_EXEC | PAGE_READ | PAGE_WRITE)) == (PAGE_EXEC | PAGE_WRITE)))
{
page_fault = true;
}
// Supervisor mode
else if (m_csr_mpriv == PRIV_SUPER)
{
// User page access - super mode access not enabled
if ((pte & PAGE_USER) && !(m_csr_msr & SR_SUM))
{
error(false, "MMU_D: PC=%08x Access %08x - User page access by super\n", pc, addr);
}
else if ((writeNotRead && ((pte & (PAGE_WRITE)) != (PAGE_WRITE))) ||
(!writeNotRead && ((pte & (PAGE_READ)) != (PAGE_READ))))
{
page_fault = true;
}
}
// User mode
else
{
if ((writeNotRead && ((pte & (PAGE_WRITE | PAGE_USER)) != (PAGE_WRITE | PAGE_USER))) ||
(!writeNotRead && ((pte & (PAGE_READ | PAGE_USER)) != (PAGE_READ | PAGE_USER))))
{
page_fault = true;
}
}
if (page_fault)
{
*physical = 0xFFFFFFFF;
exception(writeNotRead ? MCAUSE_PAGE_FAULT_STORE : MCAUSE_PAGE_FAULT_LOAD, pc, addr);
return 0;
}
uint32_t pgoff = addr & (MMU_PGSIZE-1);
uint32_t pgbase = pte >> MMU_PGSHIFT << MMU_PGSHIFT;
uint32_t paddr = pgbase + pgoff;
DPRINTF(LOG_MMU, ("DMMU: Lookup VA %x -> PA %x\n", addr, paddr));
*physical = paddr;
return 1;
}
#endif
//-----------------------------------------------------------------
// load: Perform a load operation (with optional MMU lookup)
//-----------------------------------------------------------------
int Riscv::load(uint32_t pc, uint32_t address, uint32_t *result, int width, bool signedLoad)
{
uint32_t physical = address;
#ifdef CONFIG_MMU
// Translate addresses if required
if (!mmu_d_translate(pc, address, &physical, 0))
return 0;
#endif
DPRINTF(LOG_MEM, ("LOAD: VA 0x%08x PA 0x%08x Width %d\n", address, physical, width));
event_push(COSIM_EVENT_LOAD, physical & ~3, 0);
m_stats[STATS_LOADS]++;
for (int j=0;j<m_mem_regions;j++)
if (physical >= m_mem_base[j] && physical < (m_mem_base[j] + m_mem_size[j]))
{
*result = m_mem[j]->load(physical - m_mem_base[j], width, signedLoad);
DPRINTF(LOG_MEM, ("LOAD_RESULT: 0x%08x\n",*result));
event_push(COSIM_EVENT_LOAD_RESULT, *result, 0);
return 1;
}
error(false, "%08x: Bad memory access 0x%x\n", pc, address);
return 0;
}
//-----------------------------------------------------------------
// store: Perform a store operation (with optional MMU lookup)
//-----------------------------------------------------------------
int Riscv::store(uint32_t pc, uint32_t address, uint32_t data, int width)
{
uint32_t physical = address;
#ifdef CONFIG_MMU
// Translate addresses if required
if (!mmu_d_translate(pc, address, &physical, 1))
return 0;
#endif
DPRINTF(LOG_MEM, ("STORE: VA 0x%08x PA 0x%08x Value 0x%08x Width %d\n", address, physical, data, width));
m_stats[STATS_STORES]++;
if (width == 1)
event_push(COSIM_EVENT_STORE, physical & ~3, data & 0xFF);
else if (width == 2)
event_push(COSIM_EVENT_STORE, physical & ~3, data & 0xFFFF);
else
event_push(COSIM_EVENT_STORE, physical, data);
for (int j=0;j<m_mem_regions;j++)
if (physical >= m_mem_base[j] && physical < (m_mem_base[j] + m_mem_size[j]))
{
m_mem[j]->store(physical - m_mem_base[j], data, width);
return 1;
}
error(false, "%08x: Bad memory access 0x%x\n", pc, address);
return 0;
}
//-----------------------------------------------------------------
// access_csr: Perform CSR access
//-----------------------------------------------------------------
uint32_t Riscv::access_csr(uint32_t address, uint32_t data, bool set, bool clr)
{
uint32_t result = 0;
#define CSR_STD(name, var_name) \
case CSR_ ##name: \
{ \
data &= CSR_ ##name## _MASK; \
result = var_name & CSR_ ##name## _MASK; \
if (set && clr) \
var_name = data; \
else if (set) \
var_name |= data; \
else if (clr) \
var_name &= ~data; \
} \
break;
#define CSR_CONST(name, value) \
case CSR_ ##name: \
{ \
result = value; \
} \
break;
switch (address & 0xFFF)
{
//--------------------------------------------------------
// Simulation control
//--------------------------------------------------------
case CSR_DSCRATCH:
case CSR_SIM_CTRL:
switch (data & 0xFF000000)
{
case CSR_SIM_CTRL_EXIT:
stats_dump();
exit(data & 0xFF);
break;
case CSR_SIM_CTRL_PUTC:
if (m_console)
m_console->putchar(data & 0xFF);
else
fprintf(stderr, "%c", (data & 0xFF));
break;
case CSR_SIM_CTRL_GETC:
if (m_console)
result = m_console->getchar();
else
result = 0;
break;
case CSR_SIM_CTRL_TRACE:
enable_trace(data & 0xFF);
break;
case CSR_SIM_PRINTF:
{
uint32_t fmt_addr = m_gpr[10];
uint32_t arg1 = m_gpr[11];
uint32_t arg2 = m_gpr[12];
uint32_t arg3 = m_gpr[13];
uint32_t arg4 = m_gpr[14];
#ifdef CONFIG_MMU
{
uint32_t pte = mmu_walk(fmt_addr);
uint32_t pgoff = fmt_addr & (MMU_PGSIZE-1);
uint32_t pgbase = pte >> MMU_PGSHIFT << MMU_PGSHIFT;
if (pte != 0) fmt_addr = pgbase + pgoff;
}
#endif
char fmt_str[1024];
int idx = 0;
while (idx < (sizeof(fmt_str)-1))
{
fmt_str[idx] = read(fmt_addr++);
if (fmt_str[idx] == 0)
break;
idx += 1;
}
char out_str[1024];
sprintf(out_str, fmt_str, arg1, arg2, arg3, arg4);
printf("%s",out_str);
}
break;
}
break;
//--------------------------------------------------------
// Standard - Machine
//--------------------------------------------------------
CSR_STD(MEPC, m_csr_mepc)
CSR_STD(MTVEC, m_csr_mevec)
CSR_STD(MCAUSE, m_csr_mcause)
CSR_STD(MSTATUS, m_csr_msr)
CSR_STD(MIP, m_csr_mip)
CSR_STD(MIE, m_csr_mie)
CSR_CONST(MISA, MISA_VALUE)
CSR_STD(MIDELEG, m_csr_mideleg)
CSR_STD(MEDELEG, m_csr_medeleg)
CSR_STD(MSCRATCH,m_csr_mscratch)
CSR_CONST(MHARTID, MHARTID_VALUE)
//--------------------------------------------------------
// Standard - Supervisor
//--------------------------------------------------------
CSR_STD(SEPC, m_csr_sepc)
CSR_STD(STVEC, m_csr_sevec)
CSR_STD(SCAUSE, m_csr_scause)
CSR_STD(SIP, m_csr_mip)
CSR_STD(SIE, m_csr_mie)
CSR_STD(SATP, m_csr_satp)
CSR_STD(STVAL, m_csr_stval)
CSR_STD(SSCRATCH,m_csr_sscratch)
CSR_STD(SSTATUS, m_csr_msr)
//--------------------------------------------------------
// Extensions
//--------------------------------------------------------
CSR_CONST(PMPCFG0, 0)
CSR_CONST(PMPCFG1, 0)
CSR_CONST(PMPCFG2, 0)
CSR_CONST(PMPADDR0, 0)
case CSR_MTIME:
data &= CSR_MTIME_MASK;
result = m_csr_mtime;
// Non-std behaviour - write to CSR_TIME gives next interrupt threshold
if (set && data != 0)
{
m_csr_mtimecmp = data;
// Clear interrupt pending
m_csr_mip &= ~((m_csr_mideleg & SR_IP_STIP) ? SR_IP_STIP : SR_IP_MTIP);
}
break;
case CSR_MTIMEH:
result = m_csr_mtime >> 32;
break;
default:
error(false, "*** CSR address not supported %08x [PC=%08x]\n", address, m_pc);
break;
}
return result;
}
//-----------------------------------------------------------------
// exception: Handle an exception or interrupt
//-----------------------------------------------------------------
void Riscv::exception(uint32_t cause, uint32_t pc, uint32_t badaddr /*= 0*/)
{
uint32_t deleg;
uint32_t bit;
// Interrupt
if (cause >= MCAUSE_INTERRUPT)
{
deleg = m_csr_mideleg;
bit = 1 << (cause - MCAUSE_INTERRUPT);
}
// Exception
else
{
deleg = m_csr_medeleg;
bit = 1 << cause;
}
// Exception delegated to supervisor mode
if (m_csr_mpriv <= PRIV_SUPER && (deleg & bit))
{
uint32_t s = m_csr_msr;
// Interrupt save and disable
s &= ~SR_SPIE;
s |= (s & SR_SIE) ? SR_SPIE : 0;
s &= ~SR_SIE;
// Record previous priviledge level
s &= ~SR_SPP;
s |= (m_csr_mpriv == PRIV_SUPER) ? SR_SPP : 0;
// Raise priviledge to supervisor level
m_csr_mpriv = PRIV_SUPER;
m_csr_msr = s;
m_csr_sepc = pc;
m_csr_scause = cause;
m_csr_stval = badaddr;
// Set new PC
m_pc = m_csr_sevec;
}
// Machine mode
else
{
uint32_t s = m_csr_msr;
// Interrupt save and disable
s &= ~SR_MPIE;
s |= (s & SR_MIE) ? SR_MPIE : 0;
s &= ~SR_MIE;
// Record previous priviledge level
s &= ~SR_MPP;
s |= (m_csr_mpriv << SR_MPP_SHIFT);
// Raise priviledge to machine level
m_csr_mpriv = PRIV_MACHINE;
m_csr_msr = s;
m_csr_mepc = pc;
m_csr_mcause = cause;
// Set new PC
m_pc = m_csr_mevec; // TODO: This should be a product of the except num
}
}
//-----------------------------------------------------------------
// execute: Instruction execution stage
//-----------------------------------------------------------------
void Riscv::execute(void)
{
uint32_t phy_pc = m_pc;
#ifdef CONFIG_MMU
// Translate PC to physical address
if (!mmu_i_translate(m_pc, &phy_pc))
return ;
#endif
// Get opcode at current PC
uint32_t opcode = get_opcode(phy_pc);
m_pc_x = m_pc;
// Extract registers
int rd = (opcode & OPCODE_RD_MASK) >> OPCODE_RD_SHIFT;
int rs1 = (opcode & OPCODE_RS1_MASK) >> OPCODE_RS1_SHIFT;
int rs2 = (opcode & OPCODE_RS2_MASK) >> OPCODE_RS2_SHIFT;
// Extract immediates
int typei_imm = ((signed)(opcode & OPCODE_TYPEI_IMM_MASK)) >> OPCODE_TYPEI_IMM_SHIFT;
int typeu_imm = ((signed)(opcode & OPCODE_TYPEU_IMM_MASK)) >> OPCODE_TYPEU_IMM_SHIFT;
int imm20 = typeu_imm << OPCODE_TYPEU_IMM_SHIFT;
int imm12 = typei_imm;
int bimm = OPCODE_SBTYPE_IMM(opcode);
int jimm20 = OPCODE_UJTYPE_IMM(opcode);
int storeimm = OPCODE_STYPE_IMM(opcode);
int shamt = ((signed)(opcode & OPCODE_SHAMT_MASK)) >> OPCODE_SHAMT_SHIFT;
// Retrieve registers
uint32_t reg_rd = 0;
uint32_t reg_rs1 = m_gpr[rs1];
uint32_t reg_rs2 = m_gpr[rs2];
uint32_t pc = m_pc;
bool take_exception = false;
DPRINTF(LOG_OPCODES,( "%08x: %08x\n", pc, opcode));
DPRINTF(LOG_OPCODES,( " rd(%d) r%d = %d, r%d = %d\n", rd, rs1, reg_rs1, rs2, reg_rs2));
// As RVC is not supported, fault on opcode which is all zeros
if (opcode == 0)
{
error(false, "Bad instruction @ %x\n", pc);
exception(MCAUSE_ILLEGAL_INSTRUCTION, pc);
m_fault = true;
take_exception = true;
}
else if ((opcode & INST_ANDI_MASK) == INST_ANDI)
{
// ['rd', 'rs1', 'imm12']
DPRINTF(LOG_INST,("%08x: andi r%d, r%d, %d\n", pc, rd, rs1, imm12));
INST_STAT(ENUM_INST_ANDI);
reg_rd = reg_rs1 & imm12;
pc += 4;
}
else if ((opcode & INST_ORI_MASK) == INST_ORI)
{
// ['rd', 'rs1', 'imm12']
DPRINTF(LOG_INST,("%08x: ori r%d, r%d, %d\n", pc, rd, rs1, imm12));
INST_STAT(ENUM_INST_ORI);
reg_rd = reg_rs1 | imm12;
pc += 4;
}
else if ((opcode & INST_XORI_MASK) == INST_XORI)
{
// ['rd', 'rs1', 'imm12']
DPRINTF(LOG_INST,("%08x: xori r%d, r%d, %d\n", pc, rd, rs1, imm12));
INST_STAT(ENUM_INST_XORI);
reg_rd = reg_rs1 ^ imm12;
pc += 4;
}
else if ((opcode & INST_ADDI_MASK) == INST_ADDI)
{
// ['rd', 'rs1', 'imm12']
DPRINTF(LOG_INST,("%08x: addi r%d, r%d, %d\n", pc, rd, rs1, imm12));
INST_STAT(ENUM_INST_ADDI);
reg_rd = reg_rs1 + imm12;
pc += 4;
}
else if ((opcode & INST_SLTI_MASK) == INST_SLTI)
{
// ['rd', 'rs1', 'imm12']
DPRINTF(LOG_INST,("%08x: slti r%d, r%d, %d\n", pc, rd, rs1, imm12));
INST_STAT(ENUM_INST_SLTI);
reg_rd = (signed)reg_rs1 < (signed)imm12;
pc += 4;
}
else if ((opcode & INST_SLTIU_MASK) == INST_SLTIU)
{
// ['rd', 'rs1', 'imm12']
DPRINTF(LOG_INST,("%08x: sltiu r%d, r%d, %d\n", pc, rd, rs1, (unsigned)imm12));
INST_STAT(ENUM_INST_SLTIU);
reg_rd = (unsigned)reg_rs1 < (unsigned)imm12;
pc += 4;
}
else if ((opcode & INST_SLLI_MASK) == INST_SLLI)
{
// ['rd', 'rs1']
DPRINTF(LOG_INST,("%08x: slli r%d, r%d, %d\n", pc, rd, rs1, shamt));
INST_STAT(ENUM_INST_SLLI);
reg_rd = reg_rs1 << shamt;
pc += 4;
}
else if ((opcode & INST_SRLI_MASK) == INST_SRLI)
{
// ['rd', 'rs1', 'shamt']
DPRINTF(LOG_INST,("%08x: srli r%d, r%d, %d\n", pc, rd, rs1, shamt));
INST_STAT(ENUM_INST_SRLI);
reg_rd = (unsigned)reg_rs1 >> shamt;
pc += 4;
}
else if ((opcode & INST_SRAI_MASK) == INST_SRAI)
{
// ['rd', 'rs1', 'shamt']
DPRINTF(LOG_INST,("%08x: srai r%d, r%d, %d\n", pc, rd, rs1, shamt));
INST_STAT(ENUM_INST_SRAI);
reg_rd = (signed)reg_rs1 >> shamt;
pc += 4;
}
else if ((opcode & INST_LUI_MASK) == INST_LUI)
{
// ['rd', 'imm20']
DPRINTF(LOG_INST,("%08x: lui r%d, 0x%x\n", pc, rd, imm20));
INST_STAT(ENUM_INST_LUI);
reg_rd = imm20;
pc += 4;
}
else if ((opcode & INST_AUIPC_MASK) == INST_AUIPC)
{
// ['rd', 'imm20']
DPRINTF(LOG_INST,("%08x: auipc r%d, 0x%x\n", pc, rd, imm20));
INST_STAT(ENUM_INST_AUIPC);
reg_rd = imm20 + pc;
pc += 4;
}
else if ((opcode & INST_ADD_MASK) == INST_ADD)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: add r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_ADD);
reg_rd = reg_rs1 + reg_rs2;
pc += 4;
}
else if ((opcode & INST_SUB_MASK) == INST_SUB)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: sub r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_SUB);
reg_rd = reg_rs1 - reg_rs2;
pc += 4;
}
else if ((opcode & INST_SLT_MASK) == INST_SLT)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: slt r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_SLT);
reg_rd = (signed)reg_rs1 < (signed)reg_rs2;
pc += 4;
}
else if ((opcode & INST_SLTU_MASK) == INST_SLTU)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: sltu r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_SLTU);
reg_rd = (unsigned)reg_rs1 < (unsigned)reg_rs2;
pc += 4;
}
else if ((opcode & INST_XOR_MASK) == INST_XOR)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: xor r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_XOR);
reg_rd = reg_rs1 ^ reg_rs2;
pc += 4;
}
else if ((opcode & INST_OR_MASK) == INST_OR)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: or r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_OR);
reg_rd = reg_rs1 | reg_rs2;
pc += 4;
}
else if ((opcode & INST_AND_MASK) == INST_AND)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: and r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_AND);
reg_rd = reg_rs1 & reg_rs2;
pc += 4;
}
else if ((opcode & INST_SLL_MASK) == INST_SLL)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: sll r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_SLL);
reg_rd = reg_rs1 << reg_rs2;
pc += 4;
}
else if ((opcode & INST_SRL_MASK) == INST_SRL)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: srl r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_SRL);
reg_rd = (unsigned)reg_rs1 >> reg_rs2;
pc += 4;
}
else if ((opcode & INST_SRA_MASK) == INST_SRA)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: sra r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_SRA);
reg_rd = (signed)reg_rs1 >> reg_rs2;
pc += 4;
}
else if ((opcode & INST_JAL_MASK) == INST_JAL)
{
// ['rd', 'jimm20']
DPRINTF(LOG_INST,("%08x: jal r%d, %d\n", pc, rd, jimm20));
INST_STAT(ENUM_INST_JAL);
reg_rd = pc + 4;
pc+= jimm20;
m_stats[STATS_BRANCHES]++;
}
else if ((opcode & INST_JALR_MASK) == INST_JALR)
{
// ['rd', 'rs1', 'imm12']
DPRINTF(LOG_INST,("%08x: jalr r%d, r%d\n", pc, rs1, imm12));
INST_STAT(ENUM_INST_JALR);
reg_rd = pc + 4;
pc = (reg_rs1 + imm12) & ~1;
m_stats[STATS_BRANCHES]++;
}
else if ((opcode & INST_BEQ_MASK) == INST_BEQ)
{
// ['bimm12hi', 'rs1', 'rs2', 'bimm12lo']
DPRINTF(LOG_INST,("%08x: beq r%d, r%d, %d\n", pc, rs1, rs2, bimm));
INST_STAT(ENUM_INST_BEQ);
if (reg_rs1 == reg_rs2)
pc += bimm;
else
pc += 4;
// No writeback
rd = 0;
m_stats[STATS_BRANCHES]++;
}
else if ((opcode & INST_BNE_MASK) == INST_BNE)
{
// ['bimm12hi', 'rs1', 'rs2', 'bimm12lo']
DPRINTF(LOG_INST,("%08x: bne r%d, r%d, %d\n", pc, rs1, rs2, bimm));
INST_STAT(ENUM_INST_BNE);
if (reg_rs1 != reg_rs2)
pc += bimm;
else
pc += 4;
// No writeback
rd = 0;
m_stats[STATS_BRANCHES]++;
}
else if ((opcode & INST_BLT_MASK) == INST_BLT)
{
// ['bimm12hi', 'rs1', 'rs2', 'bimm12lo']
DPRINTF(LOG_INST,("%08x: blt r%d, r%d, %d\n", pc, rs1, rs2, bimm));
INST_STAT(ENUM_INST_BLT);
if ((signed)reg_rs1 < (signed)reg_rs2)
pc += bimm;
else
pc += 4;
// No writeback
rd = 0;
m_stats[STATS_BRANCHES]++;
}
else if ((opcode & INST_BGE_MASK) == INST_BGE)
{
// ['bimm12hi', 'rs1', 'rs2', 'bimm12lo']
DPRINTF(LOG_INST,("%08x: bge r%d, r%d, %d\n", pc, rs1, rs2, bimm));
INST_STAT(ENUM_INST_BGE);
if ((signed)reg_rs1 >= (signed)reg_rs2)
pc += bimm;
else
pc += 4;
// No writeback
rd = 0;
m_stats[STATS_BRANCHES]++;
}
else if ((opcode & INST_BLTU_MASK) == INST_BLTU)
{
// ['bimm12hi', 'rs1', 'rs2', 'bimm12lo']
DPRINTF(LOG_INST,("%08x: bltu r%d, r%d, %d\n", pc, rs1, rs2, bimm));
INST_STAT(ENUM_INST_BLTU);
if ((unsigned)reg_rs1 < (unsigned)reg_rs2)
pc += bimm;
else
pc += 4;
// No writeback
rd = 0;
m_stats[STATS_BRANCHES]++;
}
else if ((opcode & INST_BGEU_MASK) == INST_BGEU)
{
// ['bimm12hi', 'rs1', 'rs2', 'bimm12lo']
DPRINTF(LOG_INST,("%08x: bgeu r%d, r%d, %d\n", pc, rs1, rs2, bimm));
INST_STAT(ENUM_INST_BGEU);
if ((unsigned)reg_rs1 >= (unsigned)reg_rs2)
pc += bimm;
else
pc += 4;
// No writeback
rd = 0;
m_stats[STATS_BRANCHES]++;
}
else if ((opcode & INST_LB_MASK) == INST_LB)
{
// ['rd', 'rs1', 'imm12']
DPRINTF(LOG_INST,("%08x: lb r%d, %d(r%d)\n", pc, rd, imm12, rs1));
INST_STAT(ENUM_INST_LB);
if (load(pc, reg_rs1 + imm12, ®_rd, 1, true))
pc += 4;
else
return;
}
else if ((opcode & INST_LH_MASK) == INST_LH)
{
// ['rd', 'rs1', 'imm12']
DPRINTF(LOG_INST,("%08x: lh r%d, %d(r%d)\n", pc, rd, imm12, rs1));
INST_STAT(ENUM_INST_LH);
if (load(pc, reg_rs1 + imm12, ®_rd, 2, true))
pc += 4;
else
return;
}
else if ((opcode & INST_LW_MASK) == INST_LW)
{
// ['rd', 'rs1', 'imm12']
INST_STAT(ENUM_INST_LW);
DPRINTF(LOG_INST,("%08x: lw r%d, %d(r%d)\n", pc, rd, imm12, rs1));
if (load(pc, reg_rs1 + imm12, ®_rd, 4, true))
pc += 4;
else
return;
}
else if ((opcode & INST_LBU_MASK) == INST_LBU)
{
// ['rd', 'rs1', 'imm12']
DPRINTF(LOG_INST,("%08x: lbu r%d, %d(r%d)\n", pc, rd, imm12, rs1));
INST_STAT(ENUM_INST_LBU);
if (load(pc, reg_rs1 + imm12, ®_rd, 1, false))
pc += 4;
else
return;
}
else if ((opcode & INST_LHU_MASK) == INST_LHU)
{
// ['rd', 'rs1', 'imm12']
DPRINTF(LOG_INST,("%08x: lhu r%d, %d(r%d)\n", pc, rd, imm12, rs1));
INST_STAT(ENUM_INST_LHU);
if (load(pc, reg_rs1 + imm12, ®_rd, 2, false))
pc += 4;
else
return;
}
else if ((opcode & INST_LWU_MASK) == INST_LWU)
{
// ['rd', 'rs1', 'imm12']
DPRINTF(LOG_INST,("%08x: lwu r%d, %d(r%d)\n", pc, rd, imm12, rs1));
INST_STAT(ENUM_INST_LWU);
if (load(pc, reg_rs1 + imm12, ®_rd, 4, false))
pc += 4;
else
return;
}
else if ((opcode & INST_SB_MASK) == INST_SB)
{
// ['imm12hi', 'rs1', 'rs2', 'imm12lo']
DPRINTF(LOG_INST,("%08x: sb %d(r%d), r%d\n", pc, storeimm, rs1, rs2));
INST_STAT(ENUM_INST_SB);
if (store(pc, reg_rs1 + storeimm, reg_rs2, 1))
pc += 4;
else
return ;
// No writeback
rd = 0;
}
else if ((opcode & INST_SH_MASK) == INST_SH)
{
// ['imm12hi', 'rs1', 'rs2', 'imm12lo']
DPRINTF(LOG_INST,("%08x: sh %d(r%d), r%d\n", pc, storeimm, rs1, rs2));
INST_STAT(ENUM_INST_SH);
if (store(pc, reg_rs1 + storeimm, reg_rs2, 2))
pc += 4;
else
return ;
// No writeback
rd = 0;
}
else if ((opcode & INST_SW_MASK) == INST_SW)
{
// ['imm12hi', 'rs1', 'rs2', 'imm12lo']
DPRINTF(LOG_INST,("%08x: sw %d(r%d), r%d\n", pc, storeimm, rs1, rs2));
INST_STAT(ENUM_INST_SW);
if (store(pc, reg_rs1 + storeimm, reg_rs2, 4))
pc += 4;
else
return ;
// No writeback
rd = 0;
}
else if ((opcode & INST_MUL_MASK) == INST_MUL)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: mul r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_MUL);
reg_rd = (signed)reg_rs1 * (signed)reg_rs2;
pc += 4;
}
else if ((opcode & INST_MULH_MASK) == INST_MULH)
{
// ['rd', 'rs1', 'rs2']
long long res = ((long long) (int)reg_rs1) * ((long long)(int)reg_rs2);
INST_STAT(ENUM_INST_MULH);
DPRINTF(LOG_INST,("%08x: mulh r%d, r%d, r%d\n", pc, rd, rs1, rs2));
reg_rd = (int)(res >> 32);
pc += 4;
}
else if ((opcode & INST_MULHSU_MASK) == INST_MULHSU)
{
// ['rd', 'rs1', 'rs2']
long long res = ((long long) (int)reg_rs1) * ((unsigned long long)(unsigned)reg_rs2);
INST_STAT(ENUM_INST_MULHSU);
DPRINTF(LOG_INST,("%08x: mulhsu r%d, r%d, r%d\n", pc, rd, rs1, rs2));
reg_rd = (int)(res >> 32);
pc += 4;
}
else if ((opcode & INST_MULHU_MASK) == INST_MULHU)
{
// ['rd', 'rs1', 'rs2']
unsigned long long res = ((unsigned long long) (unsigned)reg_rs1) * ((unsigned long long)(unsigned)reg_rs2);
INST_STAT(ENUM_INST_MULHU);
DPRINTF(LOG_INST,("%08x: mulhu r%d, r%d, r%d\n", pc, rd, rs1, rs2));
reg_rd = (int)(res >> 32);
pc += 4;
}
else if ((opcode & INST_DIV_MASK) == INST_DIV)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: div r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_DIV);
if ((signed)reg_rs1 == INT32_MIN && (signed)reg_rs2 == -1)
reg_rd = reg_rs1;
else if (reg_rs2 != 0)
reg_rd = (signed)reg_rs1 / (signed)reg_rs2;
else
reg_rd = (unsigned)-1;
pc += 4;
}
else if ((opcode & INST_DIVU_MASK) == INST_DIVU)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: divu r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_DIVU);
if (reg_rs2 != 0)
reg_rd = (unsigned)reg_rs1 / (unsigned)reg_rs2;
else
reg_rd = (unsigned)-1;
pc += 4;
}
else if ((opcode & INST_REM_MASK) == INST_REM)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: rem r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_REM);
if((signed)reg_rs1 == INT32_MIN && (signed)reg_rs2 == -1)
reg_rd = 0;
else if (reg_rs2 != 0)
reg_rd = (signed)reg_rs1 % (signed)reg_rs2;
else
reg_rd = reg_rs1;
pc += 4;
}
else if ((opcode & INST_REMU_MASK) == INST_REMU)
{
// ['rd', 'rs1', 'rs2']
DPRINTF(LOG_INST,("%08x: remu r%d, r%d, r%d\n", pc, rd, rs1, rs2));
INST_STAT(ENUM_INST_REMU);
if (reg_rs2 != 0)
reg_rd = (unsigned)reg_rs1 % (unsigned)reg_rs2;
else
reg_rd = reg_rs1;
pc += 4;
}
else if ((opcode & INST_ECALL_MASK) == INST_ECALL)
{
DPRINTF(LOG_INST,("%08x: ecall\n", pc));
INST_STAT(ENUM_INST_ECALL);
exception(MCAUSE_ECALL_U + m_csr_mpriv, pc);
take_exception = true;
}
else if ((opcode & INST_EBREAK_MASK) == INST_EBREAK)
{
DPRINTF(LOG_INST,("%08x: ebreak\n", pc));
INST_STAT(ENUM_INST_EBREAK);
exception(MCAUSE_BREAKPOINT, pc);
take_exception = true;
m_break = true;
}
else if ((opcode & INST_MRET_MASK) == INST_MRET)
{
DPRINTF(LOG_INST,("%08x: mret\n", pc));
INST_STAT(ENUM_INST_MRET);
assert(m_csr_mpriv == PRIV_MACHINE);
uint32_t s = m_csr_msr;
uint32_t prev_prv = SR_GET_MPP(m_csr_msr);
// Interrupt enable pop
s &= ~SR_MIE;
s |= (s & SR_MPIE) ? SR_MIE : 0;
s |= SR_MPIE;
// Set next MPP to user mode
s &= ~SR_MPP;
s |= SR_MPP_U;
// Set privilege level to previous MPP
m_csr_mpriv = prev_prv;
m_csr_msr = s;
// Return to EPC
pc = m_csr_mepc;
}
else if ((opcode & INST_SRET_MASK) == INST_SRET)
{
DPRINTF(LOG_INST,("%08x: sret\n", pc));
INST_STAT(ENUM_INST_SRET);
assert(m_csr_mpriv == PRIV_SUPER);
uint32_t s = m_csr_msr;
uint32_t prev_prv = (m_csr_msr & SR_SPP) ? PRIV_SUPER : PRIV_USER;
// Interrupt enable pop
s &= ~SR_SIE;
s |= (s & SR_SPIE) ? SR_SIE : 0;
s |= SR_SPIE;
// Set next SPP to user mode
s &= ~SR_SPP;
// Set privilege level to previous MPP
m_csr_mpriv = prev_prv;
m_csr_msr = s;
// Return to EPC
pc = m_csr_sepc;
}
else if ( ((opcode & INST_SFENCE_MASK) == INST_SFENCE) ||
((opcode & INST_FENCE_MASK) == INST_FENCE) ||
((opcode & INST_IFENCE_MASK) == INST_IFENCE))
{
DPRINTF(LOG_INST,("%08x: fence\n", pc));
INST_STAT(ENUM_INST_FENCE);
pc += 4;
}
else if ((opcode & INST_CSRRW_MASK) == INST_CSRRW)
{
DPRINTF(LOG_INST,("%08x: csrw r%d, r%d, 0x%x\n", pc, rd, rs1, imm12));
INST_STAT(ENUM_INST_CSRRW);
reg_rd = access_csr(imm12, reg_rs1, true, true);
pc += 4;
}
else if ((opcode & INST_CSRRS_MASK) == INST_CSRRS)
{
DPRINTF(LOG_INST,("%08x: csrs r%d, r%d, 0x%x\n", pc, rd, rs1, imm12));
INST_STAT(ENUM_INST_CSRRS);
reg_rd = access_csr(imm12, reg_rs1, true, false);
pc += 4;
}
else if ((opcode & INST_CSRRC_MASK) == INST_CSRRC)
{
DPRINTF(LOG_INST,("%08x: csrc r%d, r%d, 0x%x\n", pc, rd, rs1, imm12));
INST_STAT(ENUM_INST_CSRRC);
reg_rd = access_csr(imm12, reg_rs1, false, true);
pc += 4;
}
else if ((opcode & INST_CSRRWI_MASK) == INST_CSRRWI)
{
DPRINTF(LOG_INST,("%08x: csrwi r%d, %d, 0x%x\n", pc, rd, rs1, imm12));
INST_STAT(ENUM_INST_CSRRWI);
reg_rd = access_csr(imm12, rs1, true, true);
pc += 4;
}
else if ((opcode & INST_CSRRSI_MASK) == INST_CSRRSI)
{
DPRINTF(LOG_INST,("%08x: csrsi r%d, %d, 0x%x\n", pc, rd, rs1, imm12));
INST_STAT(ENUM_INST_CSRRSI);
reg_rd = access_csr(imm12, rs1, true, false);
pc += 4;
}
else if ((opcode & INST_CSRRCI_MASK) == INST_CSRRCI)
{
DPRINTF(LOG_INST,("%08x: csrci r%d, %d, 0x%x\n", pc, rd, rs1, imm12));
INST_STAT(ENUM_INST_CSRRCI);
reg_rd = access_csr(imm12, rs1, false, true);
pc += 4;
}
else if ((opcode & INST_WFI_MASK) == INST_WFI)
{
DPRINTF(LOG_INST,("%08x: wfi\n", pc));
INST_STAT(ENUM_INST_WFI);
pc += 4;
}
else
{
error(false, "Bad instruction @ %x (opcode %x)\n", pc, opcode);
exception(MCAUSE_ILLEGAL_INSTRUCTION, pc);
m_fault = true;
take_exception = true;
}
if (rd != 0)
m_gpr[rd] = reg_rd;
// Pending interrupt
if (!take_exception && (m_csr_mip & m_csr_mie))
{
uint32_t pending_interrupts = (m_csr_mip & m_csr_mie);
uint32_t m_enabled = m_csr_mpriv < PRIV_MACHINE || (m_csr_mpriv == PRIV_MACHINE && (m_csr_msr & SR_MIE));
uint32_t s_enabled = m_csr_mpriv < PRIV_SUPER || (m_csr_mpriv == PRIV_SUPER && (m_csr_msr & SR_SIE));
uint32_t m_interrupts = pending_interrupts & ~m_csr_mideleg & -m_enabled;
uint32_t s_interrupts = pending_interrupts & m_csr_mideleg & -s_enabled;
uint32_t interrupts = m_interrupts ? m_interrupts : s_interrupts;
// Interrupt pending and mask enabled
if (interrupts)
{
//printf("Take Interrupt...: %08x\n", interrupts);
int i;
for (i=IRQ_MIN;i<IRQ_MAX;i++)
{
if (interrupts & (1 << i))
{
// Only service one interrupt per cycle
DPRINTF(LOG_INST,( "Interrupt%d taken...\n", i));
exception(MCAUSE_INTERRUPT + i, pc);
take_exception = true;
break;
}
}
}
}
// Stats interface
if (m_stats_if)
m_stats_if->execute(m_pc, opcode);
if (!take_exception)
m_pc = pc;
}
//-----------------------------------------------------------------
// step: Step through one instruction
//-----------------------------------------------------------------
void Riscv::step(void)
{
m_stats[STATS_INSTRUCTIONS]++;
// Execute instruction at current PC
execute();
// Increment timer counter
m_csr_mtime++;
// Timer should generate a interrupt?
// Limited internal timer, truncate to 32-bits
m_csr_mtime &= 0xFFFFFFFF;
if (m_csr_mtime == m_csr_mtimecmp)
m_csr_mip |= (m_csr_mideleg & SR_IP_STIP) ? SR_IP_STIP : SR_IP_MTIP;
// Dump state
if (TRACE_ENABLED(LOG_REGISTERS))
{
// Register trace
int i;
for (i=0;i<REGISTERS;i+=4)
{
DPRINTF(LOG_REGISTERS,( " %d: ", i));
DPRINTF(LOG_REGISTERS,( " %08x %08x %08x %08x\n", m_gpr[i+0], m_gpr[i+1], m_gpr[i+2], m_gpr[i+3]));
}
}
// Breakpoint hit?
if (m_has_breakpoints && check_breakpoint(m_pc_x))
m_break = true;
}
//-----------------------------------------------------------------
// set_interrupt: Register pending interrupt
//-----------------------------------------------------------------
void Riscv::set_interrupt(int irq)
{
assert(irq == 0);
m_csr_mip |= SR_IP_MEIP;
}
//-----------------------------------------------------------------
// stats_reset: Reset runtime stats
//-----------------------------------------------------------------
void Riscv::stats_reset(void)
{
// Clear stats
for (int i=STATS_MIN;i<STATS_MAX;i++)
m_stats[i] = 0;
}
//-----------------------------------------------------------------
// stats_dump: Show execution stats
//-----------------------------------------------------------------
void Riscv::stats_dump(void)
{
// Stats interface
if (m_stats_if)
{
m_stats_if->print();
m_stats_if->reset();
}
else
{
printf( "Runtime Stats:\n");
printf( "- Total Instructions %d\n", m_stats[STATS_INSTRUCTIONS]);
if (m_stats[STATS_INSTRUCTIONS] > 0)
{
printf( "- Loads %d (%d%%)\n", m_stats[STATS_LOADS], (m_stats[STATS_LOADS] * 100) / m_stats[STATS_INSTRUCTIONS]);
printf( "- Stores %d (%d%%)\n", m_stats[STATS_STORES], (m_stats[STATS_STORES] * 100) / m_stats[STATS_INSTRUCTIONS]);
printf( "- Branches Operations %d (%d%%)\n", m_stats[STATS_BRANCHES], (m_stats[STATS_BRANCHES] * 100) / m_stats[STATS_INSTRUCTIONS]);
}
}
stats_reset();
}