I'm working on a RISC-V CPU emulator and have all instructions implemented. The thing is that I'm not sure if the jumps are failing. This is the current implementation I have for BGEU to make an example:
void CPU::BGEU() {
uint8_t rs1 = instDecoded.registers[0];
uint8_t rs2 = instDecoded.registers[1];
uint32_t inmediate = instDecoded.inmediate;
if (static_cast<uint32_t>(registers[rs1]) >= static_cast<uint32_t>(registers[rs2]))
pc += inmediate;
}
I have another method called "clock" which executes the instruction and then add 4 to PC, but I don't know if after a jump I should add 4.
In the RISC-V architecture, control transfer instructions, which include the unconditional jumps and conditional branches, manipulate the program counter (PC) differently compared to regular instructions. While most instructions simply advance the PC by 4 bytes, control transfer instructions adjust the PC based on specific conditions or targets.
In such operations, the PC is supposed to jump to a specific target address.
Example: JAL
(Jump and Link), JALR
(Jump and Link Register)
JAL
Directly jumps to a computed address derived from the current PC added to an immediate offset (of J-type encoding). It stores the address of the next sequential instruction (PC + 4) into register rd.
target address = PC + immediate offset
JALR
Jumps to an address calculated by adding a base register value (rs1) to a 12-bit immediate offset (of I-type encoding), then setting the least significant bit of this result to 0 to align with the 2-byte boundary (for the compressed instruction set). Similar to JAL, it stores the next instruction's address in register rd.
target address = (rs1 + immediate offset) & ~1
Example: BEQ
(branch equality),BNE
(branch inequality), BLT[U]
(branch less than or equality, where U is added for unsigned), BGE[U]
(branch greater than or equality, where U is added for unsigned))
Behavior:
current PC + immediate offset
, where immediate is a signed offset (of B-type encoding) determining the jump length.PC + 4
to execute the next sequential instruction.Purpose: The clock in a CPU emulator synchronizes the sequence of operations, ensuring accurate timing and control over the instruction execution cycle.
Design Implementation: Each instruction type should handle its PC adjustments to respect their unique requirements in logic. This modular approach ensures clarity and reduces errors in emulation, especially around the handling of the PC.
Therefore, all of the instructions defined should handle the calculation of target address individually instead of being handled by the clock method universally.
Hence, to summarize the recommended corrections:
PC + 4
).