Перейти к содержанию
    

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

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 ---------------------------------------------------------------------

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ох нихрена-се наворочено. Мне 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];
    };
};

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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. Код реентерабельный, т.к. работает с внешними структурами данных.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

7 hours ago, KnightIgor said:

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

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

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

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

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 6/6/2021 at 10:05 PM, AVI-crak said:

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

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

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

13 minutes ago, KnightIgor said:

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

6 minutes ago, x893 said:

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

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


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

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

Изменено пользователем KnightIgor

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

17 минут назад, KnightIgor сказал:

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 minutes ago, jcxz said:

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

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

add r0,#1

str r0,[r1],

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 минуту назад, KnightIgor сказал:

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 minutes ago, jcxz said:

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

46 minutes ago, KnightIgor said:

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...