From 50162e6f12b8ae10bb3e97df847108179edad71f Mon Sep 17 00:00:00 2001 From: Dongju Chae Date: Wed, 26 Jun 2019 20:14:33 +0900 Subject: [PATCH] [NRC/N71] Make only N4x manage the triple buffering This commit updates the overall buffer management in memory allocator. Now, only N4x can manage triple buffering via .switch_buffers() Signed-off-by: Dongju Chae --- core/npu-engine/src/ne-mem.c | 158 +++++++++++++++++++++++++++++-------------- core/npu-engine/src/ne-mem.h | 15 ++-- 2 files changed, 118 insertions(+), 55 deletions(-) diff --git a/core/npu-engine/src/ne-mem.c b/core/npu-engine/src/ne-mem.c index 116b729..20c9f45 100644 --- a/core/npu-engine/src/ne-mem.c +++ b/core/npu-engine/src/ne-mem.c @@ -32,6 +32,8 @@ */ #define MEM_LOCK() pthread_mutex_lock(&mpriv.mutex) #define MEM_UNLOCK() pthread_mutex_unlock(&mpriv.mutex) +#define MEM_WAKEUP() pthread_cond_broadcast (&mpriv.cond) +#define MEM_WAIT() pthread_cond_wait (&mpriv.cond, &mpriv.mutex); /** * @brief memory chunk state @@ -92,10 +94,11 @@ typedef struct { void* virtaddr; /**< base virtual address for memory pool */ buffer *buffer [MEM_NUM_BUFFERS]; /**< buffer array */ - int buffer_idx [MEM_NUM_BUFFERS]; /**< buffer index for each */ + int buffer_head; /**< buffer head (changed by .switch_buffers()) */ uint64_t buffer_size; /**< buffer size for each */ pthread_mutex_t mutex; /**< mutex for locking */ + pthread_cond_t cond; /**< cond for broadcasting */ list used_chunks; /**< a list of used chunks */ list free_chunks; /**< a list of free chunks */ @@ -615,9 +618,6 @@ buffer_create (uint64_t size) priv->state = BUFFER_STATE_EMPTY; priv->hwmem = new_hwmem; - pthread_mutex_init (&priv->mutex, NULL); - pthread_cond_init (&priv->cond, NULL); - new_buffer = (buffer *) malloc (sizeof (buffer)); new_buffer->priv = (void *) priv; } @@ -681,6 +681,9 @@ buffer_change_state (buffer_priv *priv, buffer_state state) bool state_changed = false; switch (state) { + case BUFFER_STATE_INVAL: + state_changed = true; + break; case BUFFER_STATE_EMPTY: /* when the output buffer was returned */ if (priv->state == BUFFER_STATE_OUTPUT_RETURN || @@ -720,9 +723,10 @@ buffer_change_state (buffer_priv *priv, buffer_state state) if (!state_changed) RETURN_ERROR (EINVAL); - pthread_cond_broadcast (&priv->cond); priv->state = state; + MEM_WAKEUP(); + return 0; } @@ -799,6 +803,7 @@ mem_init (uint64_t size_in, uint64_t *size_out) list_add (&mpriv.free_chunks, &chunk->list); pthread_mutex_init (&mpriv.mutex, NULL); + pthread_cond_init (&mpriv.cond, NULL); return 0; } @@ -967,7 +972,7 @@ mem_resize_buffers (uint64_t size) if (first) { buffer_priv *priv = mpriv.buffer[idx]->priv; - mpriv.buffer_idx[idx] = BUFFER_STATE_INVAL; + mpriv.buffer_head = 0; buffer_change_state (priv, BUFFER_STATE_EMPTY); /** @@ -988,59 +993,107 @@ out: } /** - * @brief get the next buffer (mananged in multi-buffering) + * @brief switch the role of each I/O buffer (in triple buffering) + * @note This should be called only in N4x. + */ +static void +mem_switch_buffers (void) +{ + buffer *buf; + + MEM_LOCK (); + + buf = mpriv.buffer[mpriv.buffer_head]; + /* wait until the buffer head becomes input ready */ + while (buffer_get_state (buf) != BUFFER_STATE_INPUT_READY) + MEM_WAIT(); + + mpriv.buffer_head = (mpriv.buffer_head + 1) % MEM_NUM_BUFFERS; + + /* notify buffer head is changed */ + MEM_WAKEUP(); + MEM_UNLOCK(); +} + +/** + * @brief wait the available buffer for the requested role + * @param[in] role the role of buffer + * @return the target buffer + */ +static buffer* +wait_until_available (buffer_role role) +{ + buffer *buf = NULL; + int idx; + + MEM_LOCK (); + + switch (role) { + case BUFFER_ROLE_INPUT: /* called in N1 to receive input data from the host */ + while (1) { + idx = mpriv.buffer_head; + buf = mpriv.buffer[idx]; + if (buffer_get_state(buf) == BUFFER_STATE_EMPTY) { + buffer_change_state (buf->priv, BUFFER_STATE_INPUT_WRITING); + goto out; + } + MEM_WAIT (); + } + break; + case BUFFER_ROLE_NPU: /* called in N4 to perform NPU processing */ + idx = mpriv.buffer_head == 0 ? MEM_NUM_BUFFERS -1 : mpriv.buffer_head - 1; + buf = mpriv.buffer[idx]; + /* it's always possible to access (as it manages multi-buffering) */ + assert (buffer_get_state(buf) == BUFFER_STATE_INPUT_READY); + buffer_change_state (buf->priv, BUFFER_STATE_NPU_RUNNING); + break; + case BUFFER_ROLE_OUTPUT: /* called in N1 to return output data to the host */ + while (1) { + int i; + /* iterate to find the output-ready frame (eldest first) */ + idx = (mpriv.buffer_head + 1) % MEM_NUM_BUFFERS; + for (i = 0; i < MEM_NUM_BUFFERS; i++, idx = (idx+1) % MEM_NUM_BUFFERS) { + buf = mpriv.buffer[idx]; + if (buffer_get_state (buf) == BUFFER_STATE_OUTPUT_READY) { + buffer_change_state (buf->priv, BUFFER_STATE_OUTPUT_RETURN); + goto out; + } + } + MEM_WAIT (); + } + break; + } + +out: + MEM_UNLOCK (); + + return buffer; +} + +/** + * @brief get the next I/O buffer dedicated to the requsted role. * @param[in] role the role of buffer * @return the next buffer if no error, otherwise NULL * - * @detail this switches buffers where to be operated. - * it may wait until the next buffer is ready + * @note it needs to wait to get a valid buffer if another component is still using it. */ static buffer * mem_get_next_buffer (buffer_role role) { - buffer *next_buffer; - buffer_priv *next_priv; - int next_idx; + buffer *buf = NULL; if (!(role >= BUFFER_ROLE_INPUT && role <= BUFFER_ROLE_OUTPUT)) - return NULL; + goto out; - /* get next buffer */ - next_idx = (mpriv.buffer_idx [role] + 1) % MEM_NUM_BUFFERS; - next_buffer = mpriv.buffer [next_idx]; - next_priv = next_buffer->priv; + buf = wait_until_available (role); + if (buf) { + buffer_priv *priv = buf->priv; - /** - * wait until the next buffer becomes a valid state - */ - pthread_mutex_lock (&next_priv->mutex); - if (role == BUFFER_ROLE_INPUT) { - while (next_priv->state != BUFFER_STATE_EMPTY) - pthread_cond_wait (&next_priv->cond, &next_priv->mutex); - - buffer_change_state (next_priv, BUFFER_STATE_INPUT_WRITING); - } else if (role == BUFFER_ROLE_NPU) { - while (next_priv->state != BUFFER_STATE_INPUT_READY) - pthread_cond_wait (&next_priv->cond, &next_priv->mutex); - - buffer_change_state (next_priv, BUFFER_STATE_NPU_RUNNING); - } else { /* role == BUFFER_ROLE_OUTPUT */ - while (next_priv->state != BUFFER_STATE_OUTPUT_READY) - pthread_cond_wait (&next_priv->cond, &next_priv->mutex); - - buffer_change_state (next_priv, BUFFER_STATE_OUTPUT_RETURN); + hwmem_activate (priv->hwmem); } - pthread_mutex_unlock (&next_priv->mutex); - - /* update new index */ - MEM_LOCK(); - - mpriv.buffer_idx [role] = next_idx; - - MEM_UNLOCK(); - - return next_buffer; +out: + return buf; } /** @@ -1053,14 +1106,14 @@ mem_get_next_buffer (buffer_role role) * would be valid for other users. */ static int -mem_return_buffer (buffer *buffer) +mem_return_buffer (buffer *buf) { int err = 0; - buffer_priv *priv = buffer->priv; + buffer_priv *priv = buf->priv; assert (priv); - pthread_mutex_lock (&priv->mutex); + MEM_LOCK(); switch (priv->state) { case BUFFER_STATE_INPUT_WRITING: @@ -1077,7 +1130,11 @@ mem_return_buffer (buffer *buffer) break; } - pthread_mutex_unlock (&priv->mutex); + MEM_UNLOCK(); + + /* deactivate buffer */ + if (err == 0) + hwmem_deactivate (priv->hwmem); RETURN_ERROR (err); } @@ -1111,6 +1168,7 @@ static mem mem_instance = { .dealloc = mem_dealloc, /* for I/O buffer */ .resize_buffers = mem_resize_buffers, + .switch_buffers = mem_switch_buffers, .get_next_buffer = mem_get_next_buffer, .return_buffer = mem_return_buffer, /* misc */ diff --git a/core/npu-engine/src/ne-mem.h b/core/npu-engine/src/ne-mem.h index 0473bc9..88d1c1a 100644 --- a/core/npu-engine/src/ne-mem.h +++ b/core/npu-engine/src/ne-mem.h @@ -84,7 +84,7 @@ int hwmem_deactivate (hwmem *hwmem); * @note the number of roles should be matched with MEM_NUM_BUFFERS */ typedef enum { - BUFFER_ROLE_INPUT, /* for input feed */ + BUFFER_ROLE_INPUT = 0, /* for input feed */ BUFFER_ROLE_NPU, /* for npu processing */ BUFFER_ROLE_OUTPUT, /* for output return */ } buffer_role; @@ -94,7 +94,7 @@ typedef enum { * @note this enum order is a sequence of buffer lifecycle (in most cases) */ typedef enum { - BUFFER_STATE_INVAL, + BUFFER_STATE_INVAL = 0, BUFFER_STATE_EMPTY, /* it's ready to buffer input data */ BUFFER_STATE_INPUT_WRITING, /* input data is being written */ BUFFER_STATE_INPUT_READY, /* input data is ready */ @@ -191,12 +191,17 @@ typedef struct { int (*resize_buffers) (uint64_t size); /** - * @brief get the next buffer (mananged in multi-buffering) + * @brief switch the role of each I/O buffer (in triple buffering) + * @note This should be called only in N4x. + */ + void (*switch_buffers) (void); + + /** + * @brief get the next I/O buffer dedicated to the requsted role. * @param[in] role the role of buffer * @return the next buffer if no error, otherwise NULL * - * @detail this switches buffers where to be operated. - * it may wait until the next buffer is ready + * @note it needs to wait to get a valid buffer if another component is still using it. */ buffer * (*get_next_buffer) (buffer_role role); -- 2.7.4