Модифицируем OSAL.

Только хардкор



История зарождения проблемы

OSAL — это простейшая кооперативная система c от Ti. Диспетчер ОС системы поддерживает приоритеты и события.
У меня есть подозрения, что она разработана индусами. OSAL полное УГ.

Зачем тогда такое УГ использовать ?? Дело в том, что Ti сделало хитрый финт ушами. Они вмонтировали в OSAL такие «шедевры» как Z-Stack и BLE Stack.

Шло время. И тут бац, ударила молния. Рынок помешался на Cortex-M3. Ti решило запилить SoC CC2580 (ZigBee c кортексом). Вот тут-то и началось веселье.

Изначально разработчики приняли глупое решение и привязали Z-Stack к Clib. Тем самым отсекли C++. Они попытались это исправить, но осознав, что стек придется переписать, отказались от этой идеи. В коде до сих пор остались их попытки отвязаться от Clib.

Разработчики, непонятно зачем вмонтировали Z-Stack в кооперативную систему. OSAL под это дело и была разработана. Вот нахрена они это сделали!!! В миру есть дохренище ОС, но они решили слепить свою с блэкджеком и шлюхами. Дело это поручили аматорам (я не верю, что профессионалы могли такое родить)… они и слепили УГ.

Пришла пора осваивать кортексы… А вот и облом. OSAL смотрится не кошерно на кортексе. Они попытались прикрутить FreeRTOS, но потерпели фейл. Следы ихнего фейла в коде еще остались.
Служба поддержки зарядила, что RTOS не будет т.к. нужно переписать весь стек а они это делать не собираются.

Я буду дико ржать если они на рынок выплюнут CC2580 с OSAL на борту.

Почему OSAL УГ

Причины:
  • Кооперативка. Если в конечном устройстве кооперативка еще смотрится. То в роутере, где проект получается сам по себе большим, это уже превращается в геморой.
  • Во всех хороших ОСях есть папка target. В ней находится аппаратно зависимая часть. В OSAL она тоже есть, но только чтоб не отставать от моды. По факту почти каждый файл ОС привязан к конкретному камню. Я не пойму, как можно было сделать простейшую кооперативку настолько зависимой от конкретного железа! Оценил по достоинству когда портировал на CC2510.
  • Разработана аматорами.
  • Разработчики не особо понимали смысла либы HAL. Поэтому OSAL вмонтирована в HAL. Всегда думал, что должно быть наоборот, либо раздельно. Благодаря этому приходиться одновременно учить HAL,OSAL,MAC,Z-Stack. Кстати OSAL тоже привязана к MAC.


Система УГ. Не рекомендую использовать вообще. Но у вас нет выбора ;).
Я попробовал это дело исправить, портировал scmRTOS, познал всю глубину проблемы и забил. Появилась мысля привязать FreeRTOS, но там нужно это все дело портировать c SDCC на IAR и непонятно как привязать Z-Stack. На это я тоже забил…

Ах да, тру эмбеддеры запиливают свою ОС… которая как правило не отличается от OSAL. Но я не тру.
Поэтому решил модифицировать OSAL.

ЗЫ: Недавно вышел стек ZBOSS. Разработан славянами на деньги немцев. Он намного понятней, чем Z-Stack и не привязан к какой либо ОС или библиотеке. Но он слишком свежий.

Пару слов о Z-Stack

Это действительно наиболее развитый стек… При этом он еще обильно обмазан фекалиями… Реально, у меня неоднократно появлялась мысля перейти на другое железо.

Особенности:
  • Он умнее разработчика. Поэтому он может перезагрузить SoC, проверить уровень напряжения и если оно ему не понравится, то зависнуть. Любит игнорить прямые приказы. Кстати вопрос «Как выключить Z-Stack» у службы поддержки вызывает разрыв шаблона. Никто не догадался сделать функцию выключения(выключить можно, но через попу).
  • Неквалифицированная служба поддержки. Игнорте ответы если ответил индус.
  • Чрезмерно запутан и нелогичен. Вы прочитали в доке описание API функции, вызываете ее и получаете самый неожиданный результат. Оказывается перед этим нужно вызвать еще 2-3 функции а уже потом эту. Естественно это нигде не написано. В Ti это просекли быстро и запилили SimpleAPI, но они поручили это все тем-же людям. В итоге получилось над запутанным API еще более запутанное SimpleAPI и все вместе это куй его знает как работает. В Ti не рекомендуют использовать SimpleAPI.
  • Документации много, но она не информативна.
  • Пробелы в документации. Почти все важное тактично не описано вообще.
  • Догматичен. Работает по принципу «либо так, либо никак и вообще я перезагружаюсь». Разработчик не чувствует себя царьком и богом над чипом в присутствии Z-Stack. Ti убивает всю магию системного программирования.


Чего не хватает системе


Жутко не хватает возможности прерывать выполнение процесса. На роутере это просто пипец какая проблема.

Вот это и будем исправлять. Для этого нужно модифицировать диспетчер и добавить функцию yield.

Стандартный диспетчер


У нас дан единый стек и все задачи вертятся в нем. Поэтому их прервать почти невозможно.



osalTimeUpdate();
  Hal_ProcessPoll();

  do {
    if (tasksEvents[idx])  // Task is highest priority that is ready.
    {
      break;
    }
  } while (++idx < tasksCnt);

  if (idx < tasksCnt)
  {
    uint16 events;
    halIntState_t intState;

    HAL_ENTER_CRITICAL_SECTION(intState);
    events = tasksEvents[idx];
    tasksEvents[idx] = 0;  // Clear the Events for this task.
    HAL_EXIT_CRITICAL_SECTION(intState);

    activeTaskID = idx;
    events = (tasksArr[idx])( idx, events );
    activeTaskID = TASK_NO_TASK;

    HAL_ENTER_CRITICAL_SECTION(intState);
    tasksEvents[idx] |= events;  // Add back unprocessed events to the current task.
    HAL_EXIT_CRITICAL_SECTION(intState);
  }


Принцип действия простейший. Обновляем таймеры, обрабатываем кривой HAL. После в списке событий ищем наиболее приоритетный процесс готовый к работе. Если не нашли, то перезапускаем диспетчер.
Ну а если нашли, то вызываем этот процесс. Все.

/* Yield in case cooperative scheduling is being used. */
#if defined (configUSE_PREEMPTION) && (configUSE_PREEMPTION == 0)
  {
    osal_task_yield();
  }
#endif


Кстати это ихние потуги исправить проблему… Умерли в зародыше.

Что делать?

У нас нет доступа ко многим задачам Z-Stack(можно получить через анус). Поэтому будем расслаивать на уровни все задачи. У каждого уровня будет свой стек.



Делается это с пол пинка.

Т.к. У уровня есть свой стек, то уровни тогда могут друг друга вытеснять. Также можно передавать управление, внутри уровня, другим задачам. Как же такое возможно, ведь у задач нет своих личных стеков ?! Будем пользоваться рекурсией. В кооператвке задачи, это конечные автоматы и ничего страшного если один конечный автомат вызовет другой минуя диспетчер.

Понеслась

Лезем в файл инициализации процессов и добавляем.

uint8 processList[sizeof( tasksArr ) / sizeof( tasksArr[0] )];
s_layer layerContext[2];

//allocate stack
__idata uint8 r_stack1[0x99];
uint8 v_stack1[0x300];
//__idata uint8 r_stack2[0x20];   //for one layer, you can use the system stack
uint8 v_stack2[0x100];


processList — определяет принадлежность процесса к уровню.
layerContext — структура с описанием стека уровня (кому надо глянет описание структуры в коде).

Дальше выделяется стек для процессов. Закоментированный r_stack2 это не глюк, для этого уровня мы будем использовать стек main-a.

Кстати обратите внимание как выделяется место под стек. При такой форме записи, размер стека не будет зависеть от языка программирования или даже от микроконтроллера! Некоторые это делают неправильно, поэтому размер у них зависит от языка программирования. А здесь мы четко указываем сколько надо.

uint8 taskID = 0;

  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));

  macTaskInit( taskID++ );
  nwk_init( taskID++ );
  Hal_Init( taskID++ );
#if defined( MT_TASK )
  MT_TaskInit( taskID++ );
#endif
  APS_Init( taskID++ );
#if defined ( ZIGBEE_FRAGMENTATION )
  APSF_Init( taskID++ );
#endif
  ZDApp_Init( taskID++ );
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
  ZDNwkMgr_Init( taskID++ );
#endif
  GenericApp_Init( taskID );
  
  //init layer list
  for(uint8 i=0; i<(tasksCnt-1); i++)
  {
    processList[i]=0;
  }
  processList[tasksCnt-1]=1;
  
  //init layer context
  layerContext[0].layer=0;
  layerContext[0].rstack=(uint8*)&r_stack1;
  layerContext[0].pstack=&v_stack1[256];
  init_stack_frame(layerContext[0].layer, layerContext[0].rstack, layerContext[0].pstack, osal_start_system); 
  
  layerContext[1].layer=1;
  //layerContext[1].rstack=(uint8*)&r_stack2;
  #pragma segment="ISTACK"
  layerContext[1].rstack=__segment_begin("ISTACK");
  layerContext[1].pstack=&v_stack2[256];
  init_stack_frame(layerContext[1].layer, layerContext[1].rstack, layerContext[1].pstack, osal_start_system); 


Инициализируем задачи и контексты.

void init_stack_frame(uint8 gr,uint8 *RStack,uint8 *Stack ,void (*exec)())
{ 
  *(HARDWARE_STACK_ADDRESS+(++RStack))=(uint16)exec;
  *(HARDWARE_STACK_ADDRESS+(++RStack))=(uint16)exec>>8;
  
  *(--Stack) = 0x80;             //IEN0 register
  Stack     -= 1+2;             //A+DPTR
  *(--Stack) = (uint8)(RStack); //SP  
  Stack-=1+8+VREG_COUNT+1+1;    //PSW+REG+VREG_COUNT+VB+B
  
  uint8 _spxl=(uint16)(Stack-2);
  uint8 _spxh=((uint16)(Stack-2)>>8);
     
  *(--Stack) = _spxl;   //SPX
  *(--Stack) = _spxh;
  
  layerContext[gr].context=Stack;
}


Написано кривовато, но побую.

Также видим, что передается управление не процессу а диспетчеру. Так автоматически будет занесен в стек возврата адрес кооперативного диспетчера. Помним, что в кооперативках задачи могут завершаться.

В main-е меняем функцию запуска системы

//os_start
  osal_start();


Теперь эта функция находится в ассемблерном файле. Без асма тут не обойтись, ведь будем свапать контексты.

Теперь делаем свою версию деспетчера

uint8 idx = 0;

  osalTimeUpdate();
  Hal_ProcessPoll();

  do {
    if (tasksEvents[idx])  // Task is highest priority that is ready.
    {
      break;
    }
  } while (++idx < tasksCnt);

  if (idx < tasksCnt)
  {
    uint16 events;
    halIntState_t intState;
        
    if(processList[idx]!=activeLayer)
    {
      uint8 oldLayer;   
      
      HAL_ENTER_CRITICAL_SECTION(intState);
      
      oldLayer=activeLayer;
      activeLayer=processList[idx];
      
      //switch context
      os_context_switcher(layerContext[oldLayer].context, layerContext[activeLayer].context);
      
      HAL_EXIT_CRITICAL_SECTION(intState);
    }
    else
    {
        HAL_ENTER_CRITICAL_SECTION(intState);
        events = tasksEvents[idx];
        tasksEvents[idx] = 0;  // Clear the Events for this task.
        HAL_EXIT_CRITICAL_SECTION(intState);
    
        activeTaskID = idx;
        events = (tasksArr[idx])( idx, events );
        activeTaskID = TASK_NO_TASK;
    
        HAL_ENTER_CRITICAL_SECTION(intState);
        tasksEvents[idx] |= events;  // Add back unprocessed events to the current task.
        HAL_EXIT_CRITICAL_SECTION(intState);
    }
    
  }


Я добавил, только момент с переключением контекстов уровней. Как видим, ничего сложного нет.

А теперь запиливаем самое вкусное. Ради нее родимой все и затевалось.

/*********************************************************************
 * @fn      task_yield
 *
 * @brief     
 *
 * @param   task_id - current task ID
 *
 *   return 
 *   YIELD_CONTEXT_RETURN 
 *   YIELD_CONTROL_RETURN 
 *   YIELD_RECURSION
 *   YIELD_RETURN
 *   YIELD_NO_TASK
 *
 * @
 */
uint8 task_yield(uint8 task_id)
{
  static uint8 recursion=0;
  uint8 idx = 0;
  
   osalTimeUpdate();
   Hal_ProcessPoll();
  
  do {
    if (tasksEvents[idx])  // Task is highest priority that is ready.
    {
      break;
    }
  } while (++idx < tasksCnt);
  
  
  if (idx < tasksCnt)
  {
     uint16 events;
     halIntState_t intState;
    
     if(processList[idx]!=activeLayer)
     {
        uint8 oldLayer;   
      
        HAL_ENTER_CRITICAL_SECTION(intState);
        
        oldLayer=activeLayer;
        activeLayer=processList[idx];
        
        //switch context
        os_context_switcher(layerContext[oldLayer].context, layerContext[activeLayer].context);
        
        HAL_EXIT_CRITICAL_SECTION(intState);
        
        return YIELD_CONTEXT_RETURN; 
     }
     else
     {
       if(idx!=task_id)
       {
            if(recursion==0)
            {
              HAL_ENTER_CRITICAL_SECTION(intState);
              events = tasksEvents[idx];
              tasksEvents[idx] = 0;  // Clear the Events for this task.
              
              recursion=1;
              HAL_EXIT_CRITICAL_SECTION(intState);
          
              activeTaskID = idx;
              events = (tasksArr[idx])( idx, events );
              activeTaskID = TASK_NO_TASK;
          
              HAL_ENTER_CRITICAL_SECTION(intState);
              recursion=0;
              tasksEvents[idx] |= events;  // Add back unprocessed events to the current task.
              HAL_EXIT_CRITICAL_SECTION(intState);
              
              return YIELD_CONTROL_RETURN;
            }
            else
            {
              return YIELD_RECURSION;
            }
       }
       else
       {
         return YIELD_RETURN;
       }
     }
  }
  
  
  return YIELD_NO_TASK;
}


Опять же, все очень просто. Если нужно свапаем контексты, иначе передаем управление другому процессу рекурсивно с защитой от зацикливания.
Переменная recursion должна быть статичной а не глобальной, чтоб у каждого уровня была своя локальная защита от зацикливания.

Отработав функция возвращает причину возврата.

И теперь мы можем реализовать sleep вот так. Раньше это было невозможно.

// Send a message out - This event is generated by a timer
  //  (setup in GenericApp_Init()).
  if ( events & GENERICAPP_SEND_MSG_EVT )
  {
    events = events ^ GENERICAPP_SEND_MSG_EVT;
    
    // Send "the" message
    GenericApp_SendTheMessage();

    // Setup to send message again
    osal_start_timerEx( GenericApp_TaskID,
                        GENERICAPP_SEND_MSG_EVT,
                        GENERICAPP_SEND_MSG_TIMEOUT );
    
    
    //delay
    do{
        task_yield(task_id);
        events=update_task_events( task_id,  events );
    }while(!(events&GENERICAPP_SEND_MSG_EVT));
      

    // return unprocessed events
    return (events ^ GENERICAPP_SEND_MSG_EVT);
  }


Итого

OSAL так и осталось УГ. Но теперь процессы можно прерывать. А значит можно по сносить нахрен конечные автоматы.

Стандартный пример с патчем. Это архив 7z! Здесь закачка кривая, так что меняем расширение.
  • +1
  • 26 ноября 2012, 07:54
  • a9d
  • 1
Файлы в топике: zstack1.7z.zip

Комментарии (18)

RSS свернуть / развернуть
… у нас в конторе тоже использовали этот самый OSAL и тоже для RF (но не ЗигБи, свой протокол). Я особо не вникал почему и как она работает, не моя задача была, но после ваших дифирамб спрошу. Из гугла я так понял что она предоставляет некий уровень абстракции от ОС, ну это и из названия понятно (OS Abstraction Layer) :), или это не она?
0
The OS Abstraction Layer (OSAL) project is a small software library that isolates embedded software from the real time operating system. The OSAL provides an Application Program Interface (API) to an abstract real time operating system.
… скорей всего это просто одноименная либа
0
As described in the Z-Stack OSAL API (SWRA194) document, the OSAL implements a cooperative, round-robin
task servicing loop. Each major sub-system of the Z-Stackruns as an OSAL Task. The user must create at least
one OSAL Task in which their application will run. Thisis accomplished by adding their task to the task array
[tasksArr defined in OSAL_”Application Name”.c] and calling their application’s task initialization function in
osalInitTask(). The sample applications clearly showhow the user adds a task to the OSAL system.

Z-Stack идет как процесс. А ZBOSS идет как библиотека.
0
В Ti соми толком не знают, что это. Ее называют, то кооперативной системой, то уровнем абстракции. Оба эти определения одной и той же системы можно встретить в документации. Тоже самое и на ихнем форуме. Они не знают как ее материть…

Аналогичная ситуация у них и с ZED и ZEP иногда они под конечной точкой подразумевают конечное устройство.
0
из описания вроде как полезная вещь. Может вам достался осал прикрученный к какой-нибудь кривой оси?
0
это другой проект с таким-же названием
0
… уж больно похожи по описанию. Z-Stack OSAL API
OS Abstraction Layer (OSAL) API allows the software components in the Z-stack to be written independently of the specifics of the operating system, kernel or tasking environment (including control loops or connect-to-interrupt systems).
… может быть в ТИ взяли и скрестили Z-Stack и OSAL, а дальше кто какую ось предпочитает?
0
если OSAL не ОС тогда зачем ей нужен диспетчер и функции меж процессорного обмена? Если каждый может использовать другую ОС, то почему они не смогли запустить свое детище под freeRTOS. Забавно еще, то что им не удалось портировать freeRTOS на IAR.
0
… насчет диспечера, то как-то ось же надо абстрагировать. Насчет FreeRTOS, то к примеру в нашей конторе ее не используют, поскольку относительно много памяти кушает, а процы у нас средние и без всяких ОСей памяти под завязку, да и заказчик строго просит определенную ось.
0
в любом случае есть расшифровка
OSAL — Operating System (OS) Abstraction Layer
0
… здесь вы правы, под осал все равно должна быть ось
0
… кстати небольшая статья про преимущества использования OSAL
0
И какое это имеет отношение к OSAL от Ti?
0
… к ТИ, никакого
0
Прочел Ваши отзывы об OSAL, руки опускаются осваивать сс2540 =)
0
мое личное мнение не является для кого либо оправданием.
0
у меня товарищ ставил Contiki на какойто трансивер. вот только не помню на какой. Вроде, CCxxxx был.
0
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.