123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577 |
- /**
- * Copyright (c) 2011 - 2020, 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 <stddef.h>
- #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__ */
|