Алгоритм упреждающего планирования - PullRequest
0 голосов
/ 24 декабря 2018

Вот небольшой превентивный алгоритм планирования для MSP430G2553 Launchpad от @ tonyp12 от https://forum.43oh.com/topic/9450-tiny-msp430-preemptive-multitasking-system/

Несмотря на то, что я знаю концепцию переключения контекста, эта программа немного смущает меня.Может кто-нибудь объяснить, что он делает, шаг за шагом, особенно в следующих фрагментах?

int* multistack = (int*) __get_SP_register();
  int i=0; while(i<tasks-1){
    int j = stacksize[i]; if (!j) j = 24;
    multistack -= j;  
    *(multistack) = (int) taskpnt[++i];         // prefill in PC 
    *(multistack-1) = GIE;                      // prefill in SR
    taskstackpnt[i] = (int) multistack-26;      // needs 12 dummy push words
  } 

и

#pragma vector = WDT_VECTOR 
__raw __interrupt void taskswitcher(void)
{
  asm ("push R15\n push R14\n push R13\n push R12\n"
       "push R11\n push R10\n push R9\n push R8\n"
       "push R7\n push R6\n push R5\n push R4");

  taskstackpnt[taskrun] = __get_SP_register();
  if (++taskrun == tasks) taskrun = 0;
  __set_SP_register(taskstackpnt[taskrun]);
  
  asm ("pop R4\n pop R5\n pop R6\n pop R7\n"
       "pop R8\n pop R9\n pop R10\n pop R11\n"
       "pop R12\n pop R13\n pop R14\n pop R15");
}

Спасибо.И вот полный код:

#include "msp430.h"
#include "common.h"

//=========================(C) Tony Philipsson 2016 =======================
funcpnt const taskpnt[]={ task1, task2, task3,  // <- PUT YOUR TASKS HERE
}; 
const int stacksize[tasks] = {28};              // a blank value defaults to 24 stack words
//=========================================================================
int taskstackpnt[tasks];
unsigned int taskdelay[tasks];
char taskrun;

int main( void )
{
  WDTCTL = WDTPW + WDTHOLD;                     // Stop watchdog timer 
  if (CALBC1_8MHZ != 0xff){               // erased by mistake?
    BCSCTL1 = CALBC1_8MHZ;                   // Set DCO to factory calibrate 1MHz  
    DCOCTL = CALDCO_8MHZ;
  } 
  int* multistack = (int*) __get_SP_register();
  int i=0; while(i<tasks-1){
    int j = stacksize[i]; if (!j) j = 24;
    multistack -= j;  
    *(multistack) = (int) taskpnt[++i];         // prefill in PC 
    *(multistack-1) = GIE;                      // prefill in SR
    taskstackpnt[i] = (int) multistack-26;      // needs 12 dummy push words
  }
  WDTCTL = WDTPW+WDTTMSEL+WDTCNTCL;             // 4ms interval at 8MHz smclk
  IE1 |= WDTIE;
  __bis_SR_register(GIE);
  asm ("br &taskpnt");                          // indirect jmp to first task
}

//============= TASK SWITCHER ISR =============
#pragma vector = WDT_VECTOR 
__raw __interrupt void taskswitcher(void)
{
  asm ("push R15\n push R14\n push R13\n push R12\n"
       "push R11\n push R10\n push R9\n push R8\n"
       "push R7\n push R6\n push R5\n push R4");

  taskstackpnt[taskrun] = __get_SP_register();
  if (++taskrun == tasks) taskrun = 0;
  __set_SP_register(taskstackpnt[taskrun]);

  asm ("pop R4\n pop R5\n pop R6\n pop R7\n"
       "pop R8\n pop R9\n pop R10\n pop R11\n"
       "pop R12\n pop R13\n pop R14\n pop R15");
} 
#include "msp430.h"
#include "common.h"

__task void task1(void){
  P1DIR |= BIT0;
  while(1){
    __delay_cycles(800000);
    P1OUT |= BIT0;
    __delay_cycles(800000);
    P1OUT &=~BIT0;        
  }
}
#include "msp430.h"
#include "common.h"

__task void task2(void){
  P1DIR |= BIT6;
  while(1){
    __delay_cycles(1200000);
    P1OUT |= BIT6;
    __delay_cycles(1200000);
    P1OUT &=~BIT6;        
  }
}
#include "msp430.h"
#include "common.h"
unsigned int fibo(int);

__task void task3(void){
  int temp = 0;
  while(1){
    fibo(++temp);
  }
}
unsigned int fibo(int n){
  if (n < 2)
    return n;
  else
    return (fibo(n-1) + fibo(n-2));
}
#ifndef COMMON_H_
#define COMMON_H_
#define  tasks (sizeof(taskpnt)/2)
__task void task1(void);
__task void task2(void);
__task void task3(void);
typedef __task void (*funcpnt)(void);
#endif

1 Ответ

0 голосов
/ 21 февраля 2019

Первый фрагмент кода инициализирует все стеки для различных задач.

Сначала сохраняем ПК как адрес функции задачи (первая инструкция):

*(multistack) = (int) taskpnt[++i]; 

затем он сохраняет регистр состояния с включенным GIE (необходим для правильной функции переключения задач):

*(multistack-1) = GIE;   

Эти два будут автоматически восстановлены reti, когда прерывание планировщика закончится.

Также сохраняется новый указатель стека для задачи (включая пространство для сохранения реестров):

taskstackpnt[i] = (int) multistack-26;

Второй фрагмент - это само прерывание планировщика.

ПК и SR автоматически сохраняются во время вызова прерывания.В коде прерывания реестр сохраняется для текущей задачи:

asm ("push R15\n push R14\n push R13\n push R12\n"
   "push R11\n push R10\n push R9\n push R8\n"
   "push R7\n push R6\n push R5\n push R4");

, затем программа сохраняет указатель стека для текущей задачи:

taskstackpnt[taskrun] = __get_SP_register();

и получает индекс указателя стека следующей задачи.:

if (++taskrun == tasks) taskrun = 0;

затем восстановите новый указатель стека задач:

__set_SP_register(taskstackpnt[taskrun]);

и извлеките реестры, сохраненные в стеке:

 asm ("pop R4\n pop R5\n pop R6\n pop R7\n"
   "pop R8\n pop R9\n pop R10\n pop R11\n"
   "pop R12\n pop R13\n pop R14\n pop R15");

ПК и SR дляновое задание восстанавливается с помощью reti прерывания.

Новое задание готово к работе!

...