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, ok := ve.memPages[addr>>12] if !ok { return 0, nil } return page[addr&0b111111111111], nil } func (ve *VirtualEEI) Read16(addr uint32) (uint16, error) { // n.b. will panic on overflow page, ok := ve.memPages[addr>>12] if !ok { return 0, nil } inPageAddr := addr & 0b111111111111 return (uint16(page[inPageAddr+1]) << 8) | uint16(page[inPageAddr]), 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, ok := ve.memPages[addr>>12] if !ok { return 0, nil } inPageAddr := addr & 0b111111111111 return (uint32(page[inPageAddr+3]) << 24) | (uint32(page[inPageAddr+2]) << 16) | (uint32(page[inPageAddr+1]) << 8) | uint32(page[inPageAddr]), nil } func (ve *VirtualEEI) WriteByte(addr uint32, value byte) error { page, ok := ve.memPages[addr>>12] if !ok { page = new([4096]byte) ve.memPages[addr>>12] = page } // The pointer-to-page-array is a reference type, this will write-through into ve.memPages page[addr&0b111111111111] = value return nil } func (ve *VirtualEEI) Write16(addr uint32, value uint16) error { page, ok := ve.memPages[addr>>12] if !ok { page = new([4096]byte) ve.memPages[addr>>12] = page } inPageAddr := addr & 0b111111111111 // The pointer-to-page-array is a reference type, this will write-through into ve.memPages // n.b. will panic on split page boundary page[inPageAddr+1] = byte((value >> 8) & 0b11111111) page[inPageAddr] = byte(value & 0b11111111) return nil } func (ve *VirtualEEI) Write32(addr, value uint32) error { page, ok := ve.memPages[addr>>12] if !ok { page = new([4096]byte) ve.memPages[addr>>12] = page } inPageAddr := addr & 0b111111111111 // The pointer-to-page-array is a reference type, this will write-through into ve.memPages // n.b. will panic on split page boundary page[inPageAddr+3] = byte((value >> 24) & 0b11111111) page[inPageAddr+2] = byte((value >> 16) & 0b11111111) page[inPageAddr+1] = byte((value >> 8) & 0b11111111) page[inPageAddr] = byte(value & 0b11111111) 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() error { panic("syscall") } 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