A big part of the programming model is the memory model : the organisation of data in memory, their roles and restrictions...
POSEVEN follows the CDI (Control/Data/Instructions) model and has three memory spaces that are independent from each other : a pointer for one area has no meaning in another area. All areas can be paged out and cached.
1) Stack
The first and most simple memory space is the control stack. It is not accessed directly or indirectly by the program, hardware manages the addressing and the pointers have tags automatically added to them to prevent any confusion. Each level of stack contains two words : a data word and a pointer+tags word. Each level counts as "one" increment of the stack pointer, there is also a couple of "limit" indices to help catch abnormal conditions. This separate and protected stack is essential for the implementation of fast and efficient calls to other modules. There is no special constraint to the size of the index or the stack area, but it is usually limited by the word size (32 or 64 bits).
The stack space is specific to the thread. Nothing else (except thread #0 and its surrogate) can access it, and the only reasons to access it outside of the running thread is for setup/diagnosis/debugging.
2) Program
The second area is the program space. All instructions have the same size so incrementing the instruction pointer (PC) directly points to the next instruction. There is no data and the program can not read "data" from this space. The address size for a module is limited to 24 bits, probably even 22 bits (amounting to 16MB). The module can address itself with direct addresses embedded in the opcodes. Indirect or computed addressing is strictly limited.
Each program space is specific to a module. A IPC instruction switches the thread to a different module with its new program space. Pointers/instruction addresses for one module are irrelevant to a different module.
The program space is mostly homogeneous. The exception is the first 64K instructions which, at certain aligned addresses, can contain IPE instructions to allow jumping from a different module.
3) Data
Data belong to its own space. It is not homogeneous in its function, but the structure is always the same: a linear space made of 8-bit bytes, with a pointer that is as wide as words (32 or 64 bits).
The MSB of the address/pointer directs to one of four main areas: The top MSB is the private(0)/Public(1) flag. Other lower MSB divide this space even more, and the address generators can trap when a pointer calculation overflows or underflows, creating a pointer to a different sub-area.
00 : Data Stack, where parameters and recursive short-term blocs are stored.
01 : Local/Thread-private data (private heap, local/private data, variables)
10 : Module shared data, constants, holding the module's state, semaphores, configuration...
11 : Interchange area, used to allocate buffers that can be sent to other modules (message passing, "shuttles")
A pointer or address has a different meaning and shareability depending on the area/MSB:
- a private pointer (MSB=0) can't be shared at all and will have a totally different meaning in another thread.
- a module-shared area pointer (10) can be shared among threads running said module, but it's irrelevant in any other module.
- The interchange area (11) is the only type of pointer that can have meaning across all the threads and modules, BUT a given page can only be owned by one thread, who can then "yield" it to another recipient of its choice to transfer data.
....
Overall, it's not simple but it's not overly complicated either. It does not require a complex support hardware/circuit/mechanism and it is reasonably efficient for 32-bit and 64-bit machines. Unlike a monokernel, it starts to make sense when the system implements tens or hundreds of threads and modules simultaneously, even in an embedded system.
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.