Epiphany Architecture Quirks
A collection of obscure facts and gotchas about the discontinued RISC architecture.
This is a collection of quirks I ran into many moons ago while developing a simulator for the Epiphany processor architecture. The Epiphany architecture is basically dead at this point, but I'm leaving this up for posterity.
Most of this comes from the helpful folks at Adapteva and the Parallella forums, while other things are from manual trial and error, or scavenging through binutils and CGEN.
Undefined Behavior
When undefined behavior occurs on an Epiphany chip, anything at all can happen. That means the chip could simply hang, it could branch to some completely arbitrary location, random registers could be overwritten, etc. If the chip enters a state of undefined behavior, all bets are off. You should assume everything is broken and write to the RESETCORE
register.
Undocumented Features
The architecture has a number of undocumented and/or unsupported features that haven't been finished or tested fully yet. If you're a regular user of the architecture, you can ignore these. They're mostly relevant for simulators and other such tools.
SWI
Instruction
The architecture has an undocumented SWI
instruction which raises a software exception. It sets bit 1 of ILAT
and sets the EXCAUSE
bits in STATUS
to 0b0001
(for Epiphany III) or 0b1110
(for Epiphany IV).
The instruction is encoded as 0x01E2
which does leave room for an operand of some kind. Interestingly, e-as
allows an operand that is exactly 0
, and nothing else, and encodes it as if no operand was given.
UNIMPL
Instruction
There is an undocumented instruction called UNIMPL
which raises a so-called unimplemented exception. The instruction has no operands or configuration bits and is simply encoded as 0x000F000F
. It sets bit 1 of ILAT
and sets the EXCAUSE
bits of STATUS
to 0b0100
(for Epiphany III) or 0b1111
(for Epiphany IV).
Privilege Levels
Bit 2 of the STATUS
register is documented as reserved, but actually means either user or superuser mode, where it being cleared means user mode. This bit only has significance if (the documented as reserved) bit 25 of the CONFIG
register is set, which tells the core to use privilege levels.
When privilege levels are in effect, an interrupt sets bit 2 of STATUS
, and an RTI
instruction clears it. Among other things, when privilege levels are in effect, user mode is not allowed to issue GIE
, GID
, and RTI
, and also not allowed to access system registers.
Instruction Encoding
This section documents some peculiarities and undocumented aspects of encoding instructions.
General-Purpose Registers
General-purpose registers are encoded in the obvious way: r0
is 0b0000
, r1
is 0b0001
, r2
is 0b0010
, and so on. The 4 bits leave exactly enough room to encode all 64 registers.
System Registers
It's not documented how exactly system registers should be encoded for the MOVFS
and MOVTS
instructions. In Appendix B of the manual, each register has an address. The system registers start at 0xF0400
(CONFIG
sits here). To get the register number used when encoding an instruction, take the address of the register, subtract 0xF0400
(the system register base), then divide by 4
, and subtract 1
.
So:
int addr = ...;
int reg = (addr - 0xF0400) / 4 - 1;
Doing this, CONFIG
becomes 0b0000
, STATUS
becomes 0b0001
, PC
becomes 0b0010
, etc.
Note that some gaps exist in the system register region. Instructions reading from or writing to these will trigger undefined behavior.
SYNC
, WAND
, and MBKPT
The encoding for these instructions is not present in the decode table. They are all 16-bit instructions with no operands or configuration bits and are encoded as 0x01F2
, 0x0182
, and 0x03C2
respectively.
MOV
with BL
The conditional variant of MOV
has enough bits for encoding the BL
(branch and link) condition code. This makes no sense for this instruction, however, and triggers undefined behavior. Note that e-as
will actually reject it.
LDR/STR (DISP) (16)
This instruction is listed twice in the decode table. The second listing is actually the 32-bit variant.
FLOAT
, FIX
, and FABS
These instructions are listed as having Rm
operands. They of course don't, since they operate on a single value and store that in a destination register. Simply ignore the bits that claim to be Rm
parts.
Bad Instructions
If an instruction does not correctly decode to anything the processor can recognize, undefined behavior results. The manual does claim that a software exception can be raised for invalid instructions, but this is not actually the case.
Memory Access
The Epiphany is rather fragile when it comes to memory - there are many ways to throw the core into an unpredictable state. This section tries to document all of these cases.
Invalid Memory Access
When an Epiphany core attempts to access some random, unmapped memory that lies outside of any core's local memory banks and the external memory segment, undefined behavior occurs. In practice, the core will most likely hang, but in any case, it will not behave as intended.
Note that this goes for LDR
/STR
, DMA, and everything else that might access arbitrary memory.
Registers and Active Cores
The manual is not completely explicit about it, but reading from or writing to system registers is legal while the core is active. This is not true of general-purpose registers, and for those, undefined behavior will result. This holds true for reads/writes coming from both the host and other cores.
Memory Faults
The manual says that a memory fault interrupt can be raised on a "memory protection fault". This interrupt is actually only raised when local memory banks protected with MEMPROTECT
are accessed - not arbitrary, unmapped memory.
TESTSET
Semantics
The TESTSET
instruction is only atomic with respect to other Epiphany cores in the system. If the host and an Epiphany core both write to the same location (with the host using its equivalent to TESTSET
), it will not happen atomically.
Further, TESTSET
on a memory location that lies outside an Epiphany core's local memory banks results in undefined behavior, even if it's in the external memory segment.
Interrupt Handling
This section describes some missing details about interrupt handling in the architecture manual.
Interrupt Actions
The diagram in section 7.8.1 of the manual doesn't give the whole story as to what an Epiphany core does when an interrupt enters the core.
Let N
be the interrupt level.
The
PC
is saved inIRET
.Bit
N
inILAT
is cleared.Bit
N
inIPEND
is set.The
GID
bit inSTATUS
is set.If bit 25 of
CONFIG
is set, bit 2 inSTATUS
is set.PC
is set toN * 4
(an index into the IVT).
RTI
Actions
Similarly, the manual doesn't give the whole story on RTI
.
Let N
be the interrupt level.
Bit
N
ofIPEND
is cleared.The
GID
bit inSTATUS
is cleared.Bit 2 in
STATUS
is cleared (unconditionally).PC
is set toIRET
.
Intuitively, issuing RTI
outside of an ISR would make things blow up as there isn't a current interrupt level. In this case, IPEND
is all zero, so it isn't changed. The instruction will still not do anything terribly useful (though its behavior is well-defined).
Traps and System Calls
The TRAP
instruction is included in the architecture so that Epiphany cores can call up into the host system, where something akin to a kernel can service system calls and the like.
Trap Codes
Some trap codes don't have clear meanings.
Trap codes 0
, 1
, 2
, and 6
were the old write
, read
, open
, and close
system calls, respectively. They are no longer used as such (except for a bug relating to close
; see below). The manual calls these reserved, but programs are free to use them for their own purposes. That being said, the official simulator will still interpret them as the aforementioned calls.
The two pass and fail trap codes (4
and 5
) are primarily intended for testing. The program can issue them when an assertion passes or fails. Note that the program will immediately stop upon issuing one of these (the manual is not clear about this).
System Calls
The manual doesn't document system call 19
(gettimeofday
) and system call 21
(link
).
System call 3
(close
) is documented but is not actually used by the Epiphany port of Newlib. This is a bug in the port.
ABI Rules
The manual isn't too clear on the exact ABI surrounding TRAP
.
For the deprecated trap codes 0
and 1
, r0
was the file descriptor, r1
was the buffer address, and r2
was the length. The deprecated trap code 2
used r0
as file name pointer and r1
as open mode. The deprecated trap code 6
used r0
as file descriptor. Trap code 3
uses r0
as the status indicator, i.e. 0
for success, 1
for error, etc.
Trap codes 0
, 1
, 2
, and 6
, as well as all system calls use r3
as the errno
register, and r0
as the result register. All other trap codes do not set any result register(s).