конкретный пример вотъ. код вроде простой и понятный...
собственно переключение. приоритет самый низкий
Код
F_NAKED void ePendSV(void){
register U32 rpsp asm("r3");
asm volatile(" mrs %0,PSP; stmdb %0!,{r4-r11,lr}" :"=r"(rpsp));
pRunTcb->psp=rpsp;
pRunTcb=pReadyTcb;
rpsp=pRunTcb->psp;
asm volatile(" ldmia %0!,{r4-r11,lr}; msr PSP,%0; bx lr" :"+r"(rpsp));
}
naked и асм.вставки тк gcc тупой не умеет юзать ldm/stm. а кусок сишного кода тк леньки мне и синтакс gas - бей головой дуба :D писать не удобно
также тут исключен код, перегружающий MPU регионы, нету у STM32F1 MPU. на NXP моя ось еще и защищенная

постановка задачи,готовой к выполнению в очередь. используется
внутри практически всех системных функций (CreateThread,OpenMutex итд). приоритет этих функций всегда выше или равен PendSV.
Код
void tcbEnqueueReady(t_bragOsTcb *tcb){
U32 prio;
prio=tcb->priority;
cdllEnqueue(&readyQueue[prio],&tcb->queueNode);
readyPrioMsk|=1<<(31-prio);
if(prio<pReadyTcb->priority){
pReadyTcb=tcb;
PendSVset();
}
}
Системные функции могут вызыватся
только:
1. из SVC, в данном случаи приоритет выше, чем у PendSV. сделаете иначе - попадете на оооочень неприятные грабли! лень расписывать(много невсемпонятного текста) почему, но скажу пару ключевых слов: Приоритет, Стек, Tail-chainning. Если интересно - порисуйте диаграмки последовательности выполнения и все станет понятно.
2. из Systick. приоритет равен PendSV
Ессно выше приведенный код получается автоматически защищен от race-conditions.
Удаление задачи из очереди. Здесь PendSVset выполняется всегда, тк так сложилось, что у меня эта функция удаляет из очереди только ту задачу, которая в данный момент выполняется. но с ее помощью можно удалить любую задачу из очереди, при этом можно сделать проверку, чтобы даром не выполнять пустое переключение контекста,не влияющее на ход работы системы.
Код
void tcbDequeueReady(t_bragOsTcb *tcb){
U32 prio;
cdllRemove(&tcb->queueNode);
prio=tcb->priority;
if(cdllIsEmpty(&readyQueue[prio]))readyPrioMsk&=~(1<<(31-prio));
pReadyTcb=getNextReadyTcb();
PendSVset();
}
cdllХххх - работа с обычным двусвязным списком.
getNextReadyTcb - достает из головы этого самого списка(у меня их 32,на каждый тредовый приоритет свой список,выбирается не пустой список самого высокого приоритета) адрес tcb
PendSVset используется только в этих двух функциях, ну и еще при инициализации системы, чтобы переключится на первый тред.
ну и самый сок, добавление запроса в lock-free очередь. может использоватся любым прерыванием с любым приоритетом.
Код
int SrqEnqueue(U32 a0,U32 a1,U32 svc){
register U32 r0 asm("r0");
register U32 r1 asm("r1");
U32 tail,newtail;
// lockless enqueue
do{
asm volatile("ldrex %0,%1" :"=r"(tail) :"m"(srqQueue.tail));
newtail=(tail+1)%MAX_SRQ_QUEUE_ITEMS;
if(newtail==srqQueue.head)return ERR_QUEUE_FULL;
asm volatile("strex %0,%2,%1" :"=&r"(r),"=m"(srqQueue.tail) :"r"(newtail));
}while(r);
// write data
r0=a0;r1=a1;
asm volatile("stmia %3,{%0,%1,%2}" : :"r"(r0),"r"(r1),"r"(svc),"r"(&srqQueue.buf[tail]) :"memory");
PendSTset();
return 0;
}
MAX_SRQ_QUEUE_ITEMS кратно степени двойки - % заменяется компиллером на обычный AND.
PendSTset - гыгы

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

Код
void eSystick(void){
..... блаблалбла-декларация....
SrqDequeueRun();
if(!IsSystick())return;
..... блаблабла--системный таймер........
IsSystick - это действительно таймер тикнул?
выгрузка из очереди. wait-free. выполняется только из eSystick. здесь все просто,никаких хитростей.
Код
void SrqDequeueRun(void){
register U32 r0 asm("r0");
register U32 r1 asm("r1");
U32 a0,a1,svc;
t_srqItem *data;
U32 head;
head=srqQueue.head;
if(head==srqQueue.tail)return;
while(head!=srqQueue.tail){
data=&srqQueue.buf[head];
head=(head+1)%MAX_SRQ_QUEUE_ITEMS;
asm volatile("ldmia %3,{%0,%1,%2}" :"=r"(r0),"=r"(r1),"=r"(svc) :"r"(data));
a0=r0;a1=r1;
/*if(svc>=sizeof(svcTable)/sizeof(t_Callback))*/
a0=((int(*)(U32,U32))svcTable[svc])(a0,a1);
}
srqQueue.head=head;
}
на RVCT все обходится почти без асма с тем же результатом, он ldm/stm нормально генерит. Только алгоритмический код у него немного тормознее выходит (базового кода моей оси это не касается), или это я просто привык писать под gcc,выработалось чутье его алгоритмов оптимизации

)
Мютексы простые, без наследования приоритетов. Мне это не нужно. и производительность в +
Семафоры есть простые и с таймаутом - примитивы только set,signal,wait,wait+timeout
тред можно только создать и убить с другого треда. сам тред может себя остановить на некоторое время или выгрузится. также его могут убить обработчики fault-ов через ту же lock-free srq.
Ну и в принципе все.
Никаких критических секций, никаких циклов поиска итп.
KIS - keep it simple