diff --git a/cpu.go b/cpu.go index 523285e..cc63174 100644 --- a/cpu.go +++ b/cpu.go @@ -464,7 +464,24 @@ func (c *CPUState) Step() error { case 0b0001111: // FENCE/FENCE.TSO/PAUSE - panic("todo") + funct3 := (opcode >> 12) & 0b111 + if funct3 != 0b000 { + return ErrInvalidOpcode{} + } + + if opcode == 0b00000001000000000000000000001111 { + // Zihintpause PAUSE extension + // PAUSE is encoded as a FENCE instruction with pred=W, succ=0, fm=0, rd=x0, and rs1=x0. + // PAUSE is encoded as a hint within the FENCE opcode because some implementations are ex- + // pected to deliberately stall the PAUSE instruction until outstanding memory transactions have + // completed. Because the successor set is null, however, PAUSE does not mandate any particular + // memory ordering - hence, it truly is a HINT. + } + + err := c.w.MemFence(opcode) + if err != nil { + return err + } case 0b1110011: // ECALL/EBREAK diff --git a/eei.go b/eei.go index c5fba85..15d280a 100644 --- a/eei.go +++ b/eei.go @@ -14,4 +14,6 @@ type EEI interface { Syscall() Trap() (bool, error) + + MemFence(opcode uint32) error } diff --git a/virtualEei.go b/virtualEei.go index 05e2633..65e87bb 100644 --- a/virtualEei.go +++ b/virtualEei.go @@ -116,4 +116,10 @@ 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