ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Четверг
11 июня
1591000 Топик полностью
klen (Сегодня, 01:00, просмотров: 13) ответил Nikolay_Po на RISC-V, прерывания, стек прерываний и mscratch применительно к ОСРВ Задал вопрос в телеграм-канале RISC-V MCU. Задам и тут:
ниче не понял, но вопросы правильные. я когда портил - сильно лоб морщил. у меня сделано 1) не все прерывания выключаются, только те что могуть влиять на планировщик, для этого есть регистр PFIC_ITHRESDR порога пиоритета 2) таски работают в user режиме чтоб не достали до SCR-регистров. 3)чтото еще перепиливал 

посмотри мои перлы - может мысль прилетит от созерцания и полегчает


блять.... этот редактор глючит. немогу вставить текскт чтото от в моем коде находит такте что заствляеьт его удалить текст при встаке...


вот архив с тремя файлами port_macro.h port_asm.s port.c мо

kernel_kgp_portable_riscv_qkv4_kgp.7z
думалось тяжко когдаэто пилил - пожтому везе куча камментов с размышлениями зачем и что я делал



port.c

/*
   FreeRTOS Kernel V11.0.1+ kgp/klibc
   куда пойдете - copy направо или на лево - это ваше личное решение?
   я иду вверх! присоеденяйтесь...
   Чернов Сергей aka Klen Santakheza
   klen_s@mail.ru
 */

/*
 * Порт построен на базе кода FreeRTOS\Source\portable\GCC\RISC-V-RV32 и кода
 * порта из MounRiver_Studio как результат изучения riscv и размышлений как бы
 * сделать все половчее чем "лишь бы как то заработало".
 * Первый из них является линшь заготовкой и не может работать, второй работает
 * из коробки но сделан с ошибками в стиле "хуяк-хуяк в продакшэн" - например у
 * "узкоглазых братьев" напрочь не предусмотрено переключение режимов процессора,
 * и потоки РАБОТАЮТ В РЕЖИМЕ machine, а не как полагается в режиме user!!!
 * это говнокод и трэш, друзья мои. Но их понть можно - "кейтайские пейсайтели из
 * MounRiver" кажется не умеют читать "кейтайские доки" на процессор - они уперлись
 * в то что mstatus традиционно в riscv доступен ТОЛЬКО в режиме machine :)
 * .... нешмагла... а мы сделали!
 *
 * в связи с тем что данный порт целевой - для ядера QingKeV4 микросхем ch32v307
 * файл freertos_risc_v_chip_specific_extensions.h исключен, работа с регистрами
 * сопроцессора внесена в port_asm.s
 * макросы portasmSAVE_ADDITIONAL_REGISTERS и portasmRESTORE_ADDITIONAL_REGISTER
 * внесены под дефайн  portasmADDITIONAL_CONTEXT_SIZE по этому порт должен
 * без проблем заработать на всех вариантах ядра QingKeV4 - V4A, V4B, V4C и V4F
 * посредством настройки portasmADDITIONAL_CONTEXT_SIZE.
 *
 *
 * отличия от потрта MounRiver_Studio
 *
 * 0. порт строился на связке с исходниками freertos kernel из main - ветки
 *    https://github.com/FreeRTOS/FreeRTOS-Kernel.git
 *    соответсвенно он является "наисвежайшим" :) версии V11.0.1+
 *    само ядро в моем случае тоже доработано (отдельная тема), в частности на предмет реентерабельности,
 *    использования ситемного таймера ядра процессора и тд....
 *    но работа порта от этого не зависит. ядро отдельно - порт отдельно!
 *
 * 1. freertos_port_machine_mode_init()
 *    данная функция добалена в порт и должна быть вызвана в reset/crt коде в машинном режиме до выхода в режим пользователя.
 *    данная мера необходима в связи с тем что необходимо протестировать регистр mtvec на валидность адреса в нем и выполнить
 *    сохранение указателя вершины стека обработчиков в регистре mscratch, эти регистры доступны только в машинном режиме.
 *
 * 2. проблема uxCriticalNesting - невозможно использовать статические объекты freertos.
 *    изначально в обоих исходных портах uxCriticalNesting инициализируется в 0xaaaaaaaa, что бы пометить сотояние до запуска планировщика.
 *    однако это имее негативный побочный эффект. практически любой api freertos (например размещение статических мьютексов)
 *    сбросит прерывания и не включит их!! это ломает логику работы системы до старта планировщика, на которую в таком
 *    случае ложатся ограничения по использованию статического размещения объектов freertos. для исключения данной проблемы
 *    выполняется сброс uxCriticalNesting в исходное соcтояние, необходимый для устранения проблем при вызове функций работы с объектами
 *    freertos до старта шедуллера. имеем результат: в freertos_port_machine_mode_init() выполняется uxCriticalNesting = 0 ;
 *
 * 3  xPortStartFirstTask
 *
 *
 * 4. xPortSetInterruptMask/vPortClearInterruptMask
 *    нехуй писать в mstatus 0x7800 и засирать состояние fpu по каждому прерыванию. решаем проблему так - выполняем csrrci -
 *    чтение регистра и ТОЛЬКО сброс!!! бита глобального прерывания при выходе взад записываем исходное сотояние,
 *    также получаем бонус! исключаем четыре иструкции загрузки регистров так как используем indirect варианты команд доступа к mstatus !
 *    имеем результат:
 *       portUBASE_TYPE xPortSetInterruptMask(void)                          { portUBASE_TYPE uvalue=0; __asm volatile("csrrci %0, mstatus, %1":"=r"(uvalue):"i"(0x8)); return uvalue; }
 *       void           vPortClearInterruptMask(portUBASE_TYPE uvalue)       {                          __asm volatile("csrw  mstatus, %0"::"r"(uvalue)); }
 *
 * 5. Сделано ленивое сохранение/востановление регистров сопроцессора.
 *    наличие флагов
 *            src::mstatus [14:13] Floating-point unit status
 *    позволяют выполнять отслеживание изменения состояния fpu за время кванта времени выполненя потока(task) выполнять сохранение/востановление
 *    только в случае необходимости, что сущенственно снизить время на переключение контектов потоков которые редко или вообще не не исполняют код,
 *    использующий fpu
 *    как результат этой модификации, планируется в ближайшее время посмотерь каков выйгрыш по времени и при
 *    каких условиях, выработать методику разработки и проектирования кода с эффективным использованием этой фичи.
 *
 * 6. порт назван как qkv4 - то есть для всех вариантов:  V4A, V4B, V4C и V4F. но по факту данный код для V4F у которого есть fpu.
 *    я решил поступить как делает японский автопром - сначала ебалайка на колесиках в ПОЛНОЙ КОМПЛЕКТАЦИИ, а уж потом что то отключать дефайнами,
 *    а не как у гейропейцев и у нас - сделать полуфабрикат, а потом прикручивать скотчем костыли. расширяющие фцнкционал...
  .  однако все таки код определения контектса и его переключения лежат под дефайном __riscv_f что все таки отрабатывает наличие ил отсутствие FPU 

/*-----------------------------------------------------------
 * реализация специфичных деклараий из portable.h для freertos + ядро QingKeV4
 *----------------------------------------------------------*/

/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "portmacro.h"

#include "string.h"



/* макрос configTASK_RETURN_ADDRESS не используем - явная реализация функции ловушки freertos_task_exit_catch
     /* Let the user override the pre-loading of the initial LR with the address of
prvTaskExitError() in case it messes up unwinding of the stack in the
debugger.
 Used to catch tasks that attempt to return from their implementing function. */

// поддержка возможности передачи указателя на задачу в freertos_task_exit_catch
//void freeretos_port_store_ret_code(void* val)   { asm volatile ("lw a0, %[ret_val]" : : [ret_val] "m" (val)); }


/* The stack used by interrupt service routines.  Set configISR_STACK_SIZE_WORDS
to use a statically allocated array as the interrupt stack.  Alternative leave
configISR_STACK_SIZE_WORDS undefined and update the linker script so that a
linker variable names __freertos_irq_stack_top has the same value as the top
of the stack used by main.  Using the linker script method will repurpose the
stack that was used by main before the scheduler was started for use as the
interrupt stack after the scheduler has started. */
#ifdef configISR_STACK_SIZE_WORDS
	static __attribute__ ((used,aligned(16))) StackType_t isr_stack[ configISR_STACK_SIZE_WORDS ] = { 0 };

	/* Don't use 0xa5 as the stack fill bytes as that is used by the kernerl for
	the task stacks, and so will legitimately appear in many positions within
	the ISR stack. */
	#define portISR_STACK_FILL_WORD	0x0123abcd
#else
	/* __freertos_irq_stack_top define by .ld file */
//	extern const uint32_t __freertos_irq_stack_top[];
//	const StackType_t xISRStackTop = ( StackType_t ) __freertos_irq_stack_top;
#endif

UBaseType_t  uxCriticalNesting  = 0xaaaaaaaa;
UBaseType_t* puxCriticalNesting = &uxCriticalNesting;

/*
 * Setup the timer to generate the tick interrupts.  The implementation in this
 * file is weak to allow application writers to change the timer used to
 * generate the tick interrupt.
 */

__attribute__(( weak )) void vPortSetupTimerInterrupt( void )  { freertos_port_sys_tick_rt_setup(); }



#if 0 // KGP
/* Set configCHECK_FOR_STACK_OVERFLOW to 3 to add ISR stack checking to task
stack checking.  A problem in the ISR stack will trigger an assert, not call the
stack overflow hook function (because the stack overflow hook is specific to a
task stack, not the ISR stack). */
#if defined( configISR_STACK_SIZE_WORDS ) && ( configCHECK_FOR_STACK_OVERFLOW > 2 )
	#warning This path not tested, or even compiled yet.

	static const uint8_t ucExpectedStackBytes[] = {
									portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE,		\
									portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE,		\
									portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE,		\
									portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE,		\
									portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE, portISR_STACK_FILL_BYTE };	\

	#define portCHECK_ISR_STACK() configASSERT( ( memcmp( ( void * ) xISRStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) == 0 ) )
#else
	/* Define the function away. */
	#define portCHECK_ISR_STACK()
#endif /* configCHECK_FOR_STACK_OVERFLOW > 2 */
#endif

/*-----------------------------------------------------------*/
// функция должна быть вызвана в machine mode
void freertos_port_machine_mode_init()
{
   auto const isr_stack_top = &( isr_stack[ configISR_STACK_SIZE_WORDS & ~portBYTE_ALIGNMENT_MASK ] );

   #if( configASSERT_DEFINED == 1 )
   {
	 uint32_t mtvec = 0;

	/* Check the least significant two bits of mtvec are 0b11 - indicating
	multiply vector mode. */
    asm volatile( "csrr %0, mtvec" : "=r"( mtvec ) );
	configASSERT( ( mtvec & 0x03UL ) == 0x3 );

	/* Check alignment of the interrupt stack - which is the same as the
	stack that was being used by main() prior to the scheduler being
	started. */
	configASSERT( ( (uint32_t)isr_stack_top & portBYTE_ALIGNMENT_MASK) == 0 );

   }
   #endif /* configASSERT_DEFINED */


   #if ( configISR_STACK_CHECK == 1)
      auto ptr = isr_stack ;
      do
    	  *(ptr++) = 0x0123abcd ;
      while (ptr <= isr_stack_top);
   #endif

   // установка указателя стека обработчиков
   asm volatile("csrw mscratch,%0"::"r"(isr_stack_top)) ;

   /*!
     изначально в uxCriticalNesting инициализируется в 0xaaaaaaaa, что бы пометить сотояние до запуска планировщика.
     однако это имеет негативный побочный эффект. практически любой api freertos (например размещение статических мьютексов)
     сбросит прерывания и не включит их!! это ломает логику работы системы до старта планировщика, на которую в таком
     случае ложатся онраничения. для исключения данной проблемы выполняется сброс uxCriticalNesting в исходное
     состояние, необходимое для исключения проблем при вызове функций работы с объектами freertos до старта шедуллера.
   */
   uxCriticalNesting = 0 ;
}

uint8_t freertos_port_fpu_status()
{
    uint32_t rd;
    /*vendor_csr::ginten::fs*/
    asm volatile ("csrr  %[rd], %[csr]" : [rd]"=r"(rd) : [csr] "i" (0x800): );
    return  ( rd >> 13 ) & 0b11 ;
}

void freertos_port_fpu_clean()
{
    asm volatile ("csrc %[csr], %[rs1]" : : [csr] "i" (0x800), [rs1]"r"(0b11 << 13) : ); // сброс битов
    asm volatile ("csrs %[csr], %[rs1]" : : [csr] "i" (0x800), [rs1]"r"(0b10 << 13) : ); // установка состояния clean
}

void freertos_port_fpu_init()
{
    asm volatile ("csrc %[csr], %[rs1]" : : [csr] "i" (0x800), [rs1]"r"(0b11 << 13) : ); // сброс битов
    asm volatile ("csrs %[csr], %[rs1]" : : [csr] "i" (0x800), [rs1]"r"(0b01 << 13) : ); // установка состояния init
}


void freertos_port_fpu_dirty()
{
    asm volatile ("csrs %[csr], %[rs1]" : : [csr] "i" (0x800), [rs1]"r"(0b11 << 13) : ); // установка состояния clean
}
/*-----------------------------------------------------------*/

BaseType_t xPortStartScheduler( void )
{
    void xPortStartFirstTask( void );

    /* инициализация системного таймера тиков */
    vPortSetupTimerInterrupt();

    // разрешение прерываний от таймера системных тиков и програмных запросов(software interrupt)
    freertos_port_sys_tick_software_interrupt_enable();

    /* инициализация счетчика вложенности критическиъ секций перед стартом первого потока(task). */
    uxCriticalNesting = 0;

    xPortStartFirstTask();

    /* после вызова  xPortStartFirstTask и выхода в режим пользователя - сюда уже никак не попасть!
     * функция никогда не завершается*/
    return pdFAIL;
}
/*-----------------------------------------------------------*/

void vPortEndScheduler( void ) { /* не требует реализации */	for( ;; ); }
/*-----------------------------------------------------------*/
__attribute__((interrupt("machine_hpe")))  void freertos_sys_tick_irq_handler()
{
    asm volatile("csrrw sp,mscratch,sp"); // переключение на стек обработчика
    portDISABLE_INTERRUPTS();
    freertos_port_sys_tick_rt_status_clear();
    if( xTaskIncrementTick() != pdFALSE )
        portYIELD();
    portENABLE_INTERRUPTS();
    asm volatile("csrrw sp,mscratch,sp"); // возврат к стеку используемому до входа в freertos_sys_tick_irq_handler
}
/*-----------------------------------------------------------*/
void vPortEnterCritical( void )
{
    portDISABLE_INTERRUPTS();
    uxCriticalNesting++;
}

/*-----------------------------------------------------------*/
void vPortExitCritical( void )
{
    configASSERT( uxCriticalNesting );
    uxCriticalNesting--;
    if( uxCriticalNesting == 0 )
           portENABLE_INTERRUPTS();
}


#define portWORD_SIZE 4
#define portCPU_CONTEXT_SIZE ( 28 * portWORD_SIZE )

#ifdef __riscv_f
   #define portFPU_CONTEXT_SIZE ( 33 * portWORD_SIZE )
#endif

/*
  
 
 0  pxCode при инициализации  mepc при переключении ???
 1  mstatus
 2  x1   ra      portTASK_RETURN_ADDRESS
 3  x5   t0
 4  x6   t1
 5  x7   t2
 6  x8   s0/fp
 7  x9   s1
 8  x10  a0       pvParametr 
 9  x11  a1
10  x12  a2 
11  x13  a3
12  x14  a4
13  x15  a5
14  x16  a6
15  x17  a7
16  x18  s2
17  x19  s3
18  x20  s4
19  x21  s5
20  x22  s6
21  x23  s7
22  x24  s8
23  x25  s9
24  x26  s10
25  x27  s11
26  x28  t3
27  x29  t4
28  x30  t5
29  x31  t6
30  xCriticalNesting
if ( mstatus & dirty )  
  [FPU registers fs0..fs32 + fcsr ] 33 33 слова
*/  




/*
 
0     <-   pxCode
1    31  fp31
     .
     .
32   0   fp0
33       fcsr
34   6    x1  ra  <- freertos_task_exit_catch
35   5    x5
36   4
    3
    2
    1    x9
40  22   x10  <-  pvParameters
    21   x11
    20   .
    19   .
    18   .
    17   .
    16   .
    15   x17
    14   .
    13   .
    12   .
    11   .
    10   .
    9    x23
    8    .
    7    .
    6    .
    5    .
    4    .
    3    .
    2    x30
    1    x31   
61  0 gintenr 
 */


struct full_stack_t 
{
  uint32_t   mepc   ; // адрес возврата после переключения планировщика 
  uint32_t   gintenr;
#ifdef __riscv_f
  uint32_t   fcsr   ;
  uint32_t   fp[32] ;
#endif

  uint32_t   ra   ; //    return_address
  uint32_t   t0   ;
  uint32_t   t1   ;
  uint32_t   t2   ;
  uint32_t   s0   ;
  uint32_t   s1   ;
  uint32_t   a0   ; //    pvParametr
  uint32_t   a1   ;
  uint32_t   a2   ;
  uint32_t   a3   ;
  uint32_t   a4   ;
  uint32_t   a5   ;
  uint32_t   a6   ;
  uint32_t   a7   ;
  uint32_t   s2   ;
  uint32_t   s3   ;
  uint32_t   s4   ;
  uint32_t   s5   ;
  uint32_t   s6   ;
  uint32_t   s7   ;
  uint32_t   s8   ;
  uint32_t   s9   ;
  uint32_t   s10  ;
  uint32_t   s11  ;
  uint32_t   t3   ;
  uint32_t   t4   ;
  uint32_t   t5   ;
  uint32_t   t6   ;


} __PACKED_ALIGN_4__;

// C-версия pxPortInitialiseStack для старой версии порта
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack, TaskFunction_t pxCode, void * pvParameters )
{
        struct full_stack_t* stack = (struct full_stack_t* )(pxTopOfStack - sizeof(struct full_stack_t)/sizeof(uint32_t));

        stack->mepc = (uint32_t)pxCode ; 
        stack->gintenr = 0x6880 ;
#ifdef __riscv_f
        stack->fcsr = 1 ;
        uint8_t fpi = 31 ;
        do { stack->fp[fpi] = 0 ; } while ( fpi-- != 0 ) ;
#endif
        stack->ra      = (uint32_t)freertos_task_exit_catch ;
        stack->a0      = (uint32_t)pvParameters ;

        return (StackType_t *)stack ;
}




__attribute__ ((aligned(8))) void xPortStartFirstTask() 
{

/* if it is an assembly entry code, the SP offset value is determined by the assembly code,
but the C code is determined by the compiler, so we subtract 512 here as a reservation.
When entering the interrupt function of C code, the compiler automatically presses the stack
into the task stack. We can only change the SP value used by the calling function after switching
the interrupt stack.This problem can be solved by modifying the interrupt to the assembly entry,
and there is no need to reserve 512 bytes. You only need to switch the interrupt stack at the
beginning of the interrupt function */

// а теперь по русски!!
/*
   1. каждая нитка имеет свой стек
   2. user mode main использует "основной" стек с указателем определённым в ld скрипте __stack_end__ с вершиной озу (при этом сохраняется совместимость с nosys кодом)
   3. machine mode exception/interrupt обработчики используют общий для них отдельный "irq-стек", оределяемый в port.c через configISR_STACK_SIZE_WORDS значением freertos_port_machine_mode_init::isr_stack_top, или "основной" стек на выбор
      выбор стека "irq-стек" его возврат выполняется макросами при входе POP_ISR_SP и выходе PUSH_ISR_SP из обработчика
      при необходимости при входе в обработчик стек может быть пеерключен с основного на isr и обратно при выходе. для хранения указателя isr стека используется регистр mscratch
      таким образом mscratch должен быть проиниализирован до запуска планировщика вызовом freertos_port_machine_mode_init
*/
    asm volatile ("lw  x4, pxCurrentTCB ;"		// чтение pxCurrentTCB      -> tp
                  "lw  sp, 0(x4)" : : : ) ;		// xCurrentTCB.pxTopOfStack -> sp
	
    register const struct full_stack_t* fs asm("sp") ;
	

#ifdef __riscv_f
	
    asm volatile ( 
	"csrw fcsr,%[fcsr]   ;"
	"flw ft0,  %[ft0]    ;"
	"flw ft1,  %[ft1]    ;"
	"flw ft2,  %[ft2]    ;"
	"flw ft3,  %[ft3]    ;"
	"flw ft4,  %[ft4]    ;"
	"flw ft5,  %[ft5]    ;"
	"flw ft6,  %[ft6]    ;"
	"flw ft7,  %[ft7]    ;"
	"flw fs0,  %[fs0]    ;"
	"flw fs1,  %[fs1]    ;"
	"flw fa0,  %[fa0]    ;"
	"flw fa1,  %[fa1]    ;"
	"flw fa2,  %[fa2]    ;"
	"flw fa3,  %[fa3]    ;"
	"flw fa4,  %[fa4]    ;"
	"flw fa5,  %[fa5]    ;"
	"flw fa6,  %[fa6]    ;"
	"flw fa7,  %[fa7]    ;"
	"flw fs5,  %[fs5]    ;"
	"flw fs6,  %[fs6]    ;"
	"flw fs7,  %[fs7]    ;"
	"flw fs8,  %[fs8]    ;"
	"flw fs9,  %[fs9]    ;"
	"flw fs10, %[fs10]   ;"
	"flw fs11, %[fs11]   ;"
	"flw ft8,  %[ft8]    ;"
	"flw ft9,  %[ft9]    ;"
	"flw ft10, %[ft10]   ;"
	"flw ft11, %[ft11]   ;"
	   : :
	       [fcsr]"r"(fs->fcsr),
	       [ft0] "m"(fs->fp[ 0]),
	       [ft1] "m"(fs->fp[ 1]),
	       [ft2] "m"(fs->fp[ 2]),
	       [ft3] "m"(fs->fp[ 3]),
	       [ft4] "m"(fs->fp[ 4]),
	       [ft5] "m"(fs->fp[ 5]),
	       [ft6] "m"(fs->fp[ 6]),
	       [ft7] "m"(fs->fp[ 7]),
	       [fs0] "m"(fs->fp[ 8]),
	       [fs1] "m"(fs->fp[ 9]),
	       [fa0] "m"(fs->fp[10]),
	       [fa1] "m"(fs->fp[11]),
	       [fa2] "m"(fs->fp[12]),
	       [fa3] "m"(fs->fp[13]),
	       [fa4] "m"(fs->fp[14]),
	       [fa5] "m"(fs->fp[15]),
	       [fa6] "m"(fs->fp[16]),
	       [fa7] "m"(fs->fp[17]),
	       [fs2] "m"(fs->fp[18]),
	       [fs3] "m"(fs->fp[19]),
	       [fs4] "m"(fs->fp[20]),
	       [fs5] "m"(fs->fp[21]),
	       [fs6] "m"(fs->fp[22]),
	       [fs7] "m"(fs->fp[23]),
	       [fs8] "m"(fs->fp[24]),
	       [fs9] "m"(fs->fp[25]),
	       [fs10]"m"(fs->fp[26]),
	       [fs11]"m"(fs->fp[27]),
	       [ft8] "m"(fs->fp[28]),
	       [ft9] "m"(fs->fp[29]),
	       [ft10]"m"(fs->fp[30]),
	       [ft11]"m"(fs->fp[31])
	     );
   
#endif
    
    asm volatile ("csrw %0, %1" : : "i"(0x800) , "r"(fs->gintenr | 0x08) : ) ; // mie
	
    asm volatile ("lw ra, %[ra]" :  : [ra] "m"(fs->ra)  : ) ;  // ra = freertos_task_exit_catch
    asm volatile ("lw t0, %[t0]" :  : [t0] "m"(fs->t0)  : ) ;	
    asm volatile ("lw t1, %[t1]" :  : [t1] "m"(fs->t1)  : ) ;
    asm volatile ("lw t2, %[t2]" :  : [t2] "m"(fs->t2)  : ) ;
    asm volatile ("lw s0, %[s0]" :  : [s0] "m"(fs->s0)  : ) ;
    asm volatile ("lw s1, %[s1]" :  : [s1] "m"(fs->s1)  : ) ;
    asm volatile ("lw a0, %[a0]" :  : [a0] "m"(fs->a0)  : ) ;  // a0 = pvParametr
    asm volatile ("lw a1, %[a1]" :  : [a1] "m"(fs->a1)  : ) ;  // a1 = pxCode       use for start jump
    asm volatile ("lw a2, %[a2]" :  : [a2] "m"(fs->a2)  : ) ;
    asm volatile ("lw a3, %[a3]" :  : [a3] "m"(fs->a3)  : ) ;
    asm volatile ("lw a4, %[a4]" :  : [a4] "m"(fs->a4)  : ) ;
    asm volatile ("lw a5, %[a5]" :  : [a5] "m"(fs->a5)  : ) ;
    asm volatile ("lw a6, %[a6]" :  : [a6] "m"(fs->a6)  : ) ;
    asm volatile ("lw a7, %[a7]" :  : [a7] "m"(fs->a7)  : ) ;
    asm volatile ("lw s2, %[s2]" :  : [s2] "m"(fs->s2)  : ) ;
    asm volatile ("lw s3, %[s3]" :  : [s3] "m"(fs->s3)  : ) ;
    asm volatile ("lw s4, %[s4]" :  : [s4] "m"(fs->s4)  : ) ;
    asm volatile ("lw s5, %[s5]" :  : [s5] "m"(fs->s5)  : ) ;
    asm volatile ("lw s6, %[s6]" :  : [s6] "m"(fs->s6)  : ) ;
    asm volatile ("lw s7, %[s7]" :  : [s7] "m"(fs->s7)  : ) ;
    asm volatile ("lw s8, %[s8]" :  : [s8] "m"(fs->s8)  : ) ;
    asm volatile ("lw s9, %[s9]" :  : [s9] "m"(fs->s9)  : ) ;
    asm volatile ("lw s10,%[s10]":  : [s10]"m"(fs->s10) : ) ;
    asm volatile ("lw s11,%[s11]":  : [s11]"m"(fs->s11) : ) ;
    asm volatile ("lw t3, %[t3]" :  : [t3] "m"(fs->t3)  : ) ;
    asm volatile ("lw t4, %[t4]" :  : [t4] "m"(fs->t4)  : ) ;
    asm volatile ("lw t5, %[t5]" :  : [t5] "m"(fs->t5)  : ) ;
    asm volatile ("lw t6, %[t6]" :  : [t6] "m"(fs->t6)  : ) ;
    
    
    // пеерход в pxCode
    asm volatile ("lw   a1, %0     ;"
	          "addi sp, sp, %1 ;"
	          "jr   a1         ;"   // пеерход в pxCode, в main уже не вернемся никогда! возврат произойдет в return_address
	          : :  "m"(fs->mepc) , "i"(portCPU_CONTEXT_SIZE + portFPU_CONTEXT_SIZE + (2 * portWORD_SIZE)) : ) ;
}