Add timer and soft IRQ support to rvcpp. Relevant sw_testcases now pass.
This commit is contained in:
		
							parent
							
								
									b1be56fe94
								
							
						
					
					
						commit
						a313493371
					
				| 
						 | 
					@ -5,7 +5,7 @@ EXECUTABLE:=rvcpp
 | 
				
			||||||
.PHONY: all clean
 | 
					.PHONY: all clean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
all:
 | 
					all:
 | 
				
			||||||
	g++ -std=c++17 -O3 -Wall -Wno-parentheses -I $(PWD)/include $(SRCS) -o $(EXECUTABLE)
 | 
						g++ -std=c++17 -O3 -Wall -Wno-parentheses -I include $(SRCS) -o $(EXECUTABLE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
clean:
 | 
					clean:
 | 
				
			||||||
	rm -f $(EXECUTABLE)
 | 
						rm -f $(EXECUTABLE)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,12 @@
 | 
				
			||||||
#include "rv_types.h"
 | 
					#include "rv_types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RVCSR {
 | 
					class RVCSR {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Latched IRQ signals into core
 | 
				
			||||||
 | 
						bool irq_t;
 | 
				
			||||||
 | 
						bool irq_s;
 | 
				
			||||||
 | 
						bool irq_e;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Current core privilege level (M/S/U)
 | 
						// Current core privilege level (M/S/U)
 | 
				
			||||||
	uint priv;
 | 
						uint priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,6 +28,11 @@ class RVCSR {
 | 
				
			||||||
	std::optional<ux_t> pending_write_addr;
 | 
						std::optional<ux_t> pending_write_addr;
 | 
				
			||||||
	ux_t pending_write_data;
 | 
						ux_t pending_write_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ux_t get_effective_xip();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Internal interface for updating trap state. Returns trap target pc.
 | 
				
			||||||
 | 
						ux_t trap_enter(uint xcause, ux_t xepc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	enum {
 | 
						enum {
 | 
				
			||||||
| 
						 | 
					@ -31,12 +42,15 @@ public:
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	RVCSR() {
 | 
						RVCSR() {
 | 
				
			||||||
 | 
							irq_t = false;
 | 
				
			||||||
 | 
							irq_s = false;
 | 
				
			||||||
 | 
							irq_e = false;
 | 
				
			||||||
		priv = 3;
 | 
							priv = 3;
 | 
				
			||||||
		mcycle = 0;
 | 
							mcycle = 0;
 | 
				
			||||||
		mcycleh = 0;
 | 
							mcycleh = 0;
 | 
				
			||||||
		minstret = 0;
 | 
							minstret = 0;
 | 
				
			||||||
		minstreth = 0;
 | 
							minstreth = 0;
 | 
				
			||||||
		mcountinhibit = 0;
 | 
							mcountinhibit = 0x5;
 | 
				
			||||||
		mstatus = 0;
 | 
							mstatus = 0;
 | 
				
			||||||
		mie = 0;
 | 
							mie = 0;
 | 
				
			||||||
		mip = 0;
 | 
							mip = 0;
 | 
				
			||||||
| 
						 | 
					@ -55,13 +69,36 @@ public:
 | 
				
			||||||
	// Returns false on permission/decode fail
 | 
						// Returns false on permission/decode fail
 | 
				
			||||||
	bool write(uint16_t addr, ux_t data, uint op=WRITE);
 | 
						bool write(uint16_t addr, ux_t data, uint op=WRITE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update trap state (including change of privilege level), return trap target PC
 | 
						// Determine target privilege level of an exception, update trap state
 | 
				
			||||||
	ux_t trap_enter(uint xcause, ux_t xepc);
 | 
						// (including change of privilege level), return trap target PC
 | 
				
			||||||
 | 
						ux_t trap_enter_exception(uint xcause, ux_t xepc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If there is currently a pending IRQ that must be entered, then
 | 
				
			||||||
 | 
						// determine its target privilege level, update trap state, and return
 | 
				
			||||||
 | 
						// trap target PC. Otherwise return None.
 | 
				
			||||||
 | 
						std::optional<ux_t> trap_check_enter_irq(ux_t xepc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update trap state, return mepc:
 | 
						// Update trap state, return mepc:
 | 
				
			||||||
	ux_t trap_mret();
 | 
						ux_t trap_mret();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uint getpriv() {
 | 
						uint get_true_priv() {
 | 
				
			||||||
		return priv;
 | 
							return priv;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void set_irq_t(bool irq) {
 | 
				
			||||||
 | 
							irq_t = irq;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void set_irq_s(bool irq) {
 | 
				
			||||||
 | 
							irq_s = irq;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void set_irq_e(bool irq) {
 | 
				
			||||||
 | 
							irq_e = irq;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ux_t get_xcause() {
 | 
				
			||||||
 | 
							return mcause;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,21 +78,104 @@ struct TBExitException {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct TBMemIO: MemBase32 {
 | 
					struct TBMemIO: MemBase32 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enum {
 | 
				
			||||||
 | 
							IO_PRINT_CHAR  = 0x000,
 | 
				
			||||||
 | 
							IO_PRINT_U32   = 0x004,
 | 
				
			||||||
 | 
							IO_EXIT        = 0x008,
 | 
				
			||||||
 | 
							IO_SET_SOFTIRQ = 0x010,
 | 
				
			||||||
 | 
							IO_CLR_SOFTIRQ = 0x014,
 | 
				
			||||||
 | 
							IO_GLOBMON_EN  = 0x018,
 | 
				
			||||||
 | 
							IO_SET_IRQ     = 0x020,
 | 
				
			||||||
 | 
							IO_CLR_IRQ     = 0x030,
 | 
				
			||||||
 | 
							IO_MTIME       = 0x100,
 | 
				
			||||||
 | 
							IO_MTIMEH      = 0x104,
 | 
				
			||||||
 | 
							IO_MTIMECMP    = 0x108,
 | 
				
			||||||
 | 
							IO_MTIMECMPH   = 0x10c,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint64_t mtime;
 | 
				
			||||||
 | 
						uint64_t mtimecmp;
 | 
				
			||||||
 | 
						bool softirq;
 | 
				
			||||||
 | 
						bool trace;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TBMemIO(bool trace_) {
 | 
				
			||||||
 | 
							mtime = 0;
 | 
				
			||||||
 | 
							mtimecmp = 0; // -1 would be better, but match tb and tests
 | 
				
			||||||
 | 
							softirq = false;
 | 
				
			||||||
 | 
							trace = trace_;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	virtual bool w32(ux_t addr, uint32_t data) {
 | 
						virtual bool w32(ux_t addr, uint32_t data) {
 | 
				
			||||||
		switch (addr) {
 | 
							switch (addr) {
 | 
				
			||||||
		case 0x0:
 | 
							case IO_PRINT_CHAR:
 | 
				
			||||||
			printf("%c", (char)data);
 | 
								if (trace)
 | 
				
			||||||
 | 
									printf("IO_PRINT_CHAR: %c\n", (char)data);
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									printf("%c", (char)data);
 | 
				
			||||||
			return true;
 | 
								return true;
 | 
				
			||||||
		case 0x4:
 | 
							case IO_PRINT_U32:
 | 
				
			||||||
			printf("%08x\n", data);
 | 
								if (trace)
 | 
				
			||||||
 | 
									printf("IO_PRINT_U32: %08x\n", data);
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
									printf("%08x\n", data);
 | 
				
			||||||
			return true;
 | 
								return true;
 | 
				
			||||||
		case 0x8:
 | 
							case IO_EXIT:
 | 
				
			||||||
			throw TBExitException(data);
 | 
								throw TBExitException(data);
 | 
				
			||||||
			return true;
 | 
								return true;
 | 
				
			||||||
 | 
							case IO_SET_SOFTIRQ:
 | 
				
			||||||
 | 
								softirq = softirq || (data & 0x1);
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							case IO_CLR_SOFTIRQ:
 | 
				
			||||||
 | 
								softirq = softirq && !(data & 0x1);
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							case IO_MTIME:
 | 
				
			||||||
 | 
								mtime = (mtime & 0xffffffff00000000ull) | data;
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							case IO_MTIMEH:
 | 
				
			||||||
 | 
								mtime = (mtime & 0x00000000ffffffffull) | ((uint64_t)data << 32);
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							case IO_MTIMECMP:
 | 
				
			||||||
 | 
								mtimecmp = (mtimecmp & 0xffffffff00000000ull) | data;
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
							case IO_MTIMECMPH:
 | 
				
			||||||
 | 
								mtimecmp = (mtimecmp & 0x00000000ffffffffull) | ((uint64_t)data << 32);
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual std::optional<uint32_t> r32(ux_t addr) {
 | 
				
			||||||
 | 
							switch(addr) {
 | 
				
			||||||
 | 
							case IO_MTIME:
 | 
				
			||||||
 | 
								return mtime & 0xffffffffull;
 | 
				
			||||||
 | 
							case IO_MTIMEH:
 | 
				
			||||||
 | 
								return mtime >> 32;
 | 
				
			||||||
 | 
							case IO_MTIMECMP:
 | 
				
			||||||
 | 
								return mtimecmp & 0xffffffffull;
 | 
				
			||||||
 | 
							case IO_MTIMECMPH:
 | 
				
			||||||
 | 
								return mtimecmp >> 32;
 | 
				
			||||||
 | 
							case IO_SET_SOFTIRQ:
 | 
				
			||||||
 | 
							case IO_CLR_SOFTIRQ:
 | 
				
			||||||
 | 
								return softirq;
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return {};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void step() {
 | 
				
			||||||
 | 
							mtime++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool timer_irq_pending() {
 | 
				
			||||||
 | 
							return mtime >= mtimecmp;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool soft_irq_pending() {
 | 
				
			||||||
 | 
							return softirq;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct MemMap32: MemBase32 {
 | 
					struct MemMap32: MemBase32 {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,9 +106,9 @@ int main(int argc, char **argv) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TBMemIO io;
 | 
						TBMemIO io(trace_execution);
 | 
				
			||||||
	MemMap32 mem;
 | 
						MemMap32 mem;
 | 
				
			||||||
	mem.add(0x80000000u, 12, &io);
 | 
						mem.add(0x80000000u, 0x1000, &io);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	RVCore core(mem, RAM_BASE + 0x40, RAM_BASE, ram_size);
 | 
						RVCore core(mem, RAM_BASE + 0x40, RAM_BASE, ram_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -126,8 +126,12 @@ int main(int argc, char **argv) {
 | 
				
			||||||
	int64_t cyc;
 | 
						int64_t cyc;
 | 
				
			||||||
	int rc = 0;
 | 
						int rc = 0;
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		for (cyc = 0; cyc < max_cycles; ++cyc)
 | 
							for (cyc = 0; cyc < max_cycles; ++cyc) {
 | 
				
			||||||
			core.step(trace_execution);
 | 
								core.step(trace_execution);
 | 
				
			||||||
 | 
								io.step();
 | 
				
			||||||
 | 
								core.csr.set_irq_t(io.timer_irq_pending());
 | 
				
			||||||
 | 
								core.csr.set_irq_s(io.soft_irq_pending());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if (propagate_return_code)
 | 
							if (propagate_return_code)
 | 
				
			||||||
			rc = -1;
 | 
								rc = -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -131,6 +131,10 @@ void RVCore::step(bool trace) {
 | 
				
			||||||
	std::optional<uint> exception_cause;
 | 
						std::optional<uint> exception_cause;
 | 
				
			||||||
	uint regnum_rd = 0;
 | 
						uint regnum_rd = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::optional<ux_t> trace_csr_addr;
 | 
				
			||||||
 | 
						std::optional<ux_t> trace_csr_result;
 | 
				
			||||||
 | 
						std::optional<uint> trace_priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::optional<uint16_t> fetch0 = r16(pc);
 | 
						std::optional<uint16_t> fetch0 = r16(pc);
 | 
				
			||||||
	std::optional<uint16_t> fetch1 = r16(pc + 2);
 | 
						std::optional<uint16_t> fetch1 = r16(pc + 2);
 | 
				
			||||||
	uint32_t instr = *fetch0 | ((uint32_t)*fetch1 << 16);
 | 
						uint32_t instr = *fetch0 | ((uint32_t)*fetch1 << 16);
 | 
				
			||||||
| 
						 | 
					@ -139,7 +143,10 @@ void RVCore::step(bool trace) {
 | 
				
			||||||
	uint funct3 = instr >> 12 & 0x7;
 | 
						uint funct3 = instr >> 12 & 0x7;
 | 
				
			||||||
	uint funct7 = instr >> 25 & 0x7f;
 | 
						uint funct7 = instr >> 25 & 0x7f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!fetch0 || ((*fetch0 & 0x3) == 0x3 && !fetch1)) {
 | 
						std::optional<ux_t> irq_target_pc = csr.trap_check_enter_irq(pc);
 | 
				
			||||||
 | 
						if (irq_target_pc) {
 | 
				
			||||||
 | 
							// Replace current instruction with IRQ entry
 | 
				
			||||||
 | 
						} else if (!fetch0 || ((*fetch0 & 0x3) == 0x3 && !fetch1)) {
 | 
				
			||||||
		exception_cause = XCAUSE_INSTR_FAULT;
 | 
							exception_cause = XCAUSE_INSTR_FAULT;
 | 
				
			||||||
	} else if ((instr & 0x3) == 0x3) {
 | 
						} else if ((instr & 0x3) == 0x3) {
 | 
				
			||||||
		// 32-bit instruction
 | 
							// 32-bit instruction
 | 
				
			||||||
| 
						 | 
					@ -547,18 +554,33 @@ void RVCore::step(bool trace) {
 | 
				
			||||||
			else if (funct3 >= 0b101 && funct3 <= 0b111) {
 | 
								else if (funct3 >= 0b101 && funct3 <= 0b111) {
 | 
				
			||||||
				// csrrwi, csrrsi, csrrci
 | 
									// csrrwi, csrrsi, csrrci
 | 
				
			||||||
				uint write_op = funct3 - 0b101;
 | 
									uint write_op = funct3 - 0b101;
 | 
				
			||||||
				if (write_op != RVCSR::WRITE || regnum_rd != 0)
 | 
									if (write_op != RVCSR::WRITE || regnum_rd != 0) {
 | 
				
			||||||
					rd_wdata = csr.read(csr_addr);
 | 
										rd_wdata = csr.read(csr_addr);
 | 
				
			||||||
				if (write_op == RVCSR::WRITE || regnum_rs1 != 0)
 | 
										if (!rd_wdata) {
 | 
				
			||||||
					csr.write(csr_addr, regnum_rs1, write_op);
 | 
											exception_cause = XCAUSE_INSTR_ILLEGAL;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if (trace && !exception_cause) {
 | 
				
			||||||
 | 
											trace_csr_addr = csr_addr;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if (write_op == RVCSR::WRITE || regnum_rs1 != 0) {
 | 
				
			||||||
 | 
										if (!csr.write(csr_addr, regnum_rs1, write_op)) {
 | 
				
			||||||
 | 
											exception_cause = XCAUSE_INSTR_ILLEGAL;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if (trace && !exception_cause) {
 | 
				
			||||||
 | 
											trace_csr_addr = csr_addr;
 | 
				
			||||||
 | 
											trace_csr_result = csr.read(csr_addr, false);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			} else if (RVOPC_MATCH(instr, MRET)) {
 | 
								} else if (RVOPC_MATCH(instr, MRET)) {
 | 
				
			||||||
				if (csr.getpriv() == PRV_M) {
 | 
									if (csr.get_true_priv() == PRV_M) {
 | 
				
			||||||
					pc_wdata = csr.trap_mret();
 | 
										pc_wdata = csr.trap_mret();
 | 
				
			||||||
 | 
										trace_priv = csr.get_true_priv();
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					exception_cause = XCAUSE_INSTR_ILLEGAL;
 | 
										exception_cause = XCAUSE_INSTR_ILLEGAL;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} else if (RVOPC_MATCH(instr, ECALL)) {
 | 
								} else if (RVOPC_MATCH(instr, ECALL)) {
 | 
				
			||||||
				exception_cause = XCAUSE_ECALL_U + csr.getpriv();
 | 
									exception_cause = XCAUSE_ECALL_U + csr.get_true_priv();
 | 
				
			||||||
			} else if (RVOPC_MATCH(instr, EBREAK)) {
 | 
								} else if (RVOPC_MATCH(instr, EBREAK)) {
 | 
				
			||||||
				exception_cause = XCAUSE_EBREAK;
 | 
									exception_cause = XCAUSE_EBREAK;
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
| 
						 | 
					@ -774,30 +796,49 @@ void RVCore::step(bool trace) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (trace) {
 | 
						if (trace && !irq_target_pc) {
 | 
				
			||||||
		printf("%08x: ", pc);
 | 
							printf("%08x: ", pc);
 | 
				
			||||||
		if ((instr & 0x3) == 0x3) {
 | 
							if ((instr & 0x3) == 0x3) {
 | 
				
			||||||
			printf("%08x : ", instr);
 | 
								printf("%08x : ", instr);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			printf("    %04x : ", instr & 0xffffu);
 | 
								printf("    %04x : ", instr & 0xffffu);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (regnum_rd != 0 && rd_wdata) {
 | 
							bool gpr_writeback = regnum_rd != 0 && rd_wdata;
 | 
				
			||||||
			printf("%-3s <- %08x ", friendly_reg_names[regnum_rd], *rd_wdata);
 | 
							if (gpr_writeback) {
 | 
				
			||||||
 | 
								printf("%-3s   <- %08x :\n", friendly_reg_names[regnum_rd], *rd_wdata);
 | 
				
			||||||
 | 
							} else if (pc_wdata) {
 | 
				
			||||||
 | 
								printf("pc    <- %08x <\n", *pc_wdata);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			printf("                ");
 | 
								printf("                  :\n");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (pc_wdata) {
 | 
							if (pc_wdata && gpr_writeback) {
 | 
				
			||||||
			printf(": pc <- %08x\n", *pc_wdata);
 | 
								printf("                   : pc    <- %08x <\n", *pc_wdata);
 | 
				
			||||||
		} else {
 | 
							}
 | 
				
			||||||
			printf(":\n");
 | 
							if (trace_csr_result) {
 | 
				
			||||||
 | 
								printf("                   : #%03x  <- %08x :\n", *trace_csr_addr, *trace_csr_result);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ensure pending CSR writes are applied before checking IRQ conditions
 | 
				
			||||||
 | 
						csr.step();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (exception_cause) {
 | 
						if (exception_cause) {
 | 
				
			||||||
		pc_wdata = csr.trap_enter(*exception_cause, pc);
 | 
							pc_wdata = csr.trap_enter_exception(*exception_cause, pc);
 | 
				
			||||||
		if (trace) {
 | 
							if (trace) {
 | 
				
			||||||
			printf("Trap cause %2u: pc <- %08x\n", *exception_cause, *pc_wdata);
 | 
								printf("^^^ Trap           : cause <- %-2u       :\n", *exception_cause);
 | 
				
			||||||
 | 
								printf("|||                : pc    <- %08x <\n", *pc_wdata);
 | 
				
			||||||
 | 
								trace_priv = csr.get_true_priv();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} else if (irq_target_pc) {
 | 
				
			||||||
 | 
							pc_wdata = irq_target_pc;
 | 
				
			||||||
 | 
							if (trace) {
 | 
				
			||||||
 | 
								printf("^^^ IRQ            : cause <- IRQ + %-2u :\n", csr.get_xcause() & ((1u << 31) - 1));
 | 
				
			||||||
 | 
								printf("|||                : pc    <- %08x <\n", *pc_wdata);
 | 
				
			||||||
 | 
								trace_priv = csr.get_true_priv();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (trace && trace_priv) {
 | 
				
			||||||
 | 
							printf("|||                : priv  <- %c        :\n", "US.M"[*trace_priv & 0x3]);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (pc_wdata)
 | 
						if (pc_wdata)
 | 
				
			||||||
| 
						 | 
					@ -806,5 +847,4 @@ void RVCore::step(bool trace) {
 | 
				
			||||||
		pc = pc + ((instr & 0x3) == 0x3 ? 4 : 2);
 | 
							pc = pc + ((instr & 0x3) == 0x3 ? 4 : 2);
 | 
				
			||||||
	if (rd_wdata && regnum_rd != 0)
 | 
						if (rd_wdata && regnum_rd != 0)
 | 
				
			||||||
		regs[regnum_rd] = *rd_wdata;
 | 
							regs[regnum_rd] = *rd_wdata;
 | 
				
			||||||
	csr.step();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,14 @@
 | 
				
			||||||
#define GETBITS(x, msb, lsb) (((x) & BITRANGE(msb, lsb)) >> (lsb))
 | 
					#define GETBITS(x, msb, lsb) (((x) & BITRANGE(msb, lsb)) >> (lsb))
 | 
				
			||||||
#define GETBIT(x, bit) (((x) >> (bit)) & 1u)
 | 
					#define GETBIT(x, bit) (((x) >> (bit)) & 1u)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ux_t RVCSR::get_effective_xip() {
 | 
				
			||||||
 | 
						return mip |
 | 
				
			||||||
 | 
							(irq_s ? MIP_MSIP : 0) |
 | 
				
			||||||
 | 
							(irq_t ? MIP_MTIP : 0) |
 | 
				
			||||||
 | 
							(irq_e ? MIP_MEIP : 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RVCSR::step() {
 | 
					void RVCSR::step() {
 | 
				
			||||||
	uint64_t mcycle_64 = ((uint64_t)mcycleh << 32) | mcycle;
 | 
						uint64_t mcycle_64 = ((uint64_t)mcycleh << 32) | mcycle;
 | 
				
			||||||
	uint64_t minstret_64 = ((uint64_t)minstreth << 32) | minstret;
 | 
						uint64_t minstret_64 = ((uint64_t)minstreth << 32) | minstret;
 | 
				
			||||||
| 
						 | 
					@ -66,7 +74,7 @@ std::optional<ux_t> RVCSR::read(uint16_t addr, bool side_effect) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case CSR_MSTATUS:       return mstatus;
 | 
							case CSR_MSTATUS:       return mstatus;
 | 
				
			||||||
		case CSR_MIE:           return mie;
 | 
							case CSR_MIE:           return mie;
 | 
				
			||||||
		case CSR_MIP:           return mip;
 | 
							case CSR_MIP:           return get_effective_xip();
 | 
				
			||||||
		case CSR_MTVEC:         return mtvec;
 | 
							case CSR_MTVEC:         return mtvec;
 | 
				
			||||||
		case CSR_MSCRATCH:      return mscratch;
 | 
							case CSR_MSCRATCH:      return mscratch;
 | 
				
			||||||
		case CSR_MEPC:          return mepc;
 | 
							case CSR_MEPC:          return mepc;
 | 
				
			||||||
| 
						 | 
					@ -126,6 +134,23 @@ bool RVCSR::write(uint16_t addr, ux_t data, uint op) {
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ux_t RVCSR::trap_enter_exception(uint xcause, ux_t xepc) {
 | 
				
			||||||
 | 
						assert(xcause < 32);
 | 
				
			||||||
 | 
						assert(!pending_write_addr);
 | 
				
			||||||
 | 
						return trap_enter(xcause, xepc);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::optional<ux_t> RVCSR::trap_check_enter_irq(ux_t xepc) {
 | 
				
			||||||
 | 
						ux_t m_targeted_irqs = get_effective_xip() & mie;
 | 
				
			||||||
 | 
						bool take_m_irq = m_targeted_irqs && ((mstatus & MSTATUS_MIE) || priv < PRV_M);
 | 
				
			||||||
 | 
						if (take_m_irq) {
 | 
				
			||||||
 | 
							ux_t cause = (1u << 31) | __builtin_ctz(m_targeted_irqs);
 | 
				
			||||||
 | 
							return trap_enter(cause, xepc);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return std::nullopt;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Update trap state (including change of privilege level), return trap target PC
 | 
					// Update trap state (including change of privilege level), return trap target PC
 | 
				
			||||||
ux_t RVCSR::trap_enter(uint xcause, ux_t xepc) {
 | 
					ux_t RVCSR::trap_enter(uint xcause, ux_t xepc) {
 | 
				
			||||||
	mstatus = (mstatus & ~MSTATUS_MPP) | (priv << 11);
 | 
						mstatus = (mstatus & ~MSTATUS_MPP) | (priv << 11);
 | 
				
			||||||
| 
						 | 
					@ -147,6 +172,9 @@ ux_t RVCSR::trap_enter(uint xcause, ux_t xepc) {
 | 
				
			||||||
// Update trap state, return mepc:
 | 
					// Update trap state, return mepc:
 | 
				
			||||||
ux_t RVCSR::trap_mret() {
 | 
					ux_t RVCSR::trap_mret() {
 | 
				
			||||||
	priv = GETBITS(mstatus, 12, 11);
 | 
						priv = GETBITS(mstatus, 12, 11);
 | 
				
			||||||
 | 
						mstatus &= ~MSTATUS_MPP;
 | 
				
			||||||
 | 
						if (priv != PRV_M)
 | 
				
			||||||
 | 
							mstatus &= ~MSTATUS_MPRV;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (mstatus & MSTATUS_MPIE)
 | 
						if (mstatus & MSTATUS_MPIE)
 | 
				
			||||||
		mstatus |= MSTATUS_MIE;
 | 
							mstatus |= MSTATUS_MIE;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue