/** * Copyright (c) 2011 - 2018, Nordic Semiconductor ASA * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form, except as embedded into a Nordic * Semiconductor ASA integrated circuit in a product or a software update for * such product, must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. * * 3. Neither the name of Nordic Semiconductor ASA nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * 4. This software, with or without modification, must only be used with a * Nordic Semiconductor ASA integrated circuit. * * 5. Any software provided in binary form under this license must not be reverse * engineered, decompiled, modified and/or disassembled. * * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /** * @file * @brief Atomic FIFO internal file * * This file should be included only by nrf_atfifo internally. * Needs nrf_atfifo.h included first. */ #ifndef NRF_ATFIFO_H__ #error This is internal file. Do not include this file in your program. #endif #ifndef NRF_ATFIFO_INTERNAL_H__ #define NRF_ATFIFO_INTERNAL_H__ #include #include "nrf.h" #include "app_util.h" #include "nordic_common.h" #if ((__CORTEX_M >= 0x03U) || (__CORTEX_SC >= 300U)) == 0 #error Unsupported core version #endif /* * Make sure that rd and wr pos in a tag are aligned like expected * Changing this would require changes inside assembly code! */ STATIC_ASSERT(offsetof(nrf_atfifo_postag_pos_t, wr) == 0); STATIC_ASSERT(offsetof(nrf_atfifo_postag_pos_t, rd) == 2); /** * @brief Atomically reserve space for a new write. * * @param[in,out] p_fifo FIFO object. * @param[out] old_tail Tail position tag before new space is reserved. * * @retval true Space available. * @retval false Memory full. * * @sa nrf_atfifo_wspace_close */ static bool nrf_atfifo_wspace_req(nrf_atfifo_t * const p_fifo, nrf_atfifo_postag_t * const p_old_tail); /** * @brief Atomically mark all written data available. * * This function marks all data available for reading. * This marking is done by copying tail.pos.wr into tail.pos.rd. * * It must be called only when closing the first write. * It cannot be called if any write access was interrupted. * See the code below: * @code * if (old_tail.pos.wr == old_tail.pos.rd) * { * nrf_atfifo_wspace_close(my_fifo); * return true; * } * return false; * @endcode * * @param[in,out] p_fifo FIFO object. * * @sa nrf_atfifo_wspace_req */ static void nrf_atfifo_wspace_close(nrf_atfifo_t * const p_fifo); /** * @brief Atomically get a part of a buffer to read data. * * @param[in,out] p_fifo FIFO object. * @param[out] old_head Head position tag before the data buffer is read. * * @retval true Data available for reading. * @retval false No data in the buffer. * * @sa nrf_atfifo_rspace_close */ static bool nrf_atfifo_rspace_req(nrf_atfifo_t * const p_fifo, nrf_atfifo_postag_t * const p_old_head); /** * @brief Atomically release all read data. * * This function marks all data that was read as free space, * which is available for writing. * This marking is done by copying head.pos.rd into head.pos.wr. * * It must be called only when closing the first read. * It cannot be called when the current read access interrupted any other read access. * See code below: * @code * if (old_head.pos.wr == old_head.pos.rd) * { * nrf_atfifo_rspace_close(my_fifo); * return true; * } * return false; * @endcode * * @param[in,out] p_fifo FIFO object. * * @sa nrf_atfifo_rspace_req */ static void nrf_atfifo_rspace_close(nrf_atfifo_t * const p_fifo); /** * @brief Safely clear the FIFO, internal function. * * This function realizes the functionality required by @ref nrf_atfifo_clear. * * @param[in,out] p_fifo FIFO object. * * @retval true All the data was released. * @retval false All the data available for releasing was released, but there is some pending transfer. */ static bool nrf_atfifo_space_clear(nrf_atfifo_t * const p_fifo); /* --------------------------------------------------------------------------- * Implementation starts here */ #if defined ( __CC_ARM ) __ASM bool nrf_atfifo_wspace_req(nrf_atfifo_t * const p_fifo, nrf_atfifo_postag_t * const p_old_tail) { /* Registry usage: * R0 - p_fifo * R1 - p_old_tail * R2 - internal variable old_tail (saved by caller) * R3 - internal variable new_tail (saved by caller) * R4 - internal temporary register (saved by this function) * R5 - not used stored to keep the stack aligned to 8 bytes * Returned value: * R0 (bool - 32 bits) */ push {r4, r5} nrf_atfifo_wspace_req_repeat /* Load tail tag and set memory monitor !!! R2 - old tail !!! */ ldrex r2, [r0, #__cpp(offsetof(nrf_atfifo_t, tail))] /* Extract write position !!! R3 !!! */ uxth r3, r2 /* Increment address with overload support !!! R4 used temporary !!! */ ldrh r4, [r0, #__cpp(offsetof(nrf_atfifo_t, item_size))] add r3, r4 ldrh r4, [r0, #__cpp(offsetof(nrf_atfifo_t, buf_size))] cmp r3, r4 it hs subhs r3, r3, r4 /* Check if FIFO would overload after making this increment !!! R4 used temporary !!! */ ldrh r4, [r0, #__cpp(offsetof(nrf_atfifo_t, head) + offsetof(nrf_atfifo_postag_pos_t, wr))] cmp r3, r4 ittt eq clrexeq moveq r0, #__cpp(false) beq nrf_atfifo_wspace_req_exit /* Pack everything back !!! R3 - new tail !!! */ /* Copy lower byte from new_tail, and higher byte is a value from the top of old_tail */ pkhbt r3, r3, r2 /* Store new value clearing memory monitor !!! R4 used temporary !!! */ strex r4, r3, [r0, #__cpp(offsetof(nrf_atfifo_t, tail))] cmp r4, #0 bne nrf_atfifo_wspace_req_repeat /* Return true */ mov r0, #__cpp(true) nrf_atfifo_wspace_req_exit /* Save old tail */ str r2, [r1] pop {r4, r5} bx lr } __ASM void nrf_atfifo_wspace_close(nrf_atfifo_t * const p_fifo) { /* Registry usage: * R0 - p_fifo * R1 - internal temporary register * R2 - new_tail */ nrf_atfifo_wspace_close_repeat ldrex r2, [r0, #__cpp(offsetof(nrf_atfifo_t, tail))] /* Copy from lower byte to higher */ pkhbt r2, r2, r2, lsl #16 strex r1, r2, [r0, #__cpp(offsetof(nrf_atfifo_t, tail))] cmp r1, #0 bne nrf_atfifo_wspace_close_repeat bx lr } __ASM bool nrf_atfifo_rspace_req(nrf_atfifo_t * const p_fifo, nrf_atfifo_postag_t * const p_old_head) { /* Registry usage: * R0 - p_fifo * R1 - p_old_head * R2 - internal variable old_head (saved by caller) * R3 - internal variable new_head (saved by caller) * R4 - internal temporary register (saved by this function) * R5 - not used stored to keep the stack aligned to 8 bytes * Returned value: * R0 (bool - 32 bits) */ push {r4, r5} nrf_atfifo_rspace_req_repeat /* Load tail tag and set memory monitor !!! R2 - old tail !!! */ ldrex r2, [r0, #__cpp(offsetof(nrf_atfifo_t, head))] /* Extract read position !!! R3 !!! */ uxth r3, r2, ror #16 /* Check if we have any data !!! R4 used temporary !!! */ ldrh r4, [r0, #__cpp(offsetof(nrf_atfifo_t, tail) + offsetof(nrf_atfifo_postag_pos_t, rd))] cmp r3, r4 ittt eq clrexeq moveq r0, #__cpp(false) beq nrf_atfifo_rspace_req_exit /* Increment address with overload support !!! R4 used temporary !!! */ ldrh r4, [r0, #__cpp(offsetof(nrf_atfifo_t, item_size))] add r3, r4 ldrh r4, [r0, #__cpp(offsetof(nrf_atfifo_t, buf_size))] cmp r3, r4 it hs subhs r3, r3, r4 /* Pack everything back !!! R3 - new tail !!! */ /* Copy lower byte from old_head, and higher byte is a value from write_pos */ pkhbt r3, r2, r3, lsl #16 /* Store new value clearing memory monitor !!! R4 used temporary !!! */ strex r4, r3, [r0, #__cpp(offsetof(nrf_atfifo_t, head))] cmp r4, #0 bne nrf_atfifo_rspace_req_repeat /* Return true */ mov r0, #__cpp(true) nrf_atfifo_rspace_req_exit /* Save old head */ str r2, [r1] pop {r4, r5} bx lr } __ASM void nrf_atfifo_rspace_close(nrf_atfifo_t * const p_fifo) { /* Registry usage: * R0 - p_fifo * R1 - internal temporary register * R2 - new_tail */ nrf_atfifo_rspace_close_repeat ldrex r2, [r0, #__cpp(offsetof(nrf_atfifo_t, head))] /* Copy from higher byte to lower */ pkhtb r2, r2, r2, asr #16 strex r1, r2, [r0, #__cpp(offsetof(nrf_atfifo_t, head))] cmp r1, #0 bne nrf_atfifo_rspace_close_repeat bx lr } __ASM bool nrf_atfifo_space_clear(nrf_atfifo_t * const p_fifo) { /* Registry usage: * R0 - p_fifo as input, bool output after * R1 - tail, rd pointer, new_head * R2 - head_old, destroyed when creating new_head * R3 - p_fifo - copy */ mov r3, r0 nrf_atfifo_space_clear_repeat /* Load old head in !!! R2 register !!! and read pointer of tail in !!! R1 register !!! */ ldrex r2, [r3, #__cpp(offsetof(nrf_atfifo_t, head))] ldrh r1, [r3, #__cpp(offsetof(nrf_atfifo_t, tail) + offsetof(nrf_atfifo_postag_pos_t, rd))] cmp r2, r2, ror #16 /* Return false as default */ mov r0, #__cpp(false) /* Create new head in !!! R1 register !!! Data in !!! R2 register broken !!! */ itett ne uxthne r2, r2 orreq r1, r1, r1, lsl #16 orrne r1, r2, r1, lsl #16 /* Skip header test */ bne nrf_atfifo_space_clear_head_test_skip /* Load whole tail and test it !!! R2 used !!! */ ldr r2, [r3, #__cpp(offsetof(nrf_atfifo_t, tail))] cmp r2, r2, ror #16 /* Return true if equal */ it eq moveq r0, #__cpp(true) nrf_atfifo_space_clear_head_test_skip /* Store and test if success !!! R2 used temporary !!! */ strex r2, r1, [r3, #__cpp(offsetof(nrf_atfifo_t, head))] cmp r2, #0 bne nrf_atfifo_space_clear_repeat bx lr } #elif defined ( __ICCARM__ ) || defined ( __GNUC__ ) bool nrf_atfifo_wspace_req(nrf_atfifo_t * const p_fifo, nrf_atfifo_postag_t * const p_old_tail) { volatile bool ret; volatile uint32_t old_tail; uint32_t new_tail; uint32_t temp; __ASM volatile( /* For more comments see Keil version above */ "1: \n" " ldrex %[old_tail], [%[p_fifo], %[offset_tail]] \n" " uxth %[new_tail], %[old_tail] \n" " \n" " ldrh %[temp], [%[p_fifo], %[offset_item_size]] \n" " add %[new_tail], %[temp] \n" " ldrh %[temp], [%[p_fifo], %[offset_buf_size]] \n" " cmp %[new_tail], %[temp] \n" " it hs \n" " subhs %[new_tail], %[new_tail], %[temp] \n" " \n" " ldrh %[temp], [%[p_fifo], %[offset_head_wr]] \n" " cmp %[new_tail], %[temp] \n" " ittt eq \n" " clrexeq \n" " moveq %[ret], %[false_val] \n" " beq.n 2f \n" " \n" " pkhbt %[new_tail], %[new_tail], %[old_tail] \n" " \n" " strex %[temp], %[new_tail], [%[p_fifo], %[offset_tail]] \n" " cmp %[temp], #0 \n" " bne.n 1b \n" " \n" " mov %[ret], %[true_val] \n" "2: \n" : /* Output operands */ [ret] "=r"(ret), [temp] "=&r"(temp), [old_tail]"=&r"(old_tail), [new_tail]"=&r"(new_tail) : /* Input operands */ [p_fifo] "r"(p_fifo), [offset_tail] "J"(offsetof(nrf_atfifo_t, tail)), [offset_head_wr] "J"(offsetof(nrf_atfifo_t, head) + offsetof(nrf_atfifo_postag_pos_t, wr)), [offset_item_size]"J"(offsetof(nrf_atfifo_t, item_size)), [offset_buf_size] "J"(offsetof(nrf_atfifo_t, buf_size)), [true_val] "I"(true), [false_val] "I"(false) : /* Clobbers */ "cc"); p_old_tail->tag = old_tail; UNUSED_VARIABLE(new_tail); UNUSED_VARIABLE(temp); return ret; } void nrf_atfifo_wspace_close(nrf_atfifo_t * const p_fifo) { uint32_t temp; uint32_t new_tail; __ASM volatile( /* For more comments see Keil version above */ "1: \n" " ldrex %[new_tail], [%[p_fifo], %[offset_tail]] \n" " pkhbt %[new_tail],%[new_tail], %[new_tail], lsl #16 \n" " \n" " strex %[temp], %[new_tail], [%[p_fifo], %[offset_tail]] \n" " cmp %[temp], #0 \n" " bne.n 1b \n" : /* Output operands */ [temp] "=&r"(temp), [new_tail] "=&r"(new_tail) : /* Input operands */ [p_fifo] "r"(p_fifo), [offset_tail] "J"(offsetof(nrf_atfifo_t, tail)) : /* Clobbers */ "cc"); UNUSED_VARIABLE(temp); UNUSED_VARIABLE(new_tail); } bool nrf_atfifo_rspace_req(nrf_atfifo_t * const p_fifo, nrf_atfifo_postag_t * const p_old_head) { volatile bool ret; volatile uint32_t old_head; uint32_t new_head; uint32_t temp; __ASM volatile( /* For more comments see Keil version above */ "1: \n" " ldrex %[old_head], [%[p_fifo], %[offset_head]] \n" " uxth %[new_head], %[old_head], ror #16 \n" " \n" " ldrh %[temp], [%[p_fifo], %[offset_tail_rd]] \n" " cmp %[new_head], %[temp] \n" " ittt eq \n" " clrexeq \n" " moveq %[ret], %[false_val] \n" " beq.n 2f \n" " \n" " ldrh %[temp], [%[p_fifo], %[offset_item_size]] \n" " add %[new_head], %[temp] \n" " ldrh %[temp], [%[p_fifo], %[offset_buf_size]] \n" " cmp %[new_head], %[temp] \n" " it hs \n" " subhs %[new_head], %[new_head], %[temp] \n" " \n" " pkhbt %[new_head], %[old_head], %[new_head], lsl #16 \n" " \n" " strex %[temp], %[new_head], [%[p_fifo], %[offset_head]] \n" " cmp %[temp], #0 \n" " bne.n 1b \n" " \n" " mov %[ret], %[true_val] \n" "2: \n" : /* Output operands */ [ret] "=r"(ret), [temp] "=&r"(temp), [old_head]"=&r"(old_head), [new_head]"=&r"(new_head) : /* Input operands */ [p_fifo] "r"(p_fifo), [offset_head] "J"(offsetof(nrf_atfifo_t, head)), [offset_tail_rd] "J"(offsetof(nrf_atfifo_t, tail) + offsetof(nrf_atfifo_postag_pos_t, rd)), [offset_item_size]"J"(offsetof(nrf_atfifo_t, item_size)), [offset_buf_size] "J"(offsetof(nrf_atfifo_t, buf_size)), [true_val] "I"(true), [false_val] "I"(false) : /* Clobbers */ "cc"); p_old_head->tag = old_head; UNUSED_VARIABLE(new_head); UNUSED_VARIABLE(temp); return ret; } void nrf_atfifo_rspace_close(nrf_atfifo_t * const p_fifo) { uint32_t temp; uint32_t new_head; __ASM volatile( /* For more comments see Keil version above */ "1: \n" " ldrex %[new_head], [%[p_fifo], %[offset_head]] \n" " pkhtb %[new_head],%[new_head], %[new_head], asr #16 \n" " \n" " strex %[temp], %[new_head], [%[p_fifo], %[offset_head]] \n" " cmp %[temp], #0 \n" " bne.n 1b \n" : /* Output operands */ [temp] "=&r"(temp), [new_head] "=&r"(new_head) : /* Input operands */ [p_fifo] "r"(p_fifo), [offset_head] "J"(offsetof(nrf_atfifo_t, head)) : /* Clobbers */ "cc"); UNUSED_VARIABLE(temp); UNUSED_VARIABLE(new_head); } bool nrf_atfifo_space_clear(nrf_atfifo_t * const p_fifo) { volatile bool ret; uint32_t old_head; /* This variable is left broken after assembly code finishes */ uint32_t new_head; __ASM volatile( "1: \n" " ldrex %[old_head], [%[p_fifo], %[offset_head]] \n" " ldrh %[new_head], [%[p_fifo], %[offset_tail_rd]] \n" " cmp %[old_head], %[old_head], ror #16 \n" " \n" " mov %[ret], %[false_val] \n" " \n" " itett ne \n" " uxthne %[old_head], %[old_head] \n" " orreq %[new_head], %[new_head], %[new_head], lsl #16 \n" " orrne %[new_head], %[old_head], %[new_head], lsl #16 \n" " \n" " bne.n 2f \n" " \n" " ldr %[old_head], [%[p_fifo], %[offset_tail]] \n" " cmp %[old_head], %[old_head], ror #16 \n" " it eq \n" " moveq %[ret], %[true_val] \n" " \n" "2: \n" " strex %[old_head], %[new_head], [%[p_fifo], %[offset_head]] \n" " cmp %[old_head], #0 \n" " bne.n 1b \n" : /* Output operands */ [ret] "=&r"(ret), [old_head] "=&r"(old_head), [new_head] "=&r"(new_head) : /* Input operands */ [p_fifo] "r"(p_fifo), [offset_head] "J"(offsetof(nrf_atfifo_t, head)), [offset_tail] "J"(offsetof(nrf_atfifo_t, tail)), [offset_tail_rd] "J"(offsetof(nrf_atfifo_t, tail) + offsetof(nrf_atfifo_postag_pos_t, rd)), [true_val] "I"(true), [false_val] "I"(false) : /* Clobbers */ "cc"); UNUSED_VARIABLE(old_head); UNUSED_VARIABLE(new_head); return ret; } #else #error Unsupported compiler #endif #endif /* NRF_ATFIFO_INTERNAL_H__ */