Discover more from Stream of Consciousness
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.
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
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.
The architecture has an undocumented
SWI instruction which raises a software exception. It sets bit 1 of
ILAT and sets the
EXCAUSE bits in
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.
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
0b0100 (for Epiphany III) or
0b1111 (for Epiphany IV).
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
RTI, and also not allowed to access system registers.
This section documents some peculiarities and undocumented aspects of encoding instructions.
General-purpose registers are encoded in the obvious way:
0b0010, and so on. The 4 bits leave exactly enough room to encode all 64 registers.
It's not documented how exactly system registers should be encoded for the
MOVTS instructions. In Appendix B of the manual, each register has an address. The system registers start at
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
int addr = ...; int reg = (addr - 0xF0400) / 4 - 1;
Note that some gaps exist in the system register region. Instructions reading from or writing to these will trigger undefined behavior.
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
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.
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
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.
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
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.
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 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.
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.
This section describes some missing details about interrupt handling in the architecture manual.
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.
N be the interrupt level.
PCis saved in
If bit 25 of
CONFIGis set, bit 2 in
PCis set to
N * 4(an index into the IVT).
Similarly, the manual doesn't give the whole story on
N be the interrupt level.
Bit 2 in
STATUSis cleared (unconditionally).
PCis set to
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
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.
Some trap codes don't have clear meanings.
6 were the old
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 (
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).
The manual doesn't document system call
gettimeofday) and system call
close) is documented but is not actually used by the Epiphany port of Newlib. This is a bug in the port.
The manual isn't too clear on the exact ABI surrounding
For the deprecated trap codes
r0 was the file descriptor,
r1 was the buffer address, and
r2 was the length. The deprecated trap code
r0 as file name pointer and
r1 as open mode. The deprecated trap code
r0 as file descriptor. Trap code
r0 as the status indicator, i.e.
0 for success,
1 for error, etc.
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).