Release 6.1.12

This commit is contained in:
Yuxin Zhou
2022-07-26 02:04:40 +00:00
parent 54cda6ee9e
commit 8c3c08f108
217 changed files with 13398 additions and 13432 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
/**************************************************************************/
/* */
/* Copyright (c) Microsoft Corporation. All rights reserved. */
/* */
/* This software is licensed under the Microsoft Software License */
/* Terms for Microsoft Azure RTOS. Full text of the license can be */
/* found in the LICENSE file at https://aka.ms/AzureRTOS_EULA */
/* and in the root directory of this software. */
/* */
/**************************************************************************/
/**************************************************************************/
/**************************************************************************/
/** */
/** ThreadX Component */
/** */
/** Execution Profile Kit */
/** */
/**************************************************************************/
/**************************************************************************/
#ifndef TX_EXECUTION_PROFILE_H
#define TX_EXECUTION_PROFILE_H
/* The thread execution profile kit is designed to track thread execution time
based on the hardware timer defined by TX_EXECUTION_TIME_SOURCE and
TX_EXECUTION_MAX_TIME_SOURCE below. When the thread's total time reaches
the maximum value, it remains there until the time is reset to 0 via a call
to tx_thread_execution_time_reset. There are several assumptions to the
operation of this kit, as follows:
1. In tx_port.h replace:
#define TX_THREAD_EXTENSION_3"
with:
#define TX_THREAD_EXTENSION_3 unsigned long long tx_thread_execution_time_total; \
unsigned long tx_thread_execution_time_last_start;
2. The TX_EXECUTION_TIME_SOURCE and TX_EXECUTION_MAX_TIME_SOURCE macros are
defined to utilize a local hardware time source.
3. The following routines are called from assembly code:
VOID _tx_execution_thread_enter(void);
VOID _tx_execution_thread_exit(void);
VOID _tx_execution_isr_enter(void);
VOID _tx_execution_isr_exit(void);
4. The ThreadX library must be rebuilt with TX_ENABLE_EXECUTION_CHANGE_NOTIFY so
that these macros are expanded in the TX_THREAD structure and so the assembly code macros
are enabled to call the execution profile routines.
5. Add tx_execution_profile.c to the application build. */
/* Define the basic time typedefs for 64-bit accumulation and a 32-bit timer source, which is the
most common configuration. */
typedef unsigned long long EXECUTION_TIME;
typedef unsigned long EXECUTION_TIME_SOURCE_TYPE;
/* For 64-bit time source, the typedef would be: */
/* typedef unsigned long long EXECUTION_TIME_SOURCE_TYPE; */
/* Define basic constants for the execution profile kit. */
ULONG _tx_thread_smp_time_get(void);
#define TX_EXECUTION_TIME_SOURCE (EXECUTION_TIME_SOURCE_TYPE) _tx_thread_smp_time_get();
#define TX_EXECUTION_MAX_TIME_SOURCE 0xFFFFFFFF
/* For 64-bit time source, the constant would be: */
/* #define TX_EXECUTION_MAX_TIME_SOURCE 0xFFFFFFFFFFFFFFFF */
/* Define APIs of the execution profile kit. */
struct TX_THREAD_STRUCT;
VOID _tx_execution_thread_enter(void);
VOID _tx_execution_thread_exit(void);
VOID _tx_execution_isr_enter(void);
VOID _tx_execution_isr_exit(void);
UINT _tx_execution_thread_time_reset(struct TX_THREAD_STRUCT *thread_ptr);
UINT _tx_execution_thread_total_time_reset(void);
UINT _tx_execution_isr_time_reset(void);
UINT _tx_execution_idle_time_reset(void);
UINT _tx_execution_thread_time_get(struct TX_THREAD_STRUCT *thread_ptr, EXECUTION_TIME *total_time);
UINT _tx_execution_thread_total_time_get(EXECUTION_TIME *total_time);
UINT _tx_execution_isr_time_get(EXECUTION_TIME *total_time);
UINT _tx_execution_idle_time_get(EXECUTION_TIME *total_time);
UINT _tx_execution_core_thread_total_time_get(UINT core, EXECUTION_TIME *total_time);
UINT _tx_execution_core_isr_time_get(UINT core, EXECUTION_TIME *total_time);
UINT _tx_execution_core_idle_time_get(UINT core, EXECUTION_TIME *total_time);
#endif

View File

@@ -8,25 +8,209 @@ ms.topic: article
ms.service: rtos
---
# Low Power APIs
# ThreadX Low Power Utilities
These low power utilities are intended to maintain ThreadX timers and the internal tick count while the processor is in a low-power/sleep state. The terms "low power" and "sleep" are used interchangeably throughout this document.
The low power utilities may be used with any ThreadX port except SMP.
## Installation of Low Power Utilities
The ThreadX low power utilities are comprised of two files:
- [tx_low_power.c](tx_low_power.c)
- [tx_low_power.h](tx_low_power.h)
These files can be built with the ThreadX library or built in the user application.
## Detailed Description
By default, ThreadX spins in an idle loop in the scheduler when there are no threads ready to run. It may be desireable to put the processor in a low power state while idle. Functions ```tx_low_power_enter``` and ```tx_low_power_exit``` are designed to enter and exit low power mode, respectively.
The ```tx_low_power_enter``` and ```tx_low_power_exit``` functions manage the ThreadX timers and invoke optional macros that the user must define in order to configure the hardware for low power mode. These macros are discussed in a section below.
For convenience, the Cortex-M and RX ports have calls to the low power APIs already integrated into their schedulers. If symbol **TX_LOW_POWER** is defined, functions ```tx_low_power_enter``` and ```tx_low_power_exit``` are called in the scheduler idle loop. It is assumed that the processor will exit sleep mode and resume execution in the idle loop (or in an interrupt and then return to the idle loop). For processors that exhibit different behavior (such as waking from sleep at the reset vector), these low power functions may need to be called elsewhere.
### Low Power Timer and Tick-less Low Power Mode
If accurate timekeeping is not desired while in low power mode, macros **TX_LOW_POWER_TIMER_SETUP** and **TX_LOW_POWER_TICKLESS** do not need to be defined. The internal ThreadX tick count will not reflect the time spent in low power mode.
If **TX_LOW_POWER_TIMER_SETUP** is defined, there are two use cases for timekeeping in low power operation:
1. Keep track of elapsed time in low power mode only when ThreadX timers are active. If there are ThreadX timers active, the elapsed time while in low power mode must be measured, thus a low power hardware timer is required (this timer can be configured in **TX_LOW_POWER_TIMER_SETUP**). If there are no ThreadX timers active when entering low power mode, then no hardware timer needs to keep track of elapsed time. This is "tick-less" operation. To enable this feature, the symbol **TX_LOW_POWER_TICKLESS** must be defined. The internal ThreadX tick count will not reflect the time spent in low power mode when no ThreadX timers are active.
2. Always keep track of time in low power mode. This is necessary to keep the internal ThreadX tick count accurate. In this case, **TX_LOW_POWER_TICKLESS** must *not* be defined, as a hardware timer will always be needed in low power mode to measure elapsed time. The hardware timer is intended to be configured in **TX_LOW_POWER_TIMER_SETUP**.
> Example 1: No low power timer is in use (**TX_LOW_POWER_TIMER_SETUP** is *not* defined). There is a ThreadX timer with 50 ticks remaining. The internal ThreadX tick count is 1000. The processor goes into low power mode for some length of time. After exiting low power mode, the ThreadX timer still has 50 ticks remaining. The internal ThreadX tick count is 1000.
> Example 2: A low power timer is available (**TX_LOW_POWER_TIMER_SETUP** is defined). **TX_LOW_POWER_TICKLESS** is defined. There is a ThreadX timer A with 50 ticks remaining and another ThreadX timer B with 20 ticks remaining. The internal ThreadX tick count is 1000. The processor goes into low power mode for 20 ticks. Upon exiting low power mode, the ThreadX timer A will have 30 ticks remaining. The expiration function for ThreadX timer B will be executed. The internal ThreadX tick count is 1020.
After executing for some time, the internal ThreadX tick count is 2000 and there are no ThreadX timers active. The processor goes into low power mode for some length of time. After exiting low power mode, the internal ThreadX tick count is 2000.
> Example 3: A low power timer is always used (**TX_LOW_POWER_TIMER_SETUP** is defined and **TX_LOW_POWER_TICKLESS** is *not* defined). The internal ThreadX tick count is 1000 and there are no ThreadX timers active. The processor goes into low power mode for some length of time. Upon exiting low power mode, it is determined that the processor was in low power mode for 20 ticks. The internal ThreadX tick count is updated to 1020.
## User-defined Macros
The following macros invoke functions that the user may want to define/implement.
### Summary of user-defined macros
- ```TX_LOW_POWER_TIMER_SETUP``` - *set up low power timer*
- ```TX_LOW_POWER_USER_ENTER``` - *configure processor to enter low power mode*
- ```TX_LOW_POWER_USER_EXIT``` - *configure processor to exit low power mode*
- ```TX_LOW_POWER_USER_TIMER_ADJUST``` - *return the number of ticks the processor was in low power mode*
---
### TX_LOW_POWER_TIMER_SETUP
```c
VOID TX_LOW_POWER_TIMER_SETUP(ULONG tx_low_power_next_expiration);
```
### Input parameters
- *tx_low_power_next_expiration* - the number of ticks to configure the low power timer.
### Return values
- *none*
**TX_LOW_POWER_TIMER_SETUP** is a macro invoking user-defined function that sets up a low power timer. To set up a low power timer or operate ticklessly in low power mode, this symbol must be defined. This macro is called in ```tx_low_power_enter```. If **TX_LOW_POWER_TICKLESS** is not defined and there are no timers active, the *tx_low_power_next_expiration* parameter will be set to 0xFFFFFFFF.
> Note: The number of ThreadX ticks is the input to this function. The frequency of a ThreadX timer tick is defined in **TX_TIMER_TICKS_PER_SECOND** (typically defined in file tx_api.h, tx_user.h, or tx_port.h).
> Note: do not put the processor to sleep in this macro.
### Optional Define
- **TX_LOW_POWER_TICKLESS** - an optional define to operate ticklessly in low power mode only if no ThreadX timers are active. With symbol **TX_LOW_POWER_TICKLESS** defined, if there are no ThreadX timers active, **TX_LOW_POWER_TIMER_SETUP** will not be called in ```tx_low_power_enter```. ThreadX will not maintain/update the internal tick count during or after exiting low power mode. Symbol **TX_LOW_POWER_TIMER_SETUP** must also be defined if defining **TX_LOW_POWER_TICKLESS**.
### Example
```c
/* Low power timer function prototype. */
void low_power_timer_config(ULONG ticks);
/* Define the TX_LOW_POWER_TIMER_SETUP macro. */
#define TX_LOW_POWER_TIMER_SETUP low_power_timer_config
void low_power_timer_config(ULONG ticks)
{
/* Insert code here to configure a hardware timer
to wake the processor from sleep after
ticks/TX_TIMER_TICKS_PER_SECOND seconds. */
}
```
---
### TX_LOW_POWER_USER_ENTER
A macro invoking a user-defined function that configures the processor for entering low power mode (e.g. turn off peripherals and select a sleep mode). This macro is called in ```tx_low_power_enter```.
### Input parameters
- *none*
### Return values
- *none*
### Example
```c
/* Low power enter function prototype. */
void low_power_enter(void);
/* Define the TX_LOW_POWER_USER_ENTER macro. */
#define TX_LOW_POWER_USER_ENTER low_power_enter
void low_power_enter(void)
{
/* Insert code here to configure the processor to enter low power mode. */
}
```
---
### TX_LOW_POWER_USER_EXIT
A macro invoking a user-defined function that configures the processor for exiting low power mode (e.g. turn on peripherals). This is called in ```tx_low_power_exit```.
### Input parameters
- *none*
### Return values
- *none*
### Example
```c
/* Low power exit function prototype. */
void low_power_exit(void);
/* Define the TX_LOW_POWER_USER_EXIT macro. */
#define TX_LOW_POWER_USER_EXIT low_power_exit
void low_power_exit(void)
{
/* Insert code here to configure the processor to exit low power mode. */
}
```
---
### TX_LOW_POWER_USER_TIMER_ADJUST
```c
ULONG TX_LOW_POWER_USER_TIMER_ADJUST(VOID);
```
A macro invoking a user-defined function to determine how much time has elapsed while in low power mode (in units of ThreadX ticks). This is called in ```tx_low_power_exit``` and returns the number of ticks needed to adjust the ThreadX timers.
When exiting low power mode, there are two possibilities:
1. The processor slept for the entire time the timer was configured to sleep.
2. The processor was awakened early.
### Input parameters
- *none*
### Return values
- *tx_low_power_adjust_ticks*
### Example
```c
/* Low power timer adjust function prototype. */
ULONG low_power_timer_adjust(void);
/* Define the TX_LOW_POWER_USER_TIMER_ADJUST macro. */
#define TX_LOW_POWER_USER_TIMER_ADJUST low_power_timer_adjust
ULONG low_power_timer_adjust(void)
{
ULONG actual_ticks_slept;
ULONG elapsed_time_in_ms;
/* Insert code here to read timer registers to determine
how long the processor actually slept. */
elapsed_time_in_ms = read_timer_register();
/* Convert elapsed time to ThreadX ticks. */
actual_ticks_slept = elapsed_time_in_ms / (1000/TX_TIMER_TICKS_PER_SECOND);
return(actual_ticks_slept);
}
```
---
## Summary of Low Power APIs
There are additional API functions available for low power, as follows:
- ***tx_low_power_enter*** - *Enter low power mode*
- ***tx_low_power_exit*** - *Exit low power mode*
- ***tx_time_increment*** - *Increment ThreadX timers by specific amount*
- ***tx_timer_get_next*** - *Get next ThreadX timer expiration*
- ```tx_low_power_enter``` - *Enter low power mode*
- ```tx_low_power_exit``` - *Exit low power mode*
- ```tx_time_increment``` - *Increment ThreadX timers by specific amount*
- ```tx_timer_get_next``` - *Get next ThreadX timer expiration*
---
## User-defined Macros
- **TX_LOW_POWER_TIMER_SETUP** - an optional macro to a user routine that sets up a low power clock. To set up a low power timer or operate ticklessly in low power mode, this must be defined. This is called in **tx_low_power_enter**.
- **TX_LOW_POWER_TICKLESS** - an optional define to operate ticklessly in low power mode. Symbol **TX_LOW_POWER_TIMER_SETUP** must also be defined. With this defined, there is no need to set up a low power timer to keep track of time. ThreadX will not maintain/update the internal tick count during or after exiting low power mode.
- **TX_LOW_POWER_USER_ENTER** - an optional macro to a user routine that configures the processor for entering low power mode (e.g. turn off peripherals and select a sleep mode). This is called in **tx_low_power_enter**.
- **TX_LOW_POWER_USER_EXIT** - an optional macro to a user routine that configures the processor for exiting low power mode (e.g. turn on peripherals). This is called in **tx_low_power_exit**.
- **TX_LOW_POWER_USER_TIMER_ADJUST** - an optional user macro to determine how much time has elapsed while in low power mode (in units of ThreadX ticks). This is called in **tx_low_power_exit** to get the number of ticks needed to adjust the ThreadX timers.
## tx_low_power_enter
@@ -40,26 +224,24 @@ VOID tx_low_power_enter(VOID);
### Description
This service enters low power mode.
This service enters low power mode. The macros **TX_LOW_POWER_TIMER_SETUP** and **TX_LOW_POWER_USER_ENTER** are called in this function to allow the user to configure a low power timer and configure the hardware for low power mode.
For keeping track of time while in low power mode, there are two possibilities:
1. A ThreadX timer is active. Function **tx_timer_get_next** returns **TX_TRUE**. Note that in this situation, a low power clock must be used in order to wake up the CPU for the next ThreadX timer expiration. Therefore an alternative clock must be programmed. Program the hardware timer source such that the next timer interrupt is equal to: *tx_low_power_next_expiration \* tick_frequency*. The *tick_frequency* is application-specific and typically set up in **tx_low_level_initialize**.
1. A ThreadX timer is active. Function ```tx_timer_get_next``` returns **TX_TRUE**. Note that in this situation, a low power clock must be used in order to wake up the CPU for the next ThreadX timer expiration. Therefore a low power timer/clock must be programmed. Program the hardware timer source such that the next timer interrupt is equal to: *tx_low_power_next_expiration \* tick_frequency*. The *tick_frequency* is application-specific and typically set up in ```tx_low_level_initialize```.
2. There are no ThreadX timers active. Function **tx_timer_get_next** returns **TX_FALSE**.
1. The application may choose not to keep the ThreadX internal
tick count updated (define **TX_LOW_POWER_TICKLESS**), therefore there is no need
to set up a low power clock.
2. The application still needs to keep the ThreadX tick up-to-date. In this case
a low power clock needs to be set up.
2. There are no ThreadX timers active. Function ```tx_timer_get_next``` returns *TX_FALSE*.
1. The application may choose **not** to keep the ThreadX internal
tick count updated (define **TX_LOW_POWER_TICKLESS**), therefore there is no need to set up a low power clock.
2. The application still needs to keep the ThreadX tick up-to-date. In this case a low power clock needs to be configured.
### Input parameters
- **none**
- *none*
### Return values
- **none**
- *none*
### Allowed from
@@ -67,7 +249,7 @@ Internal ThreadX code, application
### Example
ARM assembly
ARM Cortex-M assembly
```c
#ifdef TX_LOW_POWER
PUSH {r0-r3}
@@ -106,15 +288,15 @@ VOID tx_low_power_exit(VOID);
### Description
This service exits low power mode.
This service exits low power mode. Macro **TX_LOW_POWER_USER_EXIT** is called in this function to allow the user to configure the hardware to exit low power mode. Macro **TX_LOW_POWER_USER_TIMER_ADJUST** is called in this function to determine how long the processor actually slept.
### Input parameters
- **none**
- *none*
### Return values
- **none**
- *none*
### Allowed from
@@ -150,7 +332,7 @@ Internal ThreadX code, application
## tx_time_increment
This function increments the current time by a specified value. The value was derived by the application by calling the **tx_timer_get_next** function prior to this call, which was right before the processor was put in low power mode.
This function increments the current time by a specified value. The value was derived by the application by calling the ```tx_timer_get_next``` function prior to this call, which was right before the processor was put in low power mode.
### Prototype
@@ -160,15 +342,15 @@ VOID tx_time_increment(ULONG time_increment);
### Description
This function increments the current time by a specified value. The value was derived by the application by calling the **tx_timer_get_next** function prior to this call, which was right before the processor was put in low power mode.
This function increments the current time by a specified value. The value was derived by the application by calling the ```tx_timer_get_next``` function prior to this call, which was right before the processor was put in low power mode.
### Input parameters
- **time_increment** Number of ThreadX ticks to increment time and timers.
- *time_increment* - Number of ThreadX ticks to increment time and timers.
### Return values
- **none**
- *none*
### Allowed from
@@ -176,7 +358,7 @@ Internal ThreadX code, application
### Example
From **tx_low_power_exit**:
From ```tx_low_power_exit```:
```c
/* Call the low-power timer driver code to obtain the amount of time (in ticks)
@@ -213,16 +395,16 @@ ULONG tx_timer_get_next(ULONG *next_timer_tick_ptr);
### Description
This service gets the next ThreadX timer expiration, in ticks.
This service gets the next ThreadX timer expiration, in ticks.
### Input parameters
- **next_timer_tick_ptr** pointer to hold number of ticks
- *next_timer_tick_ptr* - pointer to hold number of ticks
### Return values
- **TX_TRUE** (1) At least one timer is active.
- **TX_FALSE** (0) No timers are currently active.
- *TX_TRUE* (1) At least one timer is active.
- *TX_FALSE* (0) No timers are currently active.
### Allowed from
@@ -230,7 +412,7 @@ Internal ThreadX code, application
### Example
From **tx_low_power_enter**:
From ```tx_low_power_enter```:
```c
ULONG tx_low_power_next_expiration; /* The next timer expiration (units of ThreadX timer ticks). */

View File

@@ -29,10 +29,15 @@
/* 01-31-2022 William E. Lamie Modified comment(s), and */
/* fixed compiler warnings, */
/* resulting in version 6.1.10 */
/* 07-29-2022 Cindy Deng Added simple static scheduler */
/* start flag, corrected stack */
/* allocation size, */
/* resulting in version 6.1.12 */
/* */
/**************************************************************************/
#include <stdint.h>
#include <limits.h>
#include <tx_api.h>
#include <tx_thread.h>
@@ -63,6 +68,7 @@ static TX_BYTE_POOL txfr_heap;
static UINT txfr_heap_initialized;
#if (TX_FREERTOS_AUTO_INIT == 1)
static UINT txfr_initialized;
static UINT txfr_scheduler_started;
#endif // #if (TX_FREERTOS_AUTO_INIT == 1)
// TODO - do something with malloc.
@@ -263,6 +269,7 @@ void vPortExitCritical(void)
void vTaskStartScheduler(void)
{
#if (TX_FREERTOS_AUTO_INIT == 1)
txfr_scheduler_started = 1u;
_tx_thread_schedule();
#else
// Nothing to do, THREADX scheduler is already started.
@@ -272,6 +279,11 @@ void vTaskStartScheduler(void)
BaseType_t xTaskGetSchedulerState(void)
{
#if (TX_FREERTOS_AUTO_INIT == 1)
if(txfr_scheduler_started == 0u) {
return taskSCHEDULER_NOT_STARTED;
}
#endif
if(_tx_thread_preempt_disable > 0u) {
return taskSCHEDULER_SUSPENDED;
} else {
@@ -315,6 +327,7 @@ TaskHandle_t xTaskCreateStatic(TaskFunction_t pxTaskCode,
{
UINT prio;
UINT ret;
ULONG stack_depth_bytes;
TX_INTERRUPT_SAVE_AREA;
configASSERT(pxTaskCode != NULL);
@@ -329,6 +342,13 @@ TaskHandle_t xTaskCreateStatic(TaskFunction_t pxTaskCode,
}
#endif
if(ulStackDepth > (ULONG_MAX / sizeof(StackType_t))) {
/* Integer overflow in stack depth */
TX_FREERTOS_ASSERT_FAIL();
return NULL;
}
stack_depth_bytes = ulStackDepth * sizeof(StackType_t);
TX_MEMSET(pxTaskBuffer, 0, sizeof(*pxTaskBuffer));
pxTaskBuffer->p_task_arg = pvParameters;
pxTaskBuffer->p_task_func = pxTaskCode;
@@ -342,7 +362,7 @@ TaskHandle_t xTaskCreateStatic(TaskFunction_t pxTaskCode,
prio = txfr_prio_fr_to_tx(uxPriority);
ret = tx_thread_create(&pxTaskBuffer->thread, (CHAR *)pcName, txfr_thread_wrapper, (ULONG)pvParameters,
puxStackBuffer, ulStackDepth, prio, prio, 0u, TX_DONT_START);
puxStackBuffer, stack_depth_bytes, prio, prio, 0u, TX_DONT_START);
if(ret != TX_SUCCESS) {
TX_FREERTOS_ASSERT_FAIL();
return NULL;
@@ -375,6 +395,7 @@ BaseType_t xTaskCreate(TaskFunction_t pvTaskCode,
txfr_task_t *p_task;
UINT ret;
UINT prio;
ULONG stack_depth_bytes;
TX_INTERRUPT_SAVE_AREA;
configASSERT(pvTaskCode != NULL);
@@ -387,8 +408,14 @@ BaseType_t xTaskCreate(TaskFunction_t pvTaskCode,
tx_freertos_auto_init();
}
#endif
if((usStackDepth > (SIZE_MAX / sizeof(StackType_t)))
|| (usStackDepth > (ULONG_MAX / sizeof(StackType_t)))) {
/* Integer overflow in stack depth */
return errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
stack_depth_bytes = usStackDepth * sizeof(StackType_t);
p_stack = txfr_malloc(usStackDepth);
p_stack = txfr_malloc((size_t)stack_depth_bytes);
if(p_stack == NULL) {
return errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
@@ -417,7 +444,7 @@ BaseType_t xTaskCreate(TaskFunction_t pvTaskCode,
prio = txfr_prio_fr_to_tx(uxPriority);
ret = tx_thread_create(&p_task->thread, (CHAR *)pcName, txfr_thread_wrapper, (ULONG)pvParameters,
p_stack, usStackDepth, prio, prio, 0u, TX_DONT_START);
p_stack, stack_depth_bytes, prio, prio, 0u, TX_DONT_START);
if(ret != TX_SUCCESS) {
(void)tx_semaphore_delete(&p_task->notification_sem);
txfr_free(p_stack);