126 lines
3.0 KiB
Go
126 lines
3.0 KiB
Go
package riscvemu
|
|
|
|
import (
|
|
"math"
|
|
"os"
|
|
)
|
|
|
|
type VirtualEEI struct {
|
|
memPages map[uint32][4096]byte
|
|
}
|
|
|
|
func NewVirtualEEI() VirtualEEI {
|
|
return VirtualEEI{
|
|
memPages: make(map[uint32][4096]byte),
|
|
}
|
|
}
|
|
|
|
func (ve *VirtualEEI) ReadByte(addr uint32) (byte, error) {
|
|
page, _ := ve.memPages[addr>>12]
|
|
return page[addr&0b111111111111], nil
|
|
}
|
|
|
|
func (ve *VirtualEEI) Read16(addr uint32) (uint16, error) {
|
|
// n.b. will panic on overflow
|
|
|
|
page, _ := ve.memPages[addr>>12]
|
|
inPageAddr := addr & 0b111111111111
|
|
|
|
return (uint16(page[inPageAddr]) << 8) | uint16(page[inPageAddr+1]), nil
|
|
}
|
|
|
|
func (ve *VirtualEEI) Read32(addr uint32) (uint32, error) {
|
|
// n.b. will panic on overflow
|
|
// RISC-V allows 32-bit load/stores to be implemented as either little-endian
|
|
// or big-endian. This is the little-endian version
|
|
|
|
page, _ := ve.memPages[addr>>12]
|
|
inPageAddr := addr & 0b111111111111
|
|
|
|
return (uint32(page[inPageAddr]) << 24) | (uint32(page[inPageAddr+1]) << 16) | (uint32(page[inPageAddr+2]) << 8) | uint32(page[inPageAddr+3]), nil
|
|
}
|
|
|
|
func (ve *VirtualEEI) WriteByte(addr uint32, value byte) error {
|
|
page, ok := ve.memPages[addr>>12]
|
|
|
|
// Slices are reference-types, this will write-through into ve.memPages
|
|
page[addr&0b111111111111] = value
|
|
|
|
if !ok {
|
|
ve.memPages[addr>>12] = page
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ve *VirtualEEI) Write16(addr uint32, value uint16) error {
|
|
// n.b. will panic on split page boundary
|
|
|
|
page, ok := ve.memPages[addr>>12]
|
|
inPageAddr := addr & 0b111111111111
|
|
|
|
// Slices are reference-types, this will write-through into ve.memPages
|
|
page[inPageAddr] = byte((value >> 8) & 0b11111111)
|
|
page[inPageAddr+1] = byte(value & 0b11111111)
|
|
|
|
if !ok {
|
|
ve.memPages[addr>>12] = page
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ve *VirtualEEI) Write32(addr, value uint32) error {
|
|
// n.b. will panic on split page boundary
|
|
|
|
page, ok := ve.memPages[addr>>12]
|
|
inPageAddr := addr & 0b111111111111
|
|
|
|
// Slices are reference-types, this will write-through into ve.memPages
|
|
page[inPageAddr] = byte((value >> 24) & 0b11111111)
|
|
page[inPageAddr+1] = byte((value >> 16) & 0b11111111)
|
|
page[inPageAddr+2] = byte((value >> 8) & 0b11111111)
|
|
page[inPageAddr+3] = byte(value & 0b11111111)
|
|
|
|
if !ok {
|
|
ve.memPages[addr>>12] = page
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// WriteAt implements the io.WriterAt interface on top of the EEI's virtual memory.
|
|
func (ve *VirtualEEI) WriteAt(p []byte, off int64) (n int, err error) {
|
|
if off < 0 || off >= math.MaxUint32 {
|
|
return 0, os.ErrInvalid
|
|
}
|
|
|
|
addr := uint32(off) // bounds-checked
|
|
|
|
// TODO do something more optimized
|
|
for i, bb := range p {
|
|
err := ve.WriteByte(addr+uint32(i), bb)
|
|
if err != nil {
|
|
return i, err
|
|
}
|
|
}
|
|
|
|
return len(p), nil
|
|
}
|
|
|
|
func (ve *VirtualEEI) Syscall() {
|
|
panic("syscall")
|
|
}
|
|
|
|
func (ve *VirtualEEI) Trap() (bool, error) {
|
|
panic("trap")
|
|
}
|
|
|
|
func (ve *VirtualEEI) MemFence(opcode uint32) error {
|
|
// In the virtual EEI, a memory fence is a no-op.
|
|
// It matters for synchronizing multiple cores or for peripherals.
|
|
return nil
|
|
}
|
|
|
|
var _ EEI = &VirtualEEI{} // interface assertion
|