initial commit
This commit is contained in:
commit
647998df5e
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
riscvemu
|
||||
riscvemu.debug
|
214
main.go
Normal file
214
main.go
Normal file
@ -0,0 +1,214 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ErrInvalidOpcode struct{}
|
||||
|
||||
func (e ErrInvalidOpcode) Error() string { return "invalid opcode" }
|
||||
|
||||
type CPUState struct {
|
||||
Registers [32]uint32
|
||||
Pc uint32
|
||||
w World
|
||||
}
|
||||
|
||||
func opcode_rd(opcode uint32) uint32 { return (opcode >> 7) & 0b11111 }
|
||||
func opcode_rs1(opcode uint32) uint32 { return (opcode >> 15) & 0b11111 } // 2^5 is 32 for 32 registers
|
||||
func opcode_rs2(opcode uint32) uint32 { return (opcode >> 19) & 0b11111 }
|
||||
|
||||
func (c *CPUState) ReadRegister(r uint32) uint32 {
|
||||
if r == 0 {
|
||||
return 0 // Read from the zero register always returns 0
|
||||
}
|
||||
|
||||
return c.Registers[r]
|
||||
}
|
||||
|
||||
func (c *CPUState) Step() error {
|
||||
opcode, err := c.w.ReadMemory(c.Pc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ReadMemory: %w", err)
|
||||
}
|
||||
|
||||
switch opcode & 0b1111111 {
|
||||
case 0b0110111:
|
||||
// LUI
|
||||
panic("todo")
|
||||
|
||||
case 0b0010111:
|
||||
// AUIPC
|
||||
panic("todo")
|
||||
|
||||
case 0b01101111:
|
||||
// JAL
|
||||
panic("todo")
|
||||
|
||||
case 0b1100111:
|
||||
// JALR
|
||||
panic("todo")
|
||||
|
||||
case 0b1100011:
|
||||
// BEQ/BNE/BLT/BGE/BLTU/BGEU
|
||||
panic("todo")
|
||||
|
||||
case 0b0000011:
|
||||
// LB/LH/LW/LBU/LHU
|
||||
panic("todo")
|
||||
|
||||
case 0b0100011:
|
||||
// SB/SH/SW
|
||||
panic("todo")
|
||||
|
||||
case 0b0010011:
|
||||
// ADDI/SLTI/SLTIU/XORI/ORI/ANDI/SLLI/SLRI/SRAI
|
||||
funct3 := (opcode >> 12) & 0b111
|
||||
|
||||
var imm uint32 = (opcode >> 20) & 0b111111111111
|
||||
if imm&0b100000000000 != 0 {
|
||||
imm |= 0b11111111111111111111000000000000 // sign extension: if MSB is set in imm, extend with 1's instead of 0's
|
||||
}
|
||||
|
||||
switch funct3 {
|
||||
case 0b000:
|
||||
// ADDI
|
||||
// ADDI adds the sign-extended 12-bit immediate to register rs1. Arithmetic overflow is ignored and
|
||||
// the result is simply the low XLEN bits of the result. ADDI rd, rs1, 0 is used to implement the MV
|
||||
// rd, rs1 assembler pseudoinstruction.
|
||||
c.Registers[opcode_rd(opcode)] = c.ReadRegister(opcode_rs1(opcode)) + imm
|
||||
|
||||
case 0b010:
|
||||
// SLTI
|
||||
// SLTI (set less than immediate) places the value 1 in register rd if register rs1 is less than the
|
||||
// signextended immediate when both are treated as signed numbers, else 0 is written to rd. SLTIU is
|
||||
// similar but compares the values as unsigned numbers (i.e., the immediate is first sign-extended to
|
||||
// XLEN bits then treated as an unsigned number). Note, SLTIU rd, rs1, 1 sets rd to 1 if rs1 equals
|
||||
// zero, otherwise sets rd to 0 (assembler pseudoinstruction SEQZ rd, rs).
|
||||
if int32(c.ReadRegister(opcode_rs1(opcode))) < int32(imm) {
|
||||
c.Registers[opcode_rd(opcode)] = 1
|
||||
} else {
|
||||
c.Registers[opcode_rd(opcode)] = 0
|
||||
}
|
||||
|
||||
case 0b011:
|
||||
// SLTIU
|
||||
if c.ReadRegister(opcode_rs1(opcode)) < imm {
|
||||
c.Registers[opcode_rd(opcode)] = 1
|
||||
} else {
|
||||
c.Registers[opcode_rd(opcode)] = 0
|
||||
}
|
||||
|
||||
case 0b100:
|
||||
// XORI
|
||||
// ANDI, ORI, XORI are logical operations that perform bitwise AND, OR, and XOR on register rs1
|
||||
// and the sign-extended 12-bit immediate and place the result in rd. Note, XORI rd, rs1, -1 performs
|
||||
// a bitwise logical inversion of register rs1 (assembler pseudoinstruction NOT rd, rs).
|
||||
c.Registers[opcode_rd(opcode)] = c.ReadRegister(opcode_rs1(opcode)) ^ imm
|
||||
|
||||
case 0b110:
|
||||
// ORI
|
||||
c.Registers[opcode_rd(opcode)] = c.ReadRegister(opcode_rs1(opcode)) | imm
|
||||
|
||||
case 0b111:
|
||||
// ANDI
|
||||
c.Registers[opcode_rd(opcode)] = c.ReadRegister(opcode_rs1(opcode)) & imm
|
||||
|
||||
case 0b001:
|
||||
// SLLI
|
||||
// Shifts by a constant are encoded as a specialization of the I-type format. The operand to be shifted
|
||||
// is in rs1, and the shift amount is encoded in the lower 5 bits of the I-immediate field. The right
|
||||
// shift type is encoded in bit 30. SLLI is a logical left shift (zeros are shifted into the lower bits);
|
||||
// SRLI is a logical right shift (zeros are shifted into the upper bits); and SRAI is an arithmetic right
|
||||
// shift (the original sign bit is copied into the vacated upper bits).
|
||||
|
||||
shamt := (opcode >> 20) & 0b1111
|
||||
c.Registers[opcode_rd(opcode)] = c.ReadRegister(opcode_rs1(opcode)) << shamt
|
||||
|
||||
case 0b101:
|
||||
// SRLI/SRAI share the same `funct3`
|
||||
shamt := (opcode >> 20) & 0b1111
|
||||
distinguish := (opcode >> 25) & 0b1111111
|
||||
if distinguish == 0b0000000 {
|
||||
// SRLI
|
||||
c.Registers[opcode_rd(opcode)] = c.ReadRegister(opcode_rs1(opcode)) >> shamt
|
||||
|
||||
} else if distinguish == 0b0100000 {
|
||||
// SRAI
|
||||
// Go's shift operator implements arithmetic shifts if the left operand is a signed integer and logical shifts if it is an unsigned integer.
|
||||
c.Registers[opcode_rd(opcode)] = uint32(int32(c.ReadRegister(opcode_rs1(opcode))) >> shamt)
|
||||
|
||||
} else {
|
||||
return ErrInvalidOpcode{}
|
||||
}
|
||||
|
||||
default:
|
||||
return ErrInvalidOpcode{}
|
||||
}
|
||||
|
||||
case 0b0110011:
|
||||
// ADD/SUB/SLL/SLT/SLTU/XOR/SRL/SRA/OR/AND
|
||||
panic("todo")
|
||||
|
||||
case 0b0001111:
|
||||
// FENCE/FENCE.TSO/PAUSE
|
||||
panic("todo")
|
||||
|
||||
case 0b1110011:
|
||||
// ECALL/EBREAK
|
||||
panic("todo")
|
||||
|
||||
default:
|
||||
return ErrInvalidOpcode{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type World interface {
|
||||
ReadMemory(addr uint32) (uint32, error)
|
||||
WriteMemory(addr, value uint32) error
|
||||
Syscall()
|
||||
Trap() (bool, error)
|
||||
}
|
||||
|
||||
type SampleWorld struct {
|
||||
ram [1024]uint32
|
||||
}
|
||||
|
||||
func (sw *SampleWorld) ReadMemory(addr uint32) (uint32, error) {
|
||||
if addr >= uint32(len(sw.ram)) {
|
||||
panic("out of bounds")
|
||||
}
|
||||
|
||||
return sw.ram[addr], nil
|
||||
}
|
||||
|
||||
func (sw *SampleWorld) WriteMemory(addr, value uint32) error {
|
||||
if addr >= uint32(len(sw.ram)) {
|
||||
panic("out of bounds")
|
||||
}
|
||||
|
||||
sw.ram[addr] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sw *SampleWorld) Syscall() {
|
||||
panic("syscall")
|
||||
}
|
||||
|
||||
func (sw *SampleWorld) Trap() (bool, error) {
|
||||
panic("trap")
|
||||
}
|
||||
|
||||
func main() {
|
||||
sw := SampleWorld{}
|
||||
sw.ram[0] = 0b00000000000100000000000001110011 // EBREAK
|
||||
|
||||
c := CPUState{
|
||||
w: &sw,
|
||||
}
|
||||
|
||||
err := c.Step()
|
||||
panic(err)
|
||||
}
|
Loading…
Reference in New Issue
Block a user