Портируем scmRTOS на MCU 8051

В конце концов меня задолбала OSAL. Основной проблемой стала не ее кривость а сам принцип кооперативных систем. По факту кооперативка это более развитая форма конечного автомата. А это означает, что при разрастании системы увеличивается количество состояний. Причем эта зависимость ни хрена не линейная. В конце концов, в большом проекте, вы получите дохренилион глобальных состояний, локальных состояний, еще более локальны состояний и еще… При этом названия у этих состояний будут самые безумные, в определенный момент вы просто задолбаетесь выдумывать новые имена.
Было решено использовать ртос. Погуглив я ничего не нашел (кроме FreeRTOS, uC, и т.п. систем). По непонятным причинам я не стал их использовать, уже не помню почему я от них отказался. В итоге захотелось портировать уже обкатанную систему scmRTOS.
Начало
Говорю сразу это оказалось намного легче чем я думал. Портировал всего за два дня. Намного сложней было отловить ошибки, на это я потратил полторы недели. Имея опыт в портировании я тоже самое могу проделать уже за вечер.
- Я буду использовать SoC CC2510F32 и CC2530F256. У них ядро 8051 но разные модели адресации flash памяти. Это Near(прошивка менее 32Кбайт) и Banked(прошивка более 32Кбайт). Есть еще Extended 1 и Extended 2, у меня камней с подобной адресацией нет но это тоже самое только регистр DPTR 24х битный.
- Среда разработки IAR 8051 8.10.4, в этом пункте особого выбора нет. Наличие хардварного отладчика обязательно.
- Документация datasheet и «8051 IAR C/C++ Compiler. Reference Guide for the MCS-51 Microcontroller Family». Дока на компилятор очень важна в нее будем смотреть постоянно.
MCS-51
Это одно из самых древнейших ядер. Оно поражает своей живучестью на рынке.
Ядро очень простое. Его очень легко освоить. Все мнемоники элементарные и их можно вызубрить за полчаса. В свое время я набросал проект на 1700 строк ассемблерного кода при этом толком не зная ядра.
Важные для нас регистры: A,B,R0..R7,PSW, SP,DPTR. Вот эти регистры мы и будем сохранять/восстанавливать при переключении контекста.
Но туже сразу замечаем, что чрезмерная простота и гробит 8051. Если в AVR стек паскудный, то в 8051 это просто тихий ужас! Нам дано всего 256 байт, при этом часть стека занимает банк регистров. Также он растет в неправильную сторону.
Делаем сразу заметку. В огромных проектах RTOS использовать невозможно. Банально не хватит стека.
IAR
Изучение «8051 IAR C/C++ Compiler. Reference Guide for the MCS-51 Microcontroller Family» в обязательном порядке!
Из неe мы узнаем, что есть еще виртуальные регистры V0..V31. Количество их задается в настройках проекта. Они используются как локальные буферы (связанно с особенностями ядра MCS-51, к некоторым байтам мы можем обращаться прямо а не косвенно).
Также есть регистр VB, адрес которого указывается в файле конфигурации линковщика.
Узнаем еще, что IAR отлично знает о проблемном стеке и предоставляет нам на выбор два программных стека PSTACK(размер 256 байт) и XSTACK( размер до 64Кбайт). В этих стеках сохраняется локальный контекст при входе в функцию/прерывание.
Параметры в функцию передаются через регистры

Ну а если не влазит, то через программный стек.
Итого: по мимо основных регистров нам нужно сохранять еще и регистры IAR (VB,V0..V31,XSP).
Контекст
Как уже стало ясно контекст будет состоять из регистров 8051 и виртуальных регистров IAR. А еще забегая на перед скажу, что нужен еще флаг EA(глобальный флаг разрешения прерываний).
Я буду использовать только один банк регистров, т.к. использование всех 4х банков не дает преимуществ.
Контекст:
A — 1 байт
B — 1 байт
PSW — 1 байт
R0..R7 — 8 байт. Располагаются в начале стека по адресу 0x00-0x07
SP — 1 байт.
DPTR — 2 байта.
V0..V31 — от 8 до 32 байт. Располагаются сразу за банком регистров.
VB — 1 байт. Имеет фиксированный адрес 0x20. Адрес можно изменить в настройках линковщика.
SPX — 2 байта. Располагается сразу за виртуальными регистрами.
EA — 1 байт.
Итого размер контекста от 24 до 50 байт.

Кусок стека.
Желтый — регистры.
Красный — виртуальные регистры.
Голубой — SPX
Зеленый — VB


Начинаем портировать.
С теорией разобрались. Пора приступать к практике.
Наиболее подходящим по духу является порт для AVR. В нем тоже используется раздельный стек. Вот его мы и возьмем за основу.
Сначала создадим проект пустышку и добавим к нему все нужные файлы.

Я думаю вы уже удосужились прочитать доку по scmRTOS.
Настраиваем


Выбираем максимально возможный(для этой конфигурации) размер ISTACK.
Забегая наперед скажу. Адрес расположения стека строго фиксированный. Мы можем менять только указатель на голову и только в пределах одной страницы. Естественно адреса возвратов должны хранится только в хардварном стеке. Также компилятор IAR подкидывает пакость. Мы не можем разместить свой массив в секции ISTACK во время компиляции. Можно только зарезервировать место вот так
__idata uint8_t r_stack0[32];
Эта строка не может находиться внутри класса.
Такой метод резервирования стека возвратов для процесса не подходит. Т.к. придется менять код ядра.
Поэтому я пошел другим путем. Создаю большую секцию ISTACK и программно формирую указатели(это будет впереди). При использовании такого способа почти не нужно изменять ядро.
XDATA — это наш XSTACK. Под него не требуется много места. Болеет того он используется только на старте. Если вам остро не хватает оперативки, то вы запросто можете определить смещение на секцию XSTACK(я думаю вы удосужились прочитать доку на компилятор) и смело использовать это место в процессах.
Добавляем пути для асма и C++.
$PROJ_DIR$\Src
$PROJ_DIR$\..\scmRTOS\8051
$PROJ_DIR$\..\scmRTOS\Common
$PROJ_DIR$\..\scmRTOS\Extensions\Profiler
$PROJ_DIR$\..\ccxx10
Пустышка готова. Хотя я еще рекомендую по удалять все лишнее из примера. Так будет проще работать.
Main
//---------------------------------------------------------------------------
int main()
{
// Disable global interrupt by setting the [IEN0.EA=1]
EA = 0;
// Change clock source for low power oscillator to LS RCOSC.
CLKCON |= CLKCON_OSC32;
// Power up unused oscillator (HS XOSC).
SLEEP &= ~SLEEP_OSC_PD;
// Wait until the HS XOSC is stable.
while( !(SLEEP & SLEEP_XOSC_S) );
// Change the system clock source to HS XOSC.
CLKCON &= ~CLKCON_OSC;
// Wait until source has actually changed.
while (CLKCON & CLKCON_OSC);
// Power down the HS RCOSC, since it is not beeing used.
// Note that the HS RCOSC should not be powered down before the applied
// system clock source is stable (SLEEP.XOSC_STB = 1).
SLEEP |= SLEEP_OSC_PD;
//System timer
T4CTL = T4CTL_DIV_128 | T3CTL_START | T4CTL_OVFIM | T4CTL_CLR | T4CTL_MODE_FREERUN; // 2.52ms
T4IE = 1;
//
OS::run();
}
Выбираем резонатор и настраиваем системный таймер. В качестве системного таймера используется Timer4.
OS_Target.h
#if (((__TID__ >> 8) & 0x7F) != 0x20)
#error "This file must be compiled for 8051 processor only."
#endif
Только для 8051.
//------------------------------------------------------------------------------
//
// IAR Systems include file[s]
//
#include <intrinsics.h> // interrupt state handling
Добавляем макросы для работы с прерываниями.
#define INLINE _Pragma("inline=forced") inline
Об этом дефайне немного по подробней. Это указание компилятору делать функцию с этим дефайном всегда инлайновой. Но по каким-то причинам IAR любит активно ее игнорить. Даже когда вы выставите наиболее благоприятные для нее настройки оптимизации, то иногда IAR все равно будет ее игнорить. Возможно это связанно с особенностями архитектуры. Поэтому на нее не надеемся.
#define SYS_TIMER_CRIT_SECT() TCritSect cs
#define CONTEXT_SWITCH_HOOK_CRIT_SECT() TCritSect cs
#define SEPARATE_RETURN_STACK 1
В MCS-51 прерывания могут вытесняться другими прерываниями. Поэтому важные моменты должны быть в критической секции. А также говорим системе, что стек используется раздельный.
class TCritSect
{
public:
TCritSect () : StatusReg(__get_interrupt_state()) { __disable_interrupt(); }
~TCritSect() { __set_interrupt_state(StatusReg); }
private:
status_reg_t StatusReg;
};
Код крит. секции. Тут и так все понятно.
#define get_return_sp() SP
#define set_return_sp(x) {SP=x;}
#pragma segment="ISTACK"
#define set_isr_stack_pointers() {SP = reinterpret_cast<uint16_t>(__segment_begin("ISTACK")) + 1;}
Добавляем скрипты для работы с указателем стека.
struct TSavedSP
{
stack_item_t ReturnSP;
};
Правим структуру которая используется в обвертке TISRW_SS
class TISRW
{
public:
INLINE TISRW() { ISR_Enter(); }
INLINE ~TISRW() { ISR_Exit(); }
private:
//-----------------------------------------------------
INLINE void ISR_Enter() // volatile
{
Kernel.ISR_NestCount++;
enable_interrupts();
}
//-----------------------------------------------------
INLINE void ISR_Exit()
{
TCritSect cs; //disable_interrupts();
if(--Kernel.ISR_NestCount)
{
return;
}
Kernel.sched_isr();
}
//-----------------------------------------------------
};
В обвертке TISRW немного меняется только деструктор. В AVR на выходе из прерывания устанавливается флаг EA аппаратно. В 8051 его нужно выставлять руками, да и он может быть вовсе запрещен. Поэтому используем крит секцию.
class TISRW_SS
{
public:
INLINE TISRW_SS()
{
if(Kernel.ISR_NestCount++ == 0)
{
SavedSP.ReturnSP = get_return_sp();
//SavedSP.DataSP = get_data_sp();
set_isr_stack_pointers();
}
enable_interrupts();
}
INLINE ~TISRW_SS()
{
TCritSect cs;
if(--Kernel.ISR_NestCount) return;
set_return_sp(SavedSP.ReturnSP);
//set_data_sp(SavedSP.DataSP);
Kernel.sched_isr();
}
private:
};
А тут уже идут сильные изменения. Это связанно с игнорирование INLINE. Деструктор/конструктор переписан так, чтоб ее не проигнорировать.
Также в порте AVR происходит переключение софтварного стека. В этом случае этого делать нельзя. IAR подбросил еще одну свинку. Даже если функция инлайновая и IAR это принял, то он все рано перед входом в функцию сохраняет локальный контекст в XSTACK.
Получается следующее
Сохранение локального контекста -> переключаем софтварный стек -> восстанавливаем мусор из стека прерывания на выходе из конструктора.
Поэтому переключается только хардварный стек.
Создание процесса
Тут скрепя зубами придется изменить немного ядро. Это связанно с тем как выделяется место под стек возврата.
typedef OS::process<OS::pr0, 220,39> TProc1;
220 — локальный стек. С этим проблем нет, это обычный массив.
39 — стек возврата. Это указание сколько нужно отрезать от ISTACK.
Для начала топаем к шаблону в OS_Kernel.h
template<TPriority pr, size_t stack_size, size_t rstack_size>
class process : public TBaseProcess
{
public:
INLINE_PROCESS_CTOR process();
OS_PROCESS static void exec();
#if scmRTOS_PROCESS_RESTART_ENABLE == 1
INLINE void terminate();
#endif
private:
stack_item_t Stack [stack_size/sizeof(stack_item_t)];
//stack_item_t RStack[rstack_size/sizeof(stack_item_t)];
stack_item_t *RStack;
};
template<TPriority pr, size_t stack_size, size_t rstack_size>
process<pr, stack_size, rstack_size>::process() : TBaseProcess( &Stack[stack_size/sizeof(stack_item_t)]
, RStack
, rstack_size
, pr
, reinterpret_cast<void (*)()>(exec)
#if scmRTOS_DEBUG_ENABLE == 1
, Stack
, RStack
#endif
)
{
}
RStack у нас теперь не массив а указатель и он передается конструктору(по фату нужен адрес на указатель, но изменения ядра должны быть минимальны).
Кстати обратите внимание как передается указатель на софтварный стек процесса &Stack[stack_size/sizeof(stack_item_t). Т.е. передает указатель на последний элемент массива. Ведь он у нас растет в правильную сторону.
В конструкторе TBaseProcess тоже вносим изменения.
TBaseProcess::TBaseProcess( stack_item_t * Stack
, stack_item_t * RStack
, stack_item_t rs_size
, TPriority pr
, void (*exec)()
#if scmRTOS_DEBUG_ENABLE == 1
, stack_item_t * aStackPool
, stack_item_t * aRStackPool
#endif
) : StackPointer(Stack)
, Timeout(0)
, Priority(pr)
#if scmRTOS_DEBUG_ENABLE == 1
, WaitingFor(0)
, StackPool(aStackPool)
, RStackPool(aRStackPool)
#endif
#if scmRTOS_PROCESS_RESTART_ENABLE == 1
, WaitingProcessMap(0)
#endif
{
TKernel::register_process(this);
init_stack_frame( Stack
, RStack
, rs_size
, pr
, exec
#if scmRTOS_DEBUG_ENABLE == 1
, aStackPool
, aRStackPool
#endif
);
}
Мы добавляем переменную rs_size(размер стека возврата) и pr(приоритет, он же номер в массиве).
Метод init_stack_frame должен знать размер стека возврата и того кто передает(этот параметр нужен только для сброса или функций отладки). Опят же, это вынужденные изменения.
OS_Target_cpp.cpp
//------------------------------------------------------------------------------
#pragma vector=T4_VECTOR
OS_INTERRUPT void OS_SystemTimer_ISR(void)
{
/* Clears the CPU interrupt flag. */
T4IF = 0;
/* Clears the module interrupt flag. */
T4OVFIF = 0;
scmRTOS_ISRW_TYPE ISR;
Kernel.system_timer();
#if scmRTOS_SYSTIMER_NEST_INTS_ENABLE == 1
//ABLE_NESTED_INTERRUPTS();
#else
TCritSect cs;
#endif
#if scmRTOS_SYSTIMER_HOOK_ENABLE == 1
system_timer_user_hook();
#endif
}
Тут у нас обработчик системного таймера. Изменений как таковых нет, кроме scmRTOS_SYSTIMER_NEST_INTS_ENABLE. Помним, что в 8051 есть вытеснения прерываний. Поэтому если scmRTOS_SYSTIMER_NEST_INTS_ENABLE=1, то мы ничего не делаем ибо уже все и так разрешено. А если =0, то ставим крит секцию.
Инициализация контекста
void TBaseProcess::init_stack_frame( stack_item_t * Stack
, stack_item_t * RStack
, stack_item_t rs_size
, uint8_t pr
, void (*exec)()
#if scmRTOS_DEBUG_ENABLE == 1
, stack_item_t * StackPool
, stack_item_t * RStackPool
#endif
)
{
static uint8_t free_space=0xFF; //0x21-0xFF
free_space-=rs_size;
if(free_space<=0x21)
{
while(1); //"rstack overflow"
}
RStack = ( stack_item_t*)( HARDWARE_STACK_ADDRESS + free_space); //stack point
#if scmRTOS_DEBUG_ENABLE == 1
RStackPool=RStack+rs_size;
this->RStackPool=RStackPool;
#endif
#if scmRTOS_PROCESS_RESTART_ENABLE == 1
RStack_point[pr]=RStack;
#if scmRTOS_DEBUG_ENABLE == 1
RStack_pool_point[pr]=RStackPool;
#endif
#endif
*(++RStack)=reinterpret_cast<uint16_t>(exec);
*(++RStack)=reinterpret_cast<uint16_t>(exec)>>8;
StackPointer = Stack;
*(--StackPointer) =0x80; //IEN0 register
StackPointer -= 1+2; //A+DPTR
*(--StackPointer) = reinterpret_cast<uint16_t>(RStack);// RStack; //SP
StackPointer-=1+8+VREG_COUNT+1+1; //PSW+REG+VREG_COUNT+VB+B
uint8_t _spxl=(uint16_t)(StackPointer-2);
uint8_t _spxh=((uint16_t)(StackPointer-2)>>8);
*(--StackPointer) = _spxl; //SPX
*(--StackPointer) = _spxh;
#if scmRTOS_DEBUG_ENABLE == 1
//-----------------------------------------------------------------------
// Fill stack pools with predefined value for stack consumption checking
// Do not fill area for GPR and SFR on data stack
stack_item_t *fill_ptr;
fill_ptr = RStack;
while( fill_ptr < RStackPool )
{
*++fill_ptr = STACK_DEFAULT_PATTERN;
}
fill_ptr = StackPointer;
while( fill_ptr > StackPool )
{
*--fill_ptr = STACK_DEFAULT_PATTERN;
}
#endif // scmRTOS_DEBUG_ENABLE
}
Один из самых важных методов. Он один для всех процессов. Поэтому мы можем использовать статичную переменную для контроля свободного места.
free_space=0xFF; По умолчанию выставлена на конец стека.
free_space-=rs_size;
if(free_space<=0x21)
{
while(1); //"rstack overflow"
}
Отрезаем кусочек и проверяем на переполнение. Если переполнение есть, то вешаемся. Лучше не работать вообще, чем работать неправильно. Ох если бы вы знали как меня заипали плавающие ошибки!!! Это ад их отлавливать! Одну из ошибок я отлавливал 8 часов подряд.
RStack = ( stack_item_t*)( HARDWARE_STACK_ADDRESS + free_space); //stack point
Формируем указатель.
К дефайну HARDWARE_STACK_ADDRESS мы еще вернемся.
*(++RStack)=reinterpret_cast<uint16_t>(exec);
*(++RStack)=reinterpret_cast<uint16_t>(exec)>>8;
Записываем в istack стек адрес возрата.
*(--StackPointer) =0x80; //IEN0 register
StackPointer -= 1+2; //A+DPTR
*(--StackPointer) = reinterpret_cast<uint16_t>(RStack);// RStack; //SP
StackPointer-=1+8+VREG_COUNT+1+1; //PSW+REG+VREG_COUNT+VB+B
uint8_t _spxl=(uint16_t)(StackPointer-2);
uint8_t _spxh=((uint16_t)(StackPointer-2)>>8);
*(--StackPointer) = _spxl; //SPX
*(--StackPointer) = _spxh;
А это наш контекст на момент старта процесса. IAR за нас уже обнулил все массивы. Так, что нули будут гарантированно.
В начале записываем бит EA. После записываем, только младшую часть стека возврата. А под конец ставим указатель SPX на конец контекста.
Это выглядит вот так

Софтварный стек растет вниз поэтому он не портит контекст.
А, что если внутри процесса есть переменные? Произойдет следующее:
1) При входе в процесс XSP будет указывать на свободное место.
2) IAR затолкает все в софтварный стек. Т.е. в стек процесса.
3) При переключение контекста он обновиться и будет указывать на новое место.

scmRTOS_CONFIG.h
#define BANK_OFFSET 0
#define REG_COUNT 8 //register counter (always 8 )
#define VREG_COUNT 8 //virtual register counter
#define HARDWARE_STACK_ADDRESS 0xFF00 //0x1F00 //see in the datasheet
#define VB 0x20 //see linker config (lnk51ew_cc2510F32.xcl)
По факту здесь должны быть только настройки scmRTOS. Но я немного нарушил правила и добавил еще и настройки проекта.
HARDWARE_STACK_ADDRESS это значение мы берем из даташита на конкретный микроконтроллер. Оно указывает по какому адресу находиться стек.
OS_Target_asm.s51
Это казалось бы самое сложное, но это самое простое. Ведь асм на 8051 чрезвычайно прост.
Нужно портировать сразу два способа переключения контекста.
1) Прямая передача. Это когда мы говорим прямо, что нужно переключить контекст.
2) Использование программного прерывания. Это когда мы ОС просим переключить контекст а она уже его переключит когда будет наиболее удобно для этого.
Про прямую передачу говорить особо нечего. Там все просто. На входе получили два адреса. В один записали, из другого восстановили. И все.
Для сохранения контекста я использовал 6ть буферов.
RSEG IDATA_I
//space for local buffer
RR0: DS 1
RR1: DS 1
RR2: DS 1
RR3: DS 1
RR4: DS 1
RR5: DS 1
Куда ведь надо сохранять промежуточный результат. В общем кому надо глянут ассемблерный файл.
С программным прерывание тоже оказалось все просто
common INTVEC
org CONTEXT_SWITCH_ISR_VECTOR
ljmp ContextSwitcher_ISR
RSEG CODE_C
ContextSwitcher_ISR:
Регистрируем свой обработчик.
На входе запрашиваем адрес текущего контекста
lcall os_context_current_get
И на выходе адрес нового контекста
lcall os_context_switch_hook
И на этом все. Стоит только заметить, что флаг EA должен восстанавливаться последним. Ведь прерывание может быть вытесненным.
А какое прерывание нужно использовать? Самое ненужное и самое не приоритетное.
Я использовал прерывание WDT_Timer это одно из самых ненужных прерываний да и на работу WDT оно никак не влияет.
Описывается оно в scmRTOS_TARGET_CFG.h
#define CONTEXT_SWITCH_ISR_VECTOR WDT_VECTOR
А это для его активации
INLINE void raise_context_switch() {IRCON2|=(1<<4); SPM_CONTROL_REG |= IEN2_WDTIE; } // enable SPM interrupt
INLINE void block_context_switch() { IRCON2&=~(1<<4); SPM_CONTROL_REG &= ~IEN2_WDTIE; } // disable SPM interrupt
Низший приоритет у него стоит по дефолту. И во время работы нельзя повышать его приоритет.
Почти конец
На форуме электроникса _Артём_ мне подсказал, что с вытесняющими прерываниями не все так просто.
Ведь когда мы попадаем в прерывание, то в 8051 глобально они все еще разрешены. Их нужно запретить до попадания в конструктор обвертки. Иначе это может привести к лишнему переключению контекста.
Сказано сделано. Первой командой так и сделал. Глянул, что сгенерил IAR и вижу, что запрет прерываний ни разу не первая команда. Сначала IAR в лучших своих традициях сохраняет локальный контекст а уже потом запрещает прерывания. Немного документации и выход нашелся. Дело в том, что в 8051 под вектор прерывания выделяется 8байт памяти. Вот туда мы и можем вставить свой мини обработчик.
Делается это легко.
Сначала делаем редефайн векторов на новый адрес.
#undef RFTXRX_VECTOR
#undef ADC_VECTOR
#undef URX0_VECTOR
.........
#define RFTXRX_VECTOR VECT( 0, 0x03+2 ) /* RF TX done / RX ready */
#define ADC_VECTOR VECT( 1, 0x0B+2 ) /* ADC End of Conversion */
#define URX0_VECTOR VECT( 2, 0x13+2 ) /* USART0 RX Complete */
......
2 — это размер инструкции сброса флага прерываний.
Говорю сразу. Адреса векторов прерываний у всех 8051 стандартны. Меняются только названия. Поэтому в другом камне максимум нужно изменить имя вектора.
А дальше в ассемблерном файле пишем свой обработчик
COMMON INTVEC
ORG 0x03//RFTXRX_VECTOR-2
CLR IEN0.7
ORG 0x0B//ADC_VECTOR-2
CLR IEN0.7
ORG 0x13//URX0_VECTOR-2
CLR IEN0.7

Вуаля. Первая инструкция это гарантированное запрещение прерывания. Ее вытеснить невозможно.
Конец
Как видим ничего сложно нет. Все элементарно. Забавно но на фрилансе подобная услуга стоит 1000 у.е. Хотя по факту работы всего на 300-400у.е.
Это не пособие HOWTO а пример как такое можно сделать уже с другим микроконтроллером.
Исходники можно взять тут. Естественно мелочь была опущена.
Думаю скоро они появятся в репозитории.
Тут еще нужно вставить фотку котят и ссыль на ютуб. Где будет показан пример мегающих светиков и я 2-3 часа рассказываю о том как это работает. Но это за отдельную плату.
PS: Вспомнил почему отверг все другие ОС. Дело в компиляторе и отладчике. IAR хорошо дружит с продукцией Ti.
- +11
- 20 ноября 2012, 03:24
- a9d
Тут еще нужно вставить фотку котят и ссыль на ютуб. Где будет показан пример мегающих светиков и я 2-3 часабубню на грани слышимости и через два слова на третье начинаю мычать и замолкатьрассказываю
о том как это работает. Но это за отдельную плату.
+100500! :)
Не надо так явно демонстрировать своё непонимание содержательной части, опыт придёт со временем. В отличие о текста, видео сделать очень не просто, ибо оно покажет весь уровень владения инструментарием в комплексе и что на словах просто в тексте на практике объяснить и донести не каждый сможет. Это не глупые однообразные комменты травить по постам.
А по статье интересно замечание о паскудности стека в AVR. Это в каком смысле? Что в архитектуре не так?
А по статье интересно замечание о паскудности стека в AVR. Это в каком смысле? Что в архитектуре не так?
А в чём выражается ограничения при работе с указателем? Я почему обратил на это внимание, т. к. имею опыт по компиляции одного проекта для AVR на C++ в двух различных компиляторах. У IAR и GCC по-разному осуществляется работа со стеком, но с использованием одной архитектуры, поэтому я не пойму теперь достоинство это или недостаток?
Работа со стеком в gcc мне нравится больше, ибо мне не нужно думать о размере стека или о его срыве при использовании локальных переменных, в IAR'е же я должен прикидывать размеры локальных переменных, чтобы не случился срыв, поэтому то, что работает в gcc далеко не всегда при идентичном виде заработает в iar.
Это одна из тех вещей, которые мне в iar не нравятся, так как требуют углубленного понимания работы компилятора изнутри, но сам iar мне нравится, потому я стараюсь его не забывать.
Работа со стеком в gcc мне нравится больше, ибо мне не нужно думать о размере стека или о его срыве при использовании локальных переменных, в IAR'е же я должен прикидывать размеры локальных переменных, чтобы не случился срыв, поэтому то, что работает в gcc далеко не всегда при идентичном виде заработает в iar.
Это одна из тех вещей, которые мне в iar не нравятся, так как требуют углубленного понимания работы компилятора изнутри, но сам iar мне нравится, потому я стараюсь его не забывать.
Доступна только команда mov. Никакой косвенной адресации. Если локальных переменных дохрена(на них не хватает регистров), то это приводит к печальке.
В 8051 тоже нет косвенной адресации для SP. А вот для SPX есть. Ибо эта пара храниться в первой половине стека.
В 8051 тоже нет косвенной адресации для SP. А вот для SPX есть. Ибо эта пара храниться в первой половине стека.
Давно не заглядывал в свои ассемблерные листинги, забыл уже как конкретно отличаются подходы gcc и iar, но чисто теоретически, что мешает, к примеру, сохранять указатель стека в одной из регистровых пар, для которых доступна косвенная адресация? Этого не делают, но это не означает, что это невозможно. Такой ли уж это минус тогда? Можно пихать в стек что угодно и косвенно обращаться к любому из локальных участков, это будет медленнее, чем с регистрами, видимо в этом и причина.
OSAL это детище Ti. Кооперативка работает как конечный автомат. Функция процесса завершается и запускается снова с новым состоянием.
Бред какой-то. У функции могут быть свой\и автомат\ы. На хера плодить геморрой?!
- demiurg1978
- 20 ноября 2012, 21:32
- ↑
- ↓
Честно скажу, не знаю, что такое OSAL. Не нашел русскоязычных источников. Но уже то, что я прочитал про состояния в OSAL, мне кажется бредом. Я программирую AVR на асме. Си все порываюсь изучать, да как-то все бросал на полпути. Дальше нескольких примерчиков дело так и не зашло. Для AVR написал простенькую кооперативную карусельку. Для вытесняющей задач до сих пор так и не нашлось. Кооперативной карусельки вполне хватает.
- demiurg1978
- 20 ноября 2012, 21:50
- ↑
- ↓
А у меня нет там состояний. У функций свои состояния.
Не буду пустое говорить. Про какие состояния в кооперативках вы говорите? Также состояния в OSAL меня сильно заинтересовали. Что там за состояния такие, что программирование превращается в геморрой.
Не буду пустое говорить. Про какие состояния в кооперативках вы говорите? Также состояния в OSAL меня сильно заинтересовали. Что там за состояния такие, что программирование превращается в геморрой.
- demiurg1978
- 20 ноября 2012, 22:00
- ↑
- ↓
Я и написал. Простая каруселька. Так все-таки, что там с состояниями?
- demiurg1978
- 20 ноября 2012, 22:03
- ↑
- ↓
Комментарии (29)
RSS свернуть / развернуть