Jump to content

    
KnightIgor

CubeIDE: оптимизация выше -O0 нарушает работу программы

Recommended Posts

22 hours ago, aaarrr said:

Ну а bufio_* где искать?

Spoiler

/*------------------------------------------------------------------------------
 * Name:    HAL_BufIO.h
 * Purpose: buffered IO - support of interrupt driven IO over a serial device
 *
 * Version: V1.05
 *------------------------------------------------------------------------------
 * History:
 * 28.03.20 V1.05:  Extern definitions of symbols for USART driver macro
 * 22.08.14 V1.04:  Adds bufio_PUSH()
 * 22.07.12 V1.03:  Adds useful macros to init the _RING and _BUFFER items.
 * 20.10.11 V1.02:
 *                - Ring buffer can manage elements of length > char.
 *                - The interface to READ/WRITE has been changes, so
 *                  edit the modules using the functions.
 *                - _RingGET() added.
 *          V1.01 Char ring buffer support included
 *          V1.00 Initial Version
  *-----------------------------------------------------------------------------
 * ROE: Rules of engagement.
 *
 * The "bufio" library offers a FIFO-based interrupt driven I/O and a ring
 * buffer object (see below).
 *
 * The FIFO-based interrupt driven I/O is mostly intended for an easy
 * implementation of a char terminal via UART but not only.
 *
 * It is hardware independent but expects the common I/O hardware behavior
 * like "ready to send" and "data received" flags, etc. Moreover, the state
 * machine of the library assumes, a "ready" flag being set, the (re)enabling
 * of the correspondent interrupt triggers the exception. Fortunately, Cortex
 * and 8051 peripherals like UART or SPI have the expected behavior in this
 * aspect. Refer to manuals of other MCU architectures before implementing
 * the hardware dependent functions.
 *
 * 1).
 * The user code must implement seven hardware dependent handler functions.
 * If either transmission or reception is not needed, the correspondent user
 * functions (related three of them) can be skipped (see also 2). below).
 *
 * The function names below are just for example:
 *
 *  int16_t Get(void)           - reads a value from the peripheral data register
 *  void    Send(int16_t value) - sends a value via peripheral
 *
 *  uint8_t RX_Ready(void)      - returns >0 if a value is available
 *  uint8_t TX_Ready(void)      - returns >0 if a peripheral is ready to send
 *  uint8_t TC_Ready(void)      - returns >0 if sending is complete
 *
 *  void    RXIE(uint8_t enable) - controls the "RX ready" interrupt flag,
 *  void    TXIE(uint8_t enable) - controls the "TX ready" interrupt flag
 *                                  depending on the "enable" value:
 *                                      enable > 0 - an interrupt to enable,
 *                                      enable ==0 - an interrupt to disable.
 *
 *  Examples of the implementation (ATMEL SAM3U Cortex M3, UART):
 *
 *  void RXIE(uint8_t enable)
 *  {
 *    if (enable) UART->UART_IER = UART_IER_RXRDY;   // enable RX interrupt
 *    else        UART->UART_IDR = UART_IER_RXRDY;   // disable RX interrupt
 *  }
 *
 *  uint8_t RX_Ready(void)
 *  {
 *    return !!(UART->UART_SR & UART_SR_RXRDY);   // return flag RXRDY
 *  }
 *
 *  etc...
 *
 * If reading or writing of a data register does not clear the corresponding
 * "ready" flags (like UART in 8051), they must be explicitly cleared
 * according to the peripheral features while handling the data register.
 * For 8051's UART it can look like:
 *
 *  void  Send(int16_t value)
 *  {
 *      TI   = 0;       // prior clear transmitter interrupt/ready flag
 *      SBUF = Value;   // send the value.
 *  }
 *
 * Avoid clearing the flags in "RX_Ready" or "TX_Ready" functions because
 * the flags can be expected pending for some state machine cases.
 *
 * 2).
 * A structure of type "bufio_DRIVER" must be declared, and the
 * implemented handler functions listed above must be assigned to its fields
 * (watch the order!).
 *
 *   const  bufio_DRIVER U_Drv = { RXIE,       // controls RX interrupt flag
 *                                 TXIE,       // controls TX interrupt flag
 *                                 RX_Ready,   // returns RX ready flag
 *                                 TX_Ready,   // returns TX ready flag
 *                                 TC_Ready,   // returns TC ready flag
 *                                 Read_RX,    // returns a value
 *                                 Write_TX    // sends a value
 *                               };
 *
 * If either transmission or reception only is required, another fields
 * must contain NULL.
 *
 * 3).
 * A FIFO variable of "bufio_FIFO" type must be declared.
 *
 * The FIFO can keep byte- or word-wide items. It depends on the declaration
 * of the attached buffer. The field "size" of the "bufio_BUFFER" structure
 * must be set to either 0/1 for the byte-wide or to 2 for the word-wide data.
 * The FIFO item data width can be set for transmitter and receiver buffers
 * independently.
 *
 * Example of the byte FIFO implementation:
 *
 *  char TXBuffer1[BufferDepth];           // TX buffer, byte-wide items
 *  bufio_FIFO FIFO1 = {
 *      {0,0,0,           1,0,NULL},       // no RX,
 *      {0,0,BufferDepth, 1,0,TXBuffer1},  // TX only, byte-wide items
 *      &U_Drv                             // Serial driver
 *  };
 *
 * Refer to "bufio_BUFFER" structure to see the field order.
 *
 * Above, only TX will be supported, the "bufio_BUFFER" structure for TX
 * field within the FIFO1 variable is initialized with the appropriate
 * values like BufferSize and TXBuffer1 pointer; the last field contains
 * the pointer to the bufio driver structure. Surely, the implementations
 * of the reception related user functions can be omitted here.
 *
 * 4).
 * The hardware dependent interrupt handler should redirect to the bufio
 * handler as follows (example for ATMEL SAM3U Cortex M3, UART):
 *
 *  void UART_IRQHandler(void)
 *  {
 *      bufio_IRQHandler(&FIFO1);
 *  }
 *
 * 5).
 * The published functions like "bufio_ReadChar" and "bufio_WriteChar" can
 * then be used to receive and send the data.
 *
 * Due to no static data, all the functions are reenterable and can process
 * several I/O devices via their own FIFO structures, of course.
 *
 *
 * RING:
 *
 * The Ring is a set of functions that provides a purely software driven FIFO
 * to manage elements that are merely buffers of an equal size(!) described
 * by the bufio_RING structure. The functions are reentrant.
 *
 * To create a ring, allocate and initialize the bufio_RING structure those
 * fields are commented below.
 *
 * Example of a char FIFO ring:
 * ----------------------------
 *
 * // allocation
 * char buf[DEPTH];
 * bufio_RING a_ring = {0, 0, DEPTH, sizeof(char), buf};
 *
 * // alternatively, using the macro:
 * bufio_RING a_ring = BUFIO_RING_INIT(DEPTH, sizeof(char), buf);
 *
 * // usage
 * ...
 * char c = value;
 * ...
 * bufio_RingPUT(&a_ring, &c);
 * ...
 * if (bufio_RingGET(&a_ring, &c))
 * {
 *    // do job
 * }
 *
 * Example of a common data storage ring:
 * -------------------------------------------
 *
 * // allocation
 * unsigned char buf[DEPTH];
 * bufio_RING a_ring = {0, 0, DEPTH, SIZE, *buf};
 *
 * // alternatively, using the macro:
 * bufio_RING a_ring = BUFIO_RING_INIT(DEPTH, SIZE, *buf);
 *
 * // usage
 * ...
 * unsigned char s;
 * ...
 * ... data in s are available...
 * ...
 * bufio_RingPUT(&a_ring, s);
 * ...
 * unsigned char s;
 * if (bufio_RingGET(&a_ring, s))
 * {
 *      // process s
 * }
 *
 *----------------------------------------------------------------------------*/
//
#include <stdint.h>
#include <string.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifndef _HAL_BUFIO_H
#define _HAL_BUFIO_H

#ifndef    CHAR_CR
#define CHAR_CR     '\r'
#endif
#ifndef    CHAR_LF
#define CHAR_LF        '\n'
#endif

#ifndef __STATIC_INLINE
#define __STATIC_INLINE static __inline
#endif

// -----------------------------------------------------------------------------

extern int sendchar (int ch);    // for redirect.c
extern int getkey   (void);        // for redirect.c
extern int bufio_BreakCounter;

// -----------------------------------------------------------------------------
//
//  Type definition set for the user functions
//
typedef void    (*vpLogicSet)(uint8_t  enable);
typedef uint8_t (*vpLogicGet)(void);
typedef void    (*vpValueSet)(uint16_t value);
typedef int32_t (*vpValueGet)(void);

// -----------------------------------------------------------------------------
//
//      >>> RING <<<
//
typedef struct _bufio_ring {

  void   *data;     // data array pointer
  int16_t idx;      // work write index
  int16_t cnt;      // number of elements
  int16_t depth;    // the ring buffer depth
  int16_t size;     // the data item size

} bufio_RING;
#define BUFIO_RING_INIT(d,s,b)  {b, 0, 0, d, s}
#define BUFIO_RING_OF(a)    \
        BUFIO_RING_INIT(sizeof(a)/sizeof(a[0]), sizeof(a[0]), a)
// -----------------------------------------------------------------------------
//
//      >>> FIFO <<<
//
typedef struct _bufio_buffer {

    void    *data;  // pointer to the data buffer
    int16_t  idx;   // index of the item in the buffer
    int16_t  cnt;   // number of items in the buffer
    int16_t  depth; // capacity of the buffer
    int16_t  size;  // data size of the buffer items
    uint8_t  flag;  // internal critical section flag

} bufio_BUFFER;     // like bufio_RING but having a mutex flag
#define BUFIO_BUFFER_INIT(d,s,b)    {b, 0, 0, d, s, 0}
#define BUFIO_BUFFER(buf) BUFIO_BUFFER_INIT(sizeof(buf),sizeof(buf[0]),buf)

typedef const struct bufio_driver {

    vpLogicSet      RIE;   // RX interrupt enable/disable
    vpLogicSet      TIE;   // TX interrupt enable/disable
    vpLogicGet      RI;    // RX flag acquire
    vpLogicGet      TI;    // TX flag acquire
    vpLogicGet      TC;    // TC flag acquire
    vpValueGet      Get;   // get a value
    vpValueSet      Send;  // send a value

} bufio_DRIVER;

typedef struct bufio_fifo {

    bufio_BUFFER    RX;    // input FIFO buffer
    bufio_BUFFER    TX;    // output FIFO buffer
    bufio_DRIVER*   HD;    // pointer to driver

} bufio_FIFO;

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//                           >>> RING support <<<
//
__STATIC_INLINE void bufio_RingRESET(bufio_RING *ring)
{
    if (ring)
        ring->idx =
        ring->cnt = 0;
}
// -----------------------------------------------------------------------------

__STATIC_INLINE void bufio_RingCLEAR(bufio_RING *ring)
{
    if (ring && ring->data)
        memset(ring->data, 0, ring->size * ring->depth);
}
// -----------------------------------------------------------------------------
//
//  Returns the number of occupied elements in the ring.
//
__STATIC_INLINE unsigned int bufio_RingCOUNT(bufio_RING *ring)
{
    return (ring ? ring->cnt : 0);
}
// -----------------------------------------------------------------------------
//
//  Returns TRUE (!=0) if the ring has no more room.
//
__STATIC_INLINE unsigned char bufio_RingFULL(bufio_RING *ring)
{
    return (ring ? (ring->cnt >= ring->depth) : 0);
}

// -----------------------------------------------------------------------------
//
//  Returns a pointer to the stored element of the ring indexed by num.
//  or NULL if the ring is empty or num is out of range.
//
void *bufio_RingPEEK(bufio_RING *ring, int num);

// -----------------------------------------------------------------------------
//
//  Returns a pointer to the stored top (first, oldest) element of the ring
//  (without changing the ring) or NULL if the ring is empty.
//
//  The call to RingTOP() allows to get access to and process the element data
//  buffer without copying the data to any external buffer; the following
//  call to bufio_RingGET(ring, NULL) removes then the element from the ring.
//
//  Dealing with the first element, nothing else but FIFO is implemented!
//
void *bufio_RingTOP(bufio_RING *ring);
#define bufio_RingFIRST(r)  bufio_RingTOP(r)

// -----------------------------------------------------------------------------
//
//  Returns a pointer to the stored bottom (lately added) element of the ring
//  (without changing the ring) or NULL if the ring is empty.
//
//  The call to RingBOTTOM() allows to get access to and process the element
//  data buffer without copying the data to any external buffer; the following
//  call to bufio_RingPOP(ring, NULL) removes then the element from the ring.
//
//  Dealing with the last element, nothing else but STACK is implemented!
//
void *bufio_RingBOTTOM(bufio_RING *ring);
#define bufio_RingLAST(r)  bufio_RingBOTTOM(r)

// -----------------------------------------------------------------------------
//
//  Gets the top (first, oldest) element from the ring into the buffer *c.
//  If c == NULL, just removes the top element from the ring.
//  Returns FALSE (==0) if no elements in the ring (ring is empty).
//
//  Dealing with the first element, nothing else but FIFO is implemented!
//
unsigned char bufio_RingGET(bufio_RING *ring, void *c);

// -----------------------------------------------------------------------------
//
//  Gets the bottom (lately added) element from the ring into the buffer *c.
//  If c == NULL, just removes the bottom element from the ring.
//  Returns FALSE (==0) if no elements in the ring (ring empty).
//
//  Dealing with the last element, nothing else but STACK is implemented!
//
unsigned char bufio_RingPOP(bufio_RING *ring, void *c);

// -----------------------------------------------------------------------------
//
//  Appends (copies) an element to the ring from the provided buffer *c.
//  Returns a pointer onto it or NULL (==0) if no more room has been available.
//
//  If c == NULL, no data are copied but the element is appended to the ring.
//  The returned pointer onto the buffer can then be used to access
//  the buffer. This approach can be helpful to reuse the already allocated
//  (and not additional external) data space as a storage.
//
void *bufio_RingPUT(bufio_RING *ring, const void *c);

// -----------------------------------------------------------------------------
//
//  Places (copies) an element in the ring from the provided buffer *c.
//  Returns a pointer onto it or NULL (==0) if no more room has been available.
//
//  It places the data on the TOP.
//
//  If c == NULL, no data are copied but the element is added to the ring.
//  The returned pointer onto the buffer can then be used to access
//  the buffer. This approach can be helpful to reuse the already allocated
//  (and not additional external) data space as a storage.
//
void *bufio_RingPUSH(bufio_RING *ring, const void *c);

// -----------------------------------------------------------------------------
//
//  Compares an element given by num with the provided buffer *c.
//  Returns >0 if equal else 0 (also no data/wrong num).
//
unsigned char bufio_RingCOMP(bufio_RING *ring, const void *c, int num);

// -----------------------------------------------------------------------------
//
//  Searches in the ring for an element provided by buffer *c.
//  Returns the number of the found element [0..count-1] or < 0 if none.
//
int bufio_RingFIND(bufio_RING *ring, const void *c);

// -----------------------------------------------------------------------------
//
//  Deletes in the ring an element given by num
//  Returns >0 if the element has been deleted.
//
unsigned char bufio_RingDEL(bufio_RING *ring, int num);

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
//                           >>> FIFO support <<<
//
//  Returns the amount of bytes actually stored in the input (PC -> Device) FIFO
//
int16_t bufio_RXBufferLevel   (bufio_FIFO *FIFO);

// -----------------------------------------------------------------------------
//
//  Returns a number of bytes still available in the output (Device -> PC) FIFO
//
int16_t bufio_TXBufferCapacity(bufio_FIFO *FIFO);

// -----------------------------------------------------------------------------
//
//  Returns true if the transmission is completed
//
unsigned char bufio_TXComplete(bufio_FIFO *FIFO);

// -----------------------------------------------------------------------------
//
//  Flushes the input (PC -> Device) FIFO
//
void bufio_PurgeRX(bufio_FIFO *FIFO);

// -----------------------------------------------------------------------------
//
//  Cancels the output (Device -> PC) FIFO
//
void bufio_PurgeTX(bufio_FIFO *FIFO);

// -----------------------------------------------------------------------------
//
//    Processes a received value before placing it into the FIFO buffer.
//  Due to the weak declaration can be reimplemented by the user.
//
uint16_t bufio_InputFilter(uint16_t c);

// -----------------------------------------------------------------------------
//
//    Stores a value in the software driven input (PC -> Device) FIFO buffer.
//  Returns -1 if no more place in the FIFO.
//
//  CALLED FROM THE INTERRUPT HANDLER or an external routine that wanna inject
//                                       a value
//
int32_t bufio_StoreChar(bufio_FIFO *FIFO, uint16_t c);

// -----------------------------------------------------------------------------
//
//    Places a value to the software driven output (Device -> PC) FIFO buffer
//  and starts the transmission by (re)enabling the Device interrupt.
//  Returns -1 if no place in the buffer more.
//
int32_t bufio_WriteChar(bufio_FIFO *FIFO, uint16_t c);

// -----------------------------------------------------------------------------
//
//    Reads out a value from the input (PC -> Device) FIFO buffer.
//  returns -1 if empty.
//
//    Intended for an external routine that processes the input stream.
//
int32_t bufio_ReadChar(bufio_FIFO *FIFO);

// -----------------------------------------------------------------------------
//
//    Returns the first value from the input (PC -> Device) FIFO buffer without
//  destroying the FIFO or returns -1 if empty.
//
int32_t bufio_PeepChar(bufio_FIFO * FIFO);

// -----------------------------------------------------------------------------
//
//    To be called from the Device IRQ handler
//
void bufio_IRQHandler(bufio_FIFO *FIFO);

// -----------------------------------------------------------------------------
//
//  Prepares the bufio
//
void bufio_Init(bufio_FIFO *FIFO);

/* -----------------------------------------------------------------------------
 *
 *  The help macro to define the USART driver buffers and the FIFO structure
 */
#define DEFINE_USART_BUFFERS(FIFONAME, RXSIZE, TXSIZE) \
static const  bufio_DRIVER \
    U_Drv = {RXIE,     \
             TXIE,     \
             RX_Ready, \
             TX_Ready, \
             TX_Complete, \
             Read_RX,  \
             Write_TX  \
            }; \
\
static unsigned char rxBuffer[RXSIZE], \
                     txBuffer[TXSIZE]; \
static bufio_FIFO \
             FIFONAME = {BUFIO_BUFFER(rxBuffer), \
                         BUFIO_BUFFER(txBuffer), \
                         &U_Drv}

/* -----------------------------------------------------------------------------
 *
 *  The help macro below allows to define an USART driver.
 *  The macro needs further macros like USART_RXIE_SET, etc.,
 *  that process the USART registers. Mostly, those macros are
 *  defined specifically for each platform (CPU) in a "xxxx_proc.h"
 *  file situated in HAL.
 *
 *  USARTN is a CMSIS pointer to the peripheral (like USART1)
 *  FIFONAME is a name of the driver FIFO. Mostly just 'FIFO'.
 *  RXSIZE and TXSIZE are the depths of the FIFOs for TX and RX.
 */
#ifndef USART_FLAG_XE
#define USART_FLAG_XE USART_FLAG_FE
#endif

#ifndef USART_STATE_CLEAR
#define USART_STATE_CLEAR
#endif

#define DEFINE_USART_DRIVER(USARTN, FIFONAME, RXSIZE, TXSIZE) \
__attribute__((weak)) void FIFONAME##_Break(void) {} \
\
static void RXIE(uint8_t enable) \
{ \
    if (enable) USART_RXIE_SET  (USARTN); \
    else        USART_RXIE_CLEAR(USARTN); \
} \
static void TXIE(uint8_t enable) \
{ \
    if (enable) USART_TXIE_SET  (USARTN); \
    else        USART_TXIE_CLEAR(USARTN); \
} \
static uint8_t RX_Ready(void) \
{ \
    return USART_RI_SR(USARTN); \
} \
static uint8_t TX_Ready(void) \
{ \
    return USART_TI_SR(USARTN); \
} \
static uint8_t TX_Complete(void) \
{ \
    return USART_TC_SR(USARTN); \
} \
static void Write_TX(uint16_t value) \
{ \
    USART_PUT_DR(USARTN, value); \
} \
static int32_t Read_RX(void); \
DEFINE_USART_BUFFERS(FIFONAME, RXSIZE, TXSIZE); \
\
static int32_t Read_RX(void) \
{ \
    uint16_t st = USART_GET_SR(USARTN) & USART_RX_FLAGS; \
    int32_t  dr = USART_GET_DR(USARTN); \
    if (st) \
    { \
        USART_STATE_CLEAR; \
        if ((st & (USART_FLAG_XE)) && dr == 0) \
        { \
            bufio_PurgeRX(&FIFONAME); \
            FIFONAME##_Break(); \
        } \
    } \
    return dr; \
}
// Unbelievable but just reading SR before in IRQ
// helps not to lose bytes. Found on 28.04.2014.

#define DEFINE_USART_DRIVER_IRQ(USARTN, FIFONAME, RXSIZE, TXSIZE, VECTOR) \
DEFINE_USART_DRIVER(USARTN, FIFONAME, RXSIZE, TXSIZE); \
void VECTOR(void) \
{ \
    USART_GET_SR(USARTN); \
    bufio_IRQHandler(&FIFONAME); \
}

#ifdef __cplusplus
}
#endif

#endif

// --- EOF ---------------------------------------------------------------------

 

 

Share this post


Link to post
Share on other sites

Ох нихрена-се наворочено. Мне FIFO как-то по проще видится, уж во всяком случае без индекса.

#define fifo_size 256

union fifo
{
    struct in
    {
        const volatile uint16_t     tail;
        volatile uint16_t           head;
        volatile char               ptr[fifo_size];
    };
    struct out
    {
        volatile uint16_t     tail;
        const volatile uint16_t           head;
        volatile char               ptr[fifo_size];
    };
};

Всё очень просто: голова не может догнать хвост, хвост всегда догоняет голову.

Share this post


Link to post
Share on other sites
22 hours ago, AVI-crak said:

Ох нихрена-се наворочено. Мне FIFO как-то по проще видится, уж во всяком случае без индекса.


#define fifo_size 256

union fifo
{
    struct in
    {
        const volatile uint16_t     tail;
        volatile uint16_t           head;
        volatile char               ptr[fifo_size];
    };
    struct out
    {
        volatile uint16_t     tail;
        const volatile uint16_t           head;
        volatile char               ptr[fifo_size];
    };
};

Всё очень просто: голова не может догнать хвост, хвост всегда догоняет голову.

1. Существует две основные концепции построения кольцевых буферов: указатель и счетчик или два указателя. В первом случае счетчик сразу дает количество элементов в FIFO (мне было так удобней), но второй указатель надо вычислять; во втором случае указатели готовы всегда, но количество элементов надо каждый раз вычислять, если опрашивать. Эти концепции совершенно равноправны. Спорить по этому поводу равносильно тому, разбивать ли яйца с тупой или острой стороны.

2. У меня не просто FIFO, но с атомарным доступом, который достигается флагами и основан на свойстве прерываний в 8051 и CMx, когда их можно, скажем так, отложить. При этом не нужно запрещать/разрешать прерывания глобально ( __enable_irq(),  __disable_irq()). Эта реализация может показаться несколько навороченной, но она хорошо работает без влияния на другие подсистемы или на всю подсистему прерываний.

3. Код реентерабельный, т.к. работает с внешними структурами данных.

Share this post


Link to post
Share on other sites
7 hours ago, KnightIgor said:

Существует две основные концепции построения кольцевых буферов

Вот специально для полной атомарности придумали FIFO с двумя указателями, без блокирования прерываний танцев с бубном. Два потока могут общаться через fifo, даже если выполняются на разных ядрах, одновременно. А посчитать свободное место или остаток - можно в любое время с любой стороны, и при этом не будет ошибок!!! Причём на FIFO можно натравить дма, оно конечно не полностью автоматически получится - но телодвижений будет на пару порядков меньше.

Насчёт третьего пункта - не понимаю как и в чём оно должно вам помогать. FIFO - это не синхронный механизм связи, и уж точно не является объектом распространения зоны видимости, он всегда сам по себе - между двумя фиксированными потоками. Когда желающих больше - нужно либо больше разных FIFO, либо канальный уровень сверху, ну или арбитраж ресурсов. Но самого механизма FIFO - это вообще никак не касается, его дело простое - рулить буфером. 

Кстати я уже давно заметил, чем алгоритм проще - тем надёжнее работает. 

Share this post


Link to post
Share on other sites
8 часов назад, KnightIgor сказал:

1. Существует две основные концепции построения кольцевых буферов: указатель и счетчик или два указателя. В первом случае счетчик сразу дает количество элементов в FIFO (мне было так удобней), но второй указатель надо вычислять; во втором случае указатели готовы всегда, но количество элементов надо каждый раз вычислять, если опрашивать. Эти концепции совершенно равноправны.

Это неправда. Вариант с двумя указателями позволяет строить кольцевые буфера с одним писателем и одним читателем без дополнительных средств синхронизации потоков (типа запрета прерываний). Вариант со счётчиком - не позволяет. Если вы этого не знали, то тут как раз и может крыться причина багов.

8 часов назад, KnightIgor сказал:

2. У меня не просто FIFO, но с атомарным доступом, который достигается флагами и основан на свойстве прерываний в 8051 и CMx, когда их можно, скажем так, отложить. При этом не нужно запрещать/разрешать прерывания глобально ( __enable_irq(),  __disable_irq())

Ничего не понятно, белиберда какая-то.... :wacko2:

 

PS: Имхо конечно, но стиль оформления просто ужасный - исходный код не читаемый практически, какое-то нагромождение на ровном месте где не нужно... уж не говоря об оптимальности...

Share this post


Link to post
Share on other sites
On 6/6/2021 at 10:05 PM, AVI-crak said:

Вот специально для полной атомарности придумали FIFO с двумя указателями, без блокирования прерываний танцев с бубном. Два потока могут общаться через fifo, даже если выполняются на разных ядрах, одновременно. А посчитать свободное место или остаток - можно в любое время с любой стороны, и при этом не будет ошибок!!! Причём на FIFO можно натравить дма, оно конечно не полностью автоматически получится - но телодвижений будет на пару порядков меньше.

Насчёт третьего пункта - не понимаю как и в чём оно должно вам помогать. FIFO - это не синхронный механизм связи, и уж точно не является объектом распространения зоны видимости, он всегда сам по себе - между двумя фиксированными потоками. Когда желающих больше - нужно либо больше разных FIFO, либо канальный уровень сверху, ну или арбитраж ресурсов. Но самого механизма FIFO - это вообще никак не касается, его дело простое - рулить буфером. 

Кстати я уже давно заметил, чем алгоритм проще - тем надёжнее работает. 

Как двинуть указатель атомарно?

Share this post


Link to post
Share on other sites
6 minutes ago, x893 said:

А чем не устраивает disable/enable irq ?

Даже не смешно.


Я писал: "2. У меня не просто FIFO, но с атомарным доступом, который достигается флагами и основан на свойстве прерываний в 8051 и CMx, когда их можно, скажем так, отложить. При этом не нужно запрещать/разрешать прерывания глобально ( __enable_irq(),  __disable_irq()) ".

Конечно, чтобы двигать указатели/счетчик ВНЕ прерывания, я флаг устанавливаю, отмечая критическую секцию. В самом прерывании этот флаг анализируется и прерывание откладывается. Когда критическая секция завершается, она прерывание просто (пере-)разрешает. Происходит влет в прерывание и работа уже с новыми значениями указателей/счетчиков. То есть, запрет прерывания есть, но он происходит внутри самого прерывания, а не в синхронной части программы как бы извне.

Edited by KnightIgor

Share this post


Link to post
Share on other sites
17 минут назад, KnightIgor сказал:

Как двинуть указатель атомарно?

Зачем? Для случая если только один писатель и только один читатель, это не нужно.

Share this post


Link to post
Share on other sites
8 minutes ago, jcxz said:

Зачем? Для случая если только один писатель и только один читатель, это не нужно.

например, читает прерывание, а пишет синхронный цикл. Что будет, если синхронный цикл выполняет
ldr r0,[r1]

add r0,#1

str r0,[r1],

а прямо перед str рубануло прерывание, которое использует то, куда указывает [r1]?

 

Share this post


Link to post
Share on other sites
1 минуту назад, KnightIgor сказал:

а прямо перед str рубануло прерывание, которое использует то, куда указывает [r1]?

Нормально всё будет. Если правильно реализовать.

Share this post


Link to post
Share on other sites
8 minutes ago, jcxz said:

Нормально всё будет. Если правильно реализовать.

Вот я уже заинтригован. Это в 8051 была команда xch, которая атомарная. В кортексе такого нет.

Share this post


Link to post
Share on other sites
46 minutes ago, KnightIgor said:

Конечно, чтобы двигать указатели/счетчик ВНЕ прерывания, я флаг устанавливаю, отмечая критическую секцию.

Где все это в коде?

Share this post


Link to post
Share on other sites
34 минуты назад, KnightIgor сказал:

Вот я уже заинтригован. Это в 8051 была команда xch, которая атомарная. В кортексе такого нет.

Не нужны атомарные команды чтения-модификации-записи для кольцевого буфера с одним писателем и одним читателем. От слова совсем.

Ниже пример одной из реализаций универсальных Fifo у меня (ARM).

.h-файл:

Скрытый текст

class Fifo {
  protected:
  volatile u16 rpos, wpos;
  void *buf;
  u16 size;
  public:
  size_t noEmpty() const { size_t i = wpos; return rpos - i; }
  size_t noFull() const;
  size_t cnt() const;
  size_t free() const;
  void purge() { rpos = wpos; }
  void ini(void *b, size_t siz) { buf = b; size = siz; rpos = wpos = 0; }
  Fifo(void *b, size_t siz) { ini(b, siz); }
  Fifo() {}
};

#define CreateClassFifo(clas, typ, typi, typo)                       \
class clas : public Fifo {                                           \
  public:                                                            \
  size_t write(typi);                                                \
  typo read();                                                       \
  typo first() const {                                               \
    size_t p = rpos;                                                 \
    return (p == wpos) ? (typo)-1: (typo)((typ *)buf)[p]; }          \
  clas(typ *b, size_t siz) : Fifo(b, siz) {}                         \
  clas() {}                                                          \
}

CreateClassFifo(Fifo8, u8, s32, s32);
CreateClassFifo(Fifo16, u16, s32, s32);
CreateClassFifo(Fifo32, u32, s32, s64);
CreateClassFifo(Fifo64, u64, s64, s64);

 

.cpp файл:

Скрытый текст

size_t Fifo::noFull() const
{
  size_t i;
  if (!(i = wpos)) i = size;
  return i - 1 - rpos;
}

size_t Fifo::cnt() const
{
  int i = rpos;
  if ((i -= wpos) < 0) i += size;
  return i;
}

size_t Fifo::free() const
{
  int i = wpos;
  if ((i -= rpos) <= 0) i += size;
  return i - 1;
}

//Шаблон создания класса очереди
#define FifoReadWrite(clas, typ, typi, typo) \
typo clas::read()                            \
{                                            \
  typo i;                                    \
  size_t j = rpos;                           \
  if (i = j - wpos) {                        \
    i = ((typ *)buf)[j] + 1;                 \
    rpos = ((j) ? j: size) - 1;              \
  }                                          \
  return i - 1;                              \
}                                            \
size_t clas::write(typi c)                   \
{                                            \
  size_t i, i1, i2;                          \
  if (!(i1 = i2 = wpos)) i1 = size;          \
  if (i = (--i1 - rpos)) {                   \
    ((typ *)buf)[i2] = c;                    \
    wpos = i1;                               \
  }                                          \
  return i;                                  \
}

FifoReadWrite(Fifo8, u8, s32, s32)
FifoReadWrite(Fifo16, u16, s32, s32)
FifoReadWrite(Fifo32, u32, s32, s64)
FifoReadWrite(Fifo64, u64, s64, s64)

 

Использование:

//создание объекта Fifo для (100-1) 8-разрядных элементов
#define ncell(m) (sizeof(m) / sizeof((m)[0]))
static u8 fifoBuf[100];
Fifo8 fifoQ(fifoBuf, ncell(fifoBuf));

//запись-чтение
fifoQ.write(x);
int x = fifoQ.read();

 

PS: Атомарные здесь должны быть только операции сохранения индексов rpos, wpos. А они в ARM атомарны для типов u8/u16/u32.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.