cpt1020
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Lab 2 <!-- {%hackmd hackmd-dark-theme %} --> > 國立成功大學 資訊工程學系 嵌入式作業系統分析與實作 Analysis and Implementation of Embedded Operating Systems [CSIE7618] 2022 Spring > GitHub: https://github.com/cpt1020/EmbeddedOS-Lab2 ## Objective 瞭解FreeRTOS的ready list和delayed list是用什麼data structure去實作的 ## Requirement :::info - Create four task - Red_LED_App, Green_LED_App, Delay_App, TaskMonitor_App - TaskMonitor_App will call `Taskmonitor()` periodicity - `TaskMonitor()` - Traverse ReadyTaskList, DelayedTaskList, OverflowDelayedTaskList - Print TCB information by UART - Task Name、Priority(Base/actual)、Stack Pointer、TopOfStack Pointer、 Task State [Demo Video](https://ncku365-my.sharepoint.com/:v:/g/personal/p76104702_ncku_edu_tw/EZ4hmAYudm9EoMgBhwTkWy4BS7zeZbdtmw_t8rAySqzwUQ?e=7RvzLL) ![image](https://hackmd.io/_uploads/rkIdfmRQa.png) ::: ## Prerequisites and Configuration Setup - Project前置設定可參考這篇 https://hackmd.io/@cpt/embeddedOS_lab0 - USART設定可參考這篇 https://hackmd.io/@cpt/embeddedOS_USART ### Pin Configuration in STM32CubeIDE ![image](https://hackmd.io/_uploads/SyR5Mg6E6.png) 記得要設置USART2和紅綠LED ## Add New Code 在 `main.c` 新增以下4個助教給的tasks ```cpp! void TaskMonitor_App(void *pvParameters){ for(;;){ Taskmonitor(); vTaskDelay(1000); } } void Red_LED_App(void *pvParameters){ uint32_t Redtimer = 800; for(;;){ HAL_GPIO_TogglePin(GPIOD,Red_LED_Pin); vTaskDelay(Redtimer); Redtimer+=1; } } void Green_LED_App(void *pvParameters){ uint32_t Greentimer = 1000; for(;;){ HAL_GPIO_TogglePin(GPIOD,Green_LED_Pin); vTaskDelay(Greentimer); Greentimer+=2; } } void Delay_App(void *pvParameters){ int delayflag=0; uint32_t delaytime; while(1){ if(delayflag==0){ delaytime = 1000; delayflag=1; }else{ delaytime=0xFFFFFFFF; } vTaskDelay(delaytime); } } ``` 在 `task.h` 新增以下code: ```cpp! #include "stm32f4xx_hal.h" extern UART_HandleTypeDef huart2; // for USART2 void Taskmonitor(void); ``` - `UART_HandleTypeDef huart2` 這裡宣告成 `extern` 是因為,在 `.ioc` 設定完USART之後generate code,在 `main.c` 的 `Private variables` 內就會自動產生宣告 `UART_HandleTypeDef huart2;` 的code,所以 `task.h` 的要宣告成 `extern` ,否則編譯時會出現multiple definition的錯誤訊息 在 `tasks.c` 最後面新增助教給的 `TaskMonitor()` 的template,這次lab主要就是要寫 `TaskMonitor()` ```cpp void Taskmonitor(void) { /* Initialize string */ char Monitor_data[130]; memset(Monitor_data,'\0',sizeof(Monitor_data)); /* Stop scheduler */ /* Taskmonitor() will block when UART is transmitting data */ /* Scheduler will change list data when Taskmonitor() is blocked */ vTaskSuspendAll(); /* Print title */ sprintf(Monitor_data,"|Name |Priority(Base/actual) |pxStack |pxTopOfStack |State |\n\r"); HAL_UART_Transmit(&huart2,(uint8_t *)Monitor_data,strlen(Monitor_data),0xffff); /* pxReadyTasksLists */ /* pxDelayedTaskList*/ /* pxOverflowDelayedTaskList */ /* Resume scheduler */ xTaskResumeAll(); } ``` 在 `list.h` 新增以下macro (這個macro滿好用的,後面會用到): ```cpp! #define listGET_ITEM_OF_HEAD_ENTRY(pxList) ((&((pxList)->xListEnd))->pxNext) ``` 在 `FreeRTOSConfig.h` 修改以下此macro: ```cpp! configMAX_PRIORITIES 15 ``` ## FreeRTOS需理解的部分 在 `tasks.c` 可看到以下內容: ```cpp /* Lists for ready and blocked tasks. -------------------- xDelayedTaskList1 and xDelayedTaskList2 could be move to function scople but doing so breaks some kernel aware debuggers and debuggers that rely on removing the static qualifier. */ PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*< Prioritised ready tasks. */ PRIVILEGED_DATA static List_t xDelayedTaskList1; /*< Delayed tasks. */ PRIVILEGED_DATA static List_t xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; /*< Points to the delayed task list currently being used. */ PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ PRIVILEGED_DATA static List_t xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ ``` - 我們需要存取的是: - `pxReadyTasksLists` - `pxDelayedTaskList` - `pxOverflowDelayedTaskList` - 這幾個list都被宣告為 `static` ,我們無法在別的 `.c` 裡面存取這些list,所以我們的 `TaskMonitor()` 必須要在 `task.c` 裡面才可以存取這幾個list 此外,在 `tasks.c` 可看到 `tskTaskControlBlock` 的相關定義: ```cpp /* * Task control block. A task control block (TCB) is allocated for each task, * and stores task state information, including a pointer to the task's context * (the task's run time environment, including register values) */ typedef struct tskTaskControlBlock /* The old naming convention is used to prevent breaking kernel aware debuggers. */ { /* 以下僅列出需要印出來的data member */ volatile StackType_t *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ UBaseType_t uxPriority; /*< The priority of the task. 0 is the lowest priority. */ StackType_t *pxStack; /*< Points to the start of the stack. */ char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ #if ( configUSE_MUTEXES == 1 ) UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */ #endif } tskTCB; /* The old tskTCB name is maintained above then typedefed to the new TCB_t name below to enable the use of older kernel aware debuggers. */ typedef tskTCB TCB_t; ``` 在 `list.h` 中需要弄懂以下三個struct: ```cpp /* * Definition of the only type of object that a list can contain. */ struct xLIST; struct xLIST_ITEM { listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */ struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */ struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */ void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */ struct xLIST * configLIST_VOLATILE pxContainer; /*< Pointer to the list in which this list item is placed (if any). */ listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ }; typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */ struct xMINI_LIST_ITEM { listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ configLIST_VOLATILE TickType_t xItemValue; struct xLIST_ITEM * configLIST_VOLATILE pxNext; struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; }; typedef struct xMINI_LIST_ITEM MiniListItem_t; /* * Definition of the type of queue used by the scheduler. */ typedef struct xLIST { listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ volatile UBaseType_t uxNumberOfItems; ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */ MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */ listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ } List_t; ``` 在 `list.h` 中可能會有幫助的macro: ```cpp /* * Access macro to determine if a list contains any items. The macro will * only have the value true if the list is empty. * * \page listLIST_IS_EMPTY listLIST_IS_EMPTY * \ingroup LinkedList */ #define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE ) /* * Access macro to return the number of items in the list. */ #define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems ) /* * Access function to obtain the owner of the first entry in a list. Lists * are normally sorted in ascending item value order. * * This function returns the pxOwner member of the first item in the list. * The pxOwner parameter of a list item is a pointer to the object that owns * the list item. In the scheduler this is normally a task control block. * The pxOwner parameter effectively creates a two way link between the list * item and its owner. * * @param pxList The list from which the owner of the head item is to be * returned. * * \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY * \ingroup LinkedList */ #define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner ) ``` <!-- `tasks.c` ```cpp=252 /* * Place the task represented by pxTCB into the appropriate ready list for * the task. It is inserted at the end of the list. */ #define prvAddTaskToReadyList( pxTCB ) \ traceMOVED_TASK_TO_READY_STATE( pxTCB ); \ taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \ vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \ tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ) /*-----------------------------------------------------------*/ ``` ```cpp=3584 static void prvInitialiseTaskLists( void ) { UBaseType_t uxPriority; for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ ) { vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) ); } vListInitialise( &xDelayedTaskList1 ); vListInitialise( &xDelayedTaskList2 ); vListInitialise( &xPendingReadyList ); #if ( INCLUDE_vTaskDelete == 1 ) { vListInitialise( &xTasksWaitingTermination ); } #endif /* INCLUDE_vTaskDelete */ #if ( INCLUDE_vTaskSuspend == 1 ) { vListInitialise( &xSuspendedTaskList ); } #endif /* INCLUDE_vTaskSuspend */ /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList using list2. */ pxDelayedTaskList = &xDelayedTaskList1; pxOverflowDelayedTaskList = &xDelayedTaskList2; } ``` --> :::info `TCB_t` 的data members,有 `UBaseType_t` 和指向 `StackType_t` 的pointer,以下補充他們是什麼: 在 `FreeRTOS/portable/ARM_CM4F/portmacro.h` 內(找好久才找到原來是定義在這@_@),可看到關於 `StackType_t` 、`BaseType_t` 、 `UBaseType_t` 、以及 `TickType_t` 的相關定義: ```cpp=36 /*----------------------------------------------------------- * Port specific definitions. * * The settings in this file configure FreeRTOS correctly for the * given hardware and compiler. * * These settings should not be altered. *----------------------------------------------------------- */ /* Type definitions. */ #define portCHAR char #define portFLOAT float #define portDOUBLE double #define portLONG long #define portSHORT short #define portSTACK_TYPE uint32_t #define portBASE_TYPE long typedef portSTACK_TYPE StackType_t; typedef long BaseType_t; typedef unsigned long UBaseType_t; #if( configUSE_16_BIT_TICKS == 1 ) typedef uint16_t TickType_t; #define portMAX_DELAY ( TickType_t ) 0xffff #else typedef uint32_t TickType_t; #define portMAX_DELAY ( TickType_t ) 0xffffffffUL /* 32-bit tick type on a 32-bit architecture, so reads of the tick count do not need to be guarded with a critical section. */ #define portTICK_TYPE_IS_ATOMIC 1 #endif /*-----------------------------------------------------------*/ ``` 可以看到 `StackType_t` 其實是 `uint32_t` 是unsigned integer;`BaseType_t` 是 `long`;`UBaseType_t` 是 `unsigned long` FreeRTOS的官方網站也有這幾個type的說明(https://www.freertos.org/FreeRTOS-Coding-Standard-and-Style-Guide.html#DataTypes) ::: ## Code 這份作業最麻煩的大概就是要弄清楚ready list、delayed list、overflow delayed list的data structure,以及他們是怎麼串起來的(後面會再介紹),弄清楚後其實就只是在做linked list的題目 :::info 一般我們寫C程式若用 `printf` 印出pointer的value,我們都是用 `%p` 這個format specifier來指定我們要印出來的東西是address,像是 `printf("%p", ptr)` ,這樣印出來的結果就會是16進位。 這份lab需要將TCB的 `pxStack` 和 `pxTopOfStack` 這兩個pointer的值印出來,那由於我們是用 `HAL_UART_Transmit` 把資訊輸出,我們必須先把他們的值放到char array,才能用 `HAL_UART_Transmit`。但我們若取得pointer的值,直接放到char array,這樣那個pointer的值會是以10進位表示,這樣就不符合這個lab的要求。 這時可以用 `itoa` 直接指定用16進位而很方便地將值轉換成16進位表示。 :::warning `itoa` - 此function可將整數轉換為char array - 此function通常並非 C 語言標準函式,因此其可用性可能取決於使用的編譯器。有些編譯器可能提供 itoa 函數,而有些則不提供。若是有提供的則需 `#include <stdlib>` Function prototype ```cpp char *itoa(int value, char *str, int base); ``` 參數: - `value`: 要轉換的整數值 - `str`: A pointer to the character array (string) where the result will be stored. - `base`: 表示進制的整數值,例如 10 表示十進制,16 表示十六進制等 - Return Value: Returns a pointer to the resulting string. ::: ```cpp // 看要補多少個空格到char array以用來對齊 void vPadding(char string [], int padNum) { for (int i = 0; i < padNum; ++i) { strcat(string, " "); } } // 印出助教所要求的TCB資訊 void vPrintTCB(tskTCB *tcb, char state []) { // 以下是處理task name的部分 char taskName [12]; memset(taskName, '\0', sizeof(taskName)); strcat(taskName, tcb->pcTaskName); vPadding(taskName, 10 - strlen(taskName)); // 以下是處理task priority的部分 char taskPriority [24]; memset(taskPriority, '\0', sizeof(taskPriority)); char basePriority [3]; memset(basePriority, '\0', sizeof(basePriority)); itoa(tcb->uxBasePriority, basePriority, 10); char priority [3]; memset(priority, '\0', sizeof(priority)); itoa(tcb->uxPriority, priority, 10); vPadding(taskPriority, 5); strcat(taskPriority, basePriority); strcat(taskPriority, (strlen(basePriority) == 1) ? " /" : "/"); strcat(taskPriority, priority); vPadding(taskPriority, (strlen(priority) == 1) ? 14 : 13); // 以下是處理pxStack的部分 char task_pxStack [12]; memset(task_pxStack, '\0', sizeof(task_pxStack)); char tmp [10]; itoa( (size_t) tcb->pxStack, tmp, 16); strcat(task_pxStack, "0x"); strcat(task_pxStack, tmp); vPadding(task_pxStack, 11 - strlen(task_pxStack)); // 以下是處理pxTopOfStack的部分 char task_pxTopOfStack [17]; memset(task_pxTopOfStack, '\0', sizeof(task_pxTopOfStack)); memset(tmp, '\0', sizeof(tmp)); itoa( (size_t) tcb->pxTopOfStack, tmp, 16); strcat(task_pxTopOfStack, "0x"); strcat(task_pxTopOfStack, tmp); vPadding(task_pxTopOfStack, 16 - strlen(task_pxTopOfStack)); // 以下是處理task state的部分 char taskState [9]; memset(taskState, '\0', sizeof(taskState)); strcpy(taskState, state); // 把前面處理的資訊都strcat到msg裡面 char msg [100]; memset(msg, '\0', sizeof(msg)); vPadding(msg, 1); strcat(msg, taskName); vPadding(msg, 1); strcat(msg, taskPriority); vPadding(msg, 1); strcat(msg, task_pxStack); vPadding(msg, 1); strcat(msg, task_pxTopOfStack); vPadding(msg, 1); strcat(msg, taskState); strcat(msg, "\n\r"); // 將msg用HAL_UART_Transmit印出來 HAL_UART_Transmit(&huart2, (uint8_t *) msg, strlen(msg), 0xffff); } void Taskmonitor(void) { /* Initialize string */ char Monitor_data[130]; memset(Monitor_data,'\0',sizeof(Monitor_data)); /* Stop scheduler */ /* Taskmonitor() will block when UART is transmitting data */ /* Scheduler will change list data when Taskmonitor() is blocked */ vTaskSuspendAll(); /* Print title */ sprintf(Monitor_data,"|Name |Priority(Base/actual) |pxStack |pxTopOfStack |State |\n\r"); HAL_UART_Transmit(&huart2, (uint8_t *) Monitor_data, strlen(Monitor_data), 0xffff); /* pxReadyTasksLists */ for (UBaseType_t priority = 0; priority < configMAX_PRIORITIES; ++priority) { // 前面有設定過configMAX_PRIORITIES是15,所以priority範圍是0~14 // 從TCB的struct可以看到priority的data type是UBaseType_t,所以才宣告其data type為UBaseType_t (但也可以宣告為int啦) UBaseType_t readyListLength = listCURRENT_LIST_LENGTH(&pxReadyTasksLists[priority]); // 取得目前這個priority的ready list裡面有多少個item if (readyListLength != 0) { ListItem_t *curNode = listGET_ITEM_OF_HEAD_ENTRY(&pxReadyTasksLists[priority]); // 取得此priority的ready list的第一個item for (UBaseType_t i = 0; i < readyListLength; ++i) { vPrintTCB(curNode->pvOwner, "Ready"); // 將此item的TCB傳到vPrintTCB curNode = curNode->pxNext; // 換到下一個item } } } /* pxDelayedTaskList*/ UBaseType_t delayedListLength = listCURRENT_LIST_LENGTH(pxDelayedTaskList); // 取得delayed list有多少個item if (delayedListLength != 0) { ListItem_t *curNode = listGET_ITEM_OF_HEAD_ENTRY(pxDelayedTaskList); // 取得delayed list的第一個item for (UBaseType_t i = 0; i < delayedListLength; ++i) { vPrintTCB(curNode->pvOwner, "Blocked"); // 將此item的TCB傳到vPrintTCB curNode = curNode->pxNext; // 換到下一個item } } /* pxOverflowDelayedTaskList */ UBaseType_t overflowListLength = listCURRENT_LIST_LENGTH(pxOverflowDelayedTaskList); // 取得overflow delayed list有多少個item if (overflowListLength != 0) { ListItem_t *curNode = listGET_ITEM_OF_HEAD_ENTRY(pxOverflowDelayedTaskList); // 取得overflow delayed list的第一個item for (UBaseType_t i = 0; i < overflowListLength; ++i) { vPrintTCB(curNode->pvOwner, "Overflow"); // 將此item的TCB傳到vPrintTCB curNode = curNode->pxNext; // 換到下一個item } } /* Resume scheduler */ xTaskResumeAll(); } ``` ## Result ![ezgif-5-0d2442db2b](https://hackmd.io/_uploads/ryiEzmCXa.gif) ## Ready List, Delayed List, & Overflow Delayed List of FreeRTOS ```cpp // tasks.c PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ]; PRIVILEGED_DATA static List_t xDelayedTaskList1; PRIVILEGED_DATA static List_t xDelayedTaskList2; PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; ``` 前面有提到,在 `tasks.c` 有這幾個global variable,可以看到 `pxReadyTasksLists` 被宣告成array;那關於 `xDelayedTaskList1` 和 `xDelayedTaskList2` 是什麼?可以另外在 `task.c` 看到 `prvInitialiseTaskLists` 這個function: ```cpp static void prvInitialiseTaskLists( void ) { UBaseType_t uxPriority; for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ ) { vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) ); } vListInitialise( &xDelayedTaskList1 ); vListInitialise( &xDelayedTaskList2 ); vListInitialise( &xPendingReadyList ); #if ( INCLUDE_vTaskDelete == 1 ) { vListInitialise( &xTasksWaitingTermination ); } #endif /* INCLUDE_vTaskDelete */ #if ( INCLUDE_vTaskSuspend == 1 ) { vListInitialise( &xSuspendedTaskList ); } #endif /* INCLUDE_vTaskSuspend */ /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList using list2. */ pxDelayedTaskList = &xDelayedTaskList1; pxOverflowDelayedTaskList = &xDelayedTaskList2; } ``` 所以可以看到當 `xDelayedTaskList1` 和 `xDelayedTaskList2` 被初始化之後,`pxDelayedTaskList` 和 `pxOverflowDelayedTaskList` 這兩個pointer就會分別去指向 `xDelayedTaskList1` 和 `xDelayedTaskList2`。 以下是各個list怎麼把他的item串起來的簡單示意圖: ![EmbeddedOS](https://hackmd.io/_uploads/Bkpx4KR76.png) - `pxReadyTasksLists` 是一個 `List_t` 這個struct的array - `pxReadyTasksLists[0]` - 第一個element就是priority 0的 `List_t`。這個 `List_t` 會指向所有priority為0的task - task在這裡是用 `ListItem_t` 來表達,而 `ListItem_t` 的 `pvOwner` 就會指向該task的TCB - `pxReadyTasksLists[1]` - 第二個elemnt就是priority 1的 `List_t`,此 `List_t` 會指向priority為1的task - 後面依此類推 - `pxDelayedTaskList` 和 `pxOverflowDelayedTaskList` 就不是array,他們自己就是一個 `List_t` ,並且各自指向目前是delayed/overflow delayed的tasks - task在這裡也是用`ListItem_t` 來表達,且 `ListItem_t` 的 `pvOwner` 也會指向該task的TCB ![EmbeddedOS](https://hackmd.io/_uploads/rJ718myNa.png) - 前一張圖只是簡單的示意圖,這一張是更詳細的說明 `List_t` 和 `ListItme_t` 之間的關係 - `List_t` 的 `uxNumberOfItems` 會記錄這個List有多少 `ListItem_t` ,每當有新的 `ListItem_t` 被插入,他的值就會 +1 - `List_t` 的 `pxIndex` 是指向 `ListItem_t` 的pointer。此pointer在 `List_t` 被初始化時就會指向 `xListEnd` ,除非有使用 `listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )` 這個macro,否則我看程式碼他應該都是指向 `xListEnd` - `pxIndex` 的主要的目的是要搭配 `listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )` 來walk through每個item - `List_t` 的 `xListEnd` 基本上是拿來當作這個List的最後一個item - `xListEnd` 的struct是 `MiniListItem_t` ,他的data member跟 `ListItem_t` 有點像,但比較少,所以這裡選擇用 `MiniListItem_t` 是為了可以減少記憶體空間的使用 - 在 `List_t` 被初始化時, `xListEnd` 的 `xItemValue` 就會被初始化成 `portMAX_DELAY`,這樣才能確保 `xListEnd` 的value是該list所有item中最大的 - 再來,可以看到 `ListItem_t` 之間是用doubly linked list串在一起 - 所以最後一個 `ListItem_t` 的 `pxNext` 會指向 `xListEnd`,而 `xListEnd` 的 `pxPrevious` 會指向最後一個 `ListItem_t`,這樣才能確保doubly linked list的完整性 - 第一個 `ListItem_t` 的 `pxPrevous` 會指向 `xListEnd`,而 `xListEnd` 的 `pxNext` 會指向第一個 `ListItem_t` - 在 `tasks.c` 的 `prvInitialiseNewTask()` 內可以看到, `ListItem_t` 的`xItemValue` 會被初始化成 `( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority` - `ListItem_t` 的 `pvContainer` 是用來指向說我這個Item目前是在哪個List裡面,若是在 `pxReadyTasksLists[0]`,就會指向 `pxReadyTasksLists[0]` ![EmbeddedOS](https://hackmd.io/_uploads/HJRfqrk46.png) - 來看 `List_t` 是怎麼被初始化的~ - 可以看到 `pxIndex` 會指向 `xListEnd` - `xListEnd` 的 `xItemValue` 被設定為 `portMAX_DELAY`,這樣才能確保 `xListEnd` 的value是該list所有item中最大的(這個值的大小跟放到delayed list有關) - `xListEnd` 的 `pxNext` 和 `pxPrevious` 都指向 `xListEnd`,我們也能因此知道這個 `List_t` 什麼時候是空的 - `uxNumberOfItems` 初始化成0 :::info - 一個 `ListItem_t` 要被放到ready list的話,會藉由呼叫 `prvAddTaskToReadyList( pxTCB )` (在 `tasks.c`)這個macro,此macro會呼叫 `vListInsertEnd` (在 `list.c`)這個function把他放到該priority的ready list的最後一個 - 若是要放到delayed list,則會呼叫 `ListInsert` (在 `list.c`),此function就會依照 `xItemValue` 的值決定插入的位子 ::: 以下做了簡單的圖示說明 `vListInsertEnd` 怎麼作用的: ![vListInsertEnd1](https://hackmd.io/_uploads/ByTh8I1Np.png) ![vListInsertEnd2](https://hackmd.io/_uploads/HJNpU8J46.png) 1. 將 `pxNewListItem` 的 `pxNext` 指向 `pxIndex` 所指向的對象,也就是 `xListEnd` 2. 將 `pxNewListItem` 的 `pxPreivous` 指向 `pxIndex` 所指向的對象的 `pxPrevious`,也就是 `xListEnd` 的 `pxPrevious` 所指向的對象,也就是 `xListEnd` 自己 3. `pxIndex` 所指向的對象(即`xListEnd`)的 `pxPrevious` 所指向的對象(此時仍是`xListEnd`)的 `pxNext` 指向 `pxNewListItem` 4. `xListEnd` 的 `pxPrevious` 指向 `pxNewListItem` 5. `pxNewListItem` 的 `pxContainer` 改成指向此list 6. 此list的 `uxNumberOfItems` +1 ![vListInsertEnd3](https://hackmd.io/_uploads/r15TUL146.png) ## listGET_OWNER_OF_NEXT_ENTRY 前面在研究 `List_t` 時,一直很好奇 `pxIndex` 的功能是什麼,一直看到註解說這個pointer要搭配 `listGET_OWNER_OF_NEXT_ENTRY` 這個macro就可以walk through這個list全部的item,並得到該item的TCB,想說這不是超適合這個lab嗎~所以就研究了一下這個macro,以下是此macro的定義: ```cpp /* * Access function to obtain the owner of the next entry in a list. * * The list member pxIndex is used to walk through a list. Calling * listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list * and returns that entry's pxOwner parameter. Using multiple calls to this * function it is therefore possible to move through every item contained in * a list. * * The pxOwner parameter of a list item is a pointer to the object that owns * the list item. In the scheduler this is normally a task control block. * The pxOwner parameter effectively creates a two way link between the list * item and its owner. * * @param pxTCB pxTCB is set to the address of the owner of the next list item. * @param pxList The list from which the next item owner is to be returned. * * \page listGET_OWNER_OF_NEXT_ENTRY listGET_OWNER_OF_NEXT_ENTRY * \ingroup LinkedList */ #define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \ { \ List_t * const pxConstList = ( pxList ); \ /* Increment the index to the next item and return the item, ensuring */ \ /* we don't return the marker used at the end of the list. */ \ ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \ { \ ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ } \ ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \ } ``` 來逐一看這個macro的code: - 首先,參數 `pxTCB` 和 `pxList` 都是pointer型態,`pxTCB` 是指向 `TCB_t` 的pointer,`pxList` 則是指向 `List_t` 的pointer - 宣告一個指向 `List_t` 的pointer,名稱是 `pxConstList`,並把 `pxList` 的value assign給 `pxConstList`,這樣 `pxConstList` 就指向 `pxList` 所指向的list了 - PS. `pxConstList` 是 `List_t * const` ,代表這個pointer指向的對象是不能更改的,也就是這個pointer的value是不能更改的,他只能指向同一個東西,不能讓他指向其他東西,所以一開始宣告時就必須必須告訴他要指向誰才行 - `( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;` - 一開始的 `pxIndex` 是指向 `List_t` 的 `xListEnd`,所以這裡讓 `pxIndex` 指向第一個 `ListItem_t` - `if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) )` - `pxIndex` 是一個指向 `ListItem_t` 的pointer,把這個pointer的value拿出來並轉型成 `(void *)` - 把list的 `xListEnd` 的address拿出來,並且也轉型成 `(void *)` - 再來,比較這兩個address是否一樣,若一樣則代表已經到list的end了,因為已經到 `xListEnd` 了,所以這時要 `( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;` ,這樣才能指向第一個item - `( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;` - 把目前 `pxIndex` 指向的 `ListItem_t` 的 `pvOwner` 的值assign給 `pxTCB` ,這樣 `pxTCB` 就會指向該TCB,我們後續就可以用 `pxTCB` 去存取TCB - 這裡因為是用macro,所以可以直接更改pointer指向的對象;但若 `listGET_OWNER_OF_NEXT_ENTRY` 被改成一個function,那 `pxTCB` 這個參數就必須是pointer to pointer才可以 :::info 為什麼要轉型成 `(void *)` 才去做比較? - 在C,若要比較pointers的值是否相同,很常用的技巧是會先把他轉型成 `(void *)` 後才去做比較,否則編譯器可能會有type mismatch的warning或error - 另外,也可以轉型成 `size_t` 去做比較 ::: :::info 複習 **Const & Pointers** ++Pointers to constants++ ```cpp int num1 = 100; int num2 = 200; const int *ptr = &num1; *ptr = 1000; // ERROR! ptr = &num2; // OK ``` - 可以讓pointer去指向不同的物件 - 但是我們不能更改pointer指向的物件的value ++Constant pointers++ ```cpp int num1 = 100; int num2 = 200; int *const ptr = &num1; *ptr = 1000; // OK ptr = &num2; // ERROR! ``` - 這個pointer本身是constant,不能讓他指向其他物件 - 所以這用法在宣告時就必須一併告訴他要指向哪個物件 - 我們可以修改此pointer指向的物件的value ++Constant pointers to constants++ ```cpp int num1 = 100; int num2 = 200; const int *const ptr = &num1; *ptr = 1000; // ERROR! ptr = &num2; // ERROR! ``` - 這個pointer本身是constant,不能讓他指向其他物件 - 在宣告此pointer時就必須一併告訴他要指向哪個物件 - 我們不能更改pointer指向的物件的value ::: 再來,我找了一下 `tasks.c` 裡面都是怎麼使用 `listGET_OWNER_OF_NEXT_ENTRY` 這個macro,找到了以下這段code: ```cpp static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ) { configLIST_VOLATILE TCB_t *pxNextTCB, *pxFirstTCB; UBaseType_t uxTask = 0; if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) { listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ /* Populate an TaskStatus_t structure within the pxTaskStatusArray array for each task that is referenced from pxList. See the definition of TaskStatus_t in task.h for the meaning of each TaskStatus_t structure member. */ do { listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ vTaskGetInfo( ( TaskHandle_t ) pxNextTCB, &( pxTaskStatusArray[ uxTask ] ), pdTRUE, eState ); uxTask++; } while( pxNextTCB != pxFirstTCB ); } else { mtCOVERAGE_TEST_MARKER(); } return uxTask; } ``` 可以看到若要使用 `listGET_OWNER_OF_NEXT_ENTRY` 這個macro,基本用法如下: ```cpp! TCB_t *pxNextTCB, *pxFirstTCB; if (listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0) { // 要先確認這個 List 裡面是有 item 的 // PS. pxList 是個 pointer to List_t listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); do { listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); // 這裡就可以用 pxNextTCB 去存取 ListItem 的 TCB } while ( pxNextTCB != pxFirstTCB ); } ``` 所以依據這個macro,我又改寫了一下我的 `Taskmonitor`,並確定可以得到一樣的結果: ```cpp= void Taskmonitor(void) { /* Initialize string */ char Monitor_data[130]; memset(Monitor_data,'\0',sizeof(Monitor_data)); /* Stop scheduler */ /* Taskmonitor() will block when UART is transmitting data */ /* Scheduler will change list data when Taskmonitor() is blocked */ vTaskSuspendAll(); /* Print title */ sprintf(Monitor_data,"|Name |Priority(Base/actual) |pxStack |pxTopOfStack |State |\n\r"); HAL_UART_Transmit(&huart2, (uint8_t *) Monitor_data, strlen(Monitor_data), 0xffff); TCB_t *pxNextTCB = NULL, *pxFirstTCB = NULL; /* pxReadyTasksLists */ for (UBaseType_t priority = 0; priority < configMAX_PRIORITIES; ++priority) { if (listCURRENT_LIST_LENGTH( &pxReadyTasksLists[priority] ) > ( UBaseType_t ) 0) { listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, &pxReadyTasksLists[priority] ); do { listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, &pxReadyTasksLists[priority] ); vPrintTCB(pxNextTCB, "Ready"); } while (pxNextTCB != pxFirstTCB); } } /* pxDelayedTaskList*/ if (listCURRENT_LIST_LENGTH( pxDelayedTaskList ) > ( UBaseType_t ) 0) { listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxDelayedTaskList ); do { listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxDelayedTaskList ); vPrintTCB(pxNextTCB, "Blocked"); } while (pxNextTCB != pxFirstTCB); } /* pxOverflowDelayedTaskList */ if (listCURRENT_LIST_LENGTH( pxOverflowDelayedTaskList ) > ( UBaseType_t ) 0) { listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxOverflowDelayedTaskList ); do { listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxOverflowDelayedTaskList ); vPrintTCB(pxNextTCB, "Overflow"); } while (pxNextTCB != pxFirstTCB); } /* Resume scheduler */ xTaskResumeAll(); } ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully