
On Habré, there have already been many articles that described the use of FreeRTOS or porting to widespread processor architectures. In this article I want to share the experience of porting FreeRTOS to the Russian architecture of "Multiklet" and show how the Multiclet P1 processor handles several parallel tasks. As an example, a GPS tracker will be created with the ability to record coordinates on SD, a shell via UART, a small text editor and the ability to work with the FAT32 file system.
Step 1. Porting.
As an example, the FreeRTOS port for ATMega323 was used, since I was familiar with the architecture of this processor for a long time. After studying the files, it became clear that this task could not be solved in the forehead, since for FreeRTOS it is recommended to use the GCC compiler set (for a specific architecture) with its __attribute__ and asm inserts, and this possibility is not available in the current LCC compiler from Multiclet. It did not become a big problem, the only attribute that was used in the port for ATMega323 is __attribute__ ((naked)), which tells the compiler not to create a prologue and an epilog of a function. This attribute is assigned to a function that is responsible for changing the context of the task and leaving for the execution of a new one; therefore, it is important that when it is called, a prologue and an epilog are not created (otherwise the stack / memory will end quickly). This function and all the functions with asm inserts had to be written in the assembly Multiclet.
The next problem I encountered while porting the OS is the stack. The fact is that there are no push and pop commands in Multiclet processors due to the nature of the architecture. The architecture of the port turned out, perhaps a bit tricky, but at the moment I do not see more
elegant solution. The idea is as follows: each task in the OS has its own memory area, in which the values of its registers and call stack are located, and there is a common OS stack, in which function calls are placed during interrupt processing. The substitution of the values of the #SP and #BP registers (pointers to the base and frame of the function) is performed in the primary interrupt handler (file crt0.s).
An example of a task change at the time of interruption.
There are 2 tasks with the same priority. After the tasks have been created and added by the kernel to the queue, the xPortStartScheduler (void) function will be called, which will start the system timer and call the primary context change function PRS (). In the PRS () function, the pxCurrentTCB pointer (pxCurrentTCB is the OS pointer to the task context) sets the value of all registers, including #SP and #BP (the global values of #SP and #BP are stored in the gSP and gBP variables, respectively) responsible for the stack the current task and the transition to its implementation.
')
PRS: jmp PRS_fin PXCTCB:= rdl pxCurrentTCB pxTopOfStack:= rdl @PXCTCB setl #DI, @pxTopOfStack complete PRS_fin: reg7 := rdl #DI, pStack reg6 := rdl #DI, pStack + 4 reg5 := rdl #DI, pStack + 8 reg4 := rdl #DI, pStack + 12 reg3 := rdl #DI, pStack + 16 reg2 := rdl #DI, pStack + 20 reg1 := rdl #DI, pStack + 24 reg0:= rdl #DI, pStack + 28 pvp := rdl #DI, pStack + 32 pxCode := rdl #DI, pStack + 36 taskSP := rdl #DI, pStack + 40 taskBP := rdl #DI, pStack + 44 saveSP := getl #SP saveBP := getl #BP setl #R7,@reg7 setl #R6,@reg6 setl #R5,@reg5 setl #R4,@reg4 setl #R3,@reg3 setl #R2,@reg2 setl #R1,@reg1 setl #R0,@reg0 wrl @saveSP, gSP wrl @saveBP, gBP setl #SP, @taskSP setl #BP, @taskBP jmp @pxCode complete
At the time of the occurrence of an interrupt from the system timer, control is transferred to the primary interrupt handler.
master.isr: jmp mi.hendler saveSP := getl #SP saveBP := getl #BP loadSP := rdl gSP loadBP := rdl gBP reta := getl #IRETADDR wrl @saveSP, iSP </code> wrl @saveBP, iBP setl #SP, @loadSP setl #BP, @loadBP wrl @reta, master.isr.retaddr complete
In the primary interrupt handler (master.isr), pointers to the stack are replaced and the return address from the interrupt is stored, then the RTOS context change function is called.
mi.hendler: getl #SP getl mi.stop getl #INTNUMR getl irq.desc.tbl mull @2, sizeof.I</code> DT.item addl @1, @2 rdl @1 jmp @1 subl @8, sizeof.ptr wrl @8, @1 setl #SP, @2 complete
During the context change, the current task context saving function is called, then the OC scheduler sets the pxCurrentTCB pointer to the task that needs to be restored, and the context recovery function is called with transfer of control to the epilog of the primary interrupt handler.
mi.stop: jmp mi.PF getl #SP addl @1, sizeof.ptr setl #SP, @1 complete mi.PF: rdl master.isr.retaddr jmp @1 getq #PSW or @1, PSW.ONIRQS setq #PSW, @1 saveSP := getl #SP saveBP := getl #BP loadSP := rdl iSP loadBP := rdl iBP wrl @saveSP, gSP wrl @saveBP, gBP setl #SP, @loadSP setl #BP, @loadBP complete
This is how the task and stack changes of FreeRTOS Multiclet P1 work.
Step 2. Example of use.
As an example of real OS usage, a small GPS tracker layout was assembled on the demo board.

The board on top is Pinboard II. It was used only as a connector for an SD card.
The main task with the highest priority was the terminal emulator, implemented through the COM port.

Parallel tasks: writing to an SD card, reading an SD card, receiving data from a GPS receiver, writing and reading a coordinate track to / from file (a).


The obtained data is not reliable (since the GPS receiver was lying on the table) and demonstrate only the work with the file system. By placing the demo board on the window we get almost correct results.

The OS is working with a memory card using the FatFS library by Comrade Chan [
elm-chan.org/fsw/ff/00index_e.html ].
Conclusion
In this article, I wanted to show that Multiclet processors are capable of executing programs widely used on other architectures, and taking into account low power consumption and hardware parallelization, it is also much more efficient.