#include "modbus_data_store.h" #include #if defined(PLSR_HOST_TEST) #define MODBUS_DATA_BARRIER() __sync_synchronize() #else #include "stm32f4xx.h" #define MODBUS_DATA_BARRIER() __DMB() #endif #define MODBUS_DATA_SRAM_WORD_COUNT (20000UL) #define MODBUS_DATA_CCM_WORD_COUNT (29999UL) #define MODBUS_DATA_HD_SRAM_OFFSET (10000UL) #define MODBUS_DATA_LINEAR_CCM_BASE (40000UL) #if (MODBUS_DATA_X_BIT_COUNT != MODBUS_DATA_M_BIT_COUNT) \ || (MODBUS_DATA_X_BIT_COUNT != MODBUS_DATA_HM_BIT_COUNT) #error "Packed X/M/HM images require equal configured capacities" #endif #define MODBUS_DATA_BIT_BYTES ((MODBUS_DATA_X_BIT_COUNT + 7UL) / 8UL) static uint16_t ModbusDataSram[MODBUS_DATA_SRAM_WORD_COUNT]; #if !defined(PLSR_HOST_TEST) #pragma location = ".ccmram" #pragma data_alignment = 4 __root #endif static uint16_t ModbusDataCcm[MODBUS_DATA_CCM_WORD_COUNT]; static uint8_t ModbusBitImages[3U][MODBUS_DATA_BIT_BYTES]; static volatile uint32_t ModbusDataWriteSequence; static volatile uint32_t ModbusDataWriteFirstAddress; static volatile uint32_t ModbusDataWriteWordCount; static volatile MODBUS_DATA_DEVICE ModbusDataWriteDevice; static uint32_t ModbusDataEnterShortCritical(void) { #if defined(PLSR_HOST_TEST) return 0UL; #else uint32_t interruptState; interruptState = __get_PRIMASK(); __disable_irq(); return interruptState; #endif } static void ModbusDataExitShortCritical(uint32_t interruptState) { #if defined(PLSR_HOST_TEST) (void)interruptState; #else if (interruptState == 0UL) { __enable_irq(); } #endif } static uint8_t ModbusDataResolve(MODBUS_DATA_DEVICE device, uint32_t address, uint16_t **word) { if (word == NULL) { return 0U; } switch (device) { case MODBUS_DATA_DEVICE_D: if (address >= MODBUS_DATA_D_WORD_COUNT) { return 0U; } *word = &ModbusDataSram[address]; return 1U; case MODBUS_DATA_DEVICE_HD: if (address >= MODBUS_DATA_HD_WORD_COUNT) { return 0U; } *word = &ModbusDataSram[MODBUS_DATA_HD_SRAM_OFFSET + address]; return 1U; case MODBUS_DATA_DEVICE_FD: if (address >= MODBUS_DATA_FD_WORD_COUNT) { return 0U; } *word = &ModbusDataCcm[address]; return 1U; default: return 0U; } } static uint32_t ModbusDataCapacity(MODBUS_DATA_DEVICE device) { switch (device) { case MODBUS_DATA_DEVICE_D: return MODBUS_DATA_D_WORD_COUNT; case MODBUS_DATA_DEVICE_HD: return MODBUS_DATA_HD_WORD_COUNT; case MODBUS_DATA_DEVICE_FD: return MODBUS_DATA_FD_WORD_COUNT; default: return 0UL; } } static uint8_t ModbusDataWriteOverlaps(MODBUS_DATA_DEVICE device, uint32_t firstAddress, uint32_t wordCount) { uint32_t activeFirst; uint32_t activeCount; if (device != ModbusDataWriteDevice) { return 0U; } activeFirst = ModbusDataWriteFirstAddress; activeCount = ModbusDataWriteWordCount; if ((activeCount == 0UL) || (wordCount == 0UL)) { return 0U; } return ((firstAddress < (activeFirst + activeCount)) && (activeFirst < (firstAddress + wordCount))) ? 1U : 0U; } static void ModbusDataWriteBegin(MODBUS_DATA_DEVICE device, uint32_t firstAddress, uint32_t wordCount) { ModbusDataWriteDevice = device; ModbusDataWriteFirstAddress = firstAddress; ModbusDataWriteWordCount = wordCount; MODBUS_DATA_BARRIER(); ModbusDataWriteSequence++; MODBUS_DATA_BARRIER(); } static void ModbusDataWriteEnd(void) { MODBUS_DATA_BARRIER(); ModbusDataWriteSequence++; } uint8_t ModbusDataValidateWords(MODBUS_DATA_DEVICE device, uint32_t firstAddress, uint32_t wordCount) { uint32_t capacity; capacity = ModbusDataCapacity(device); if ((capacity == 0UL) || (wordCount == 0UL) || (firstAddress >= capacity)) { return 0U; } return (wordCount <= (capacity - firstAddress)) ? 1U : 0U; } uint8_t ModbusDataReadWord(MODBUS_DATA_DEVICE device, uint32_t address, uint16_t *value) { uint16_t *word; uint32_t before; uint32_t after; uint16_t snapshot; if ((value == NULL) || (ModbusDataResolve(device, address, &word) == 0U)) { return 0U; } before = ModbusDataWriteSequence; if (((before & 1UL) != 0UL) && (ModbusDataWriteOverlaps(device, address, 1UL) != 0U)) { return 0U; } MODBUS_DATA_BARRIER(); snapshot = *word; MODBUS_DATA_BARRIER(); after = ModbusDataWriteSequence; if ((before != after) && (ModbusDataWriteOverlaps(device, address, 1UL) != 0U)) { return 0U; } if (((after & 1UL) != 0UL) && (ModbusDataWriteOverlaps(device, address, 1UL) != 0U)) { return 0U; } *value = snapshot; return 1U; } uint8_t ModbusDataReadDword(MODBUS_DATA_DEVICE device, uint32_t lowAddress, int32_t *value) { uint16_t *lowWord; uint16_t *highWord; uint16_t lowSnapshot; uint16_t highSnapshot; uint32_t before; uint32_t after; if ((value == NULL) || (ModbusDataValidateWords(device, lowAddress, 2UL) == 0U) || (ModbusDataResolve(device, lowAddress, &lowWord) == 0U) || (ModbusDataResolve(device, lowAddress + 1UL, &highWord) == 0U)) { return 0U; } before = ModbusDataWriteSequence; if (((before & 1UL) != 0UL) && (ModbusDataWriteOverlaps(device, lowAddress, 2UL) != 0U)) { return 0U; } MODBUS_DATA_BARRIER(); lowSnapshot = *lowWord; highSnapshot = *highWord; MODBUS_DATA_BARRIER(); after = ModbusDataWriteSequence; if ((before != after) && (ModbusDataWriteOverlaps(device, lowAddress, 2UL) != 0U)) { return 0U; } if (((after & 1UL) != 0UL) && (ModbusDataWriteOverlaps(device, lowAddress, 2UL) != 0U)) { return 0U; } *value = (int32_t)(((uint32_t)highSnapshot << 16U) | lowSnapshot); return 1U; } uint8_t ModbusDataWriteWord(MODBUS_DATA_DEVICE device, uint32_t address, uint16_t value) { uint16_t *word; uint32_t interruptState; if (ModbusDataResolve(device, address, &word) == 0U) { return 0U; } interruptState = ModbusDataEnterShortCritical(); ModbusDataWriteBegin(device, address, 1UL); *word = value; ModbusDataWriteEnd(); ModbusDataExitShortCritical(interruptState); return 1U; } uint8_t ModbusDataWriteWords(MODBUS_DATA_DEVICE device, uint32_t firstAddress, const uint16_t *values, uint32_t wordCount) { uint16_t *firstWord; uint32_t index; uint32_t interruptState = 1UL; if ((values == NULL) || (ModbusDataValidateWords(device, firstAddress, wordCount) == 0U) || (ModbusDataResolve(device, firstAddress, &firstWord) == 0U)) { return 0U; } /* A live INT32 update is only two words. Keep that very short commit * indivisible to the 100us ISR, so it sees either the old or new value. * Larger block writes use the non-blocking sequence protocol instead of * delaying pulse-related interrupts for an unbounded block copy. */ if (wordCount <= 2UL) { interruptState = ModbusDataEnterShortCritical(); } ModbusDataWriteBegin(device, firstAddress, wordCount); for (index = 0UL; index < wordCount; index++) { firstWord[index] = values[index]; } ModbusDataWriteEnd(); if (wordCount <= 2UL) { ModbusDataExitShortCritical(interruptState); } return 1U; } uint8_t ModbusDataReadLinear(uint32_t address, uint16_t *value) { if (address < MODBUS_DATA_SRAM_WORD_COUNT) { return ModbusDataReadWord((address < MODBUS_DATA_D_WORD_COUNT) ? MODBUS_DATA_DEVICE_D : MODBUS_DATA_DEVICE_HD, (address < MODBUS_DATA_D_WORD_COUNT) ? address : address - MODBUS_DATA_HD_SRAM_OFFSET, value); } if ((address >= MODBUS_DATA_LINEAR_CCM_BASE) && ((address - MODBUS_DATA_LINEAR_CCM_BASE) < MODBUS_DATA_CCM_WORD_COUNT)) { uint32_t before; uint32_t after; uint16_t snapshot; if (value == NULL) { return 0U; } before = ModbusDataWriteSequence; if ((before & 1UL) != 0UL) { return 0U; } MODBUS_DATA_BARRIER(); snapshot = ModbusDataCcm[address - MODBUS_DATA_LINEAR_CCM_BASE]; MODBUS_DATA_BARRIER(); after = ModbusDataWriteSequence; if ((before != after) || ((after & 1UL) != 0UL)) { return 0U; } *value = snapshot; return 1U; } return 0U; } uint32_t ModbusDataGetWriteSequence(void) { return ModbusDataWriteSequence; } static uint32_t ModbusDataBitCapacity(MODBUS_BIT_DEVICE device) { switch (device) { case MODBUS_BIT_DEVICE_X: return MODBUS_DATA_X_BIT_COUNT; case MODBUS_BIT_DEVICE_M: return MODBUS_DATA_M_BIT_COUNT; case MODBUS_BIT_DEVICE_HM: return MODBUS_DATA_HM_BIT_COUNT; default: return 0UL; } } uint8_t ModbusDataValidateBits(MODBUS_BIT_DEVICE device, uint32_t firstAddress, uint32_t bitCount) { uint32_t capacity = ModbusDataBitCapacity(device); if ((capacity == 0UL) || (bitCount == 0UL) || (firstAddress >= capacity)) { return 0U; } return (bitCount <= (capacity - firstAddress)) ? 1U : 0U; } uint8_t ModbusDataReadBit(MODBUS_BIT_DEVICE device, uint32_t address, uint8_t *value) { uint8_t mask; if ((value == NULL) || (ModbusDataValidateBits(device, address, 1UL) == 0U)) { return 0U; } mask = (uint8_t)(1U << (address & 7UL)); MODBUS_DATA_BARRIER(); *value = ((ModbusBitImages[(uint32_t)device][address >> 3U] & mask) != 0U) ? 1U : 0U; MODBUS_DATA_BARRIER(); return 1U; } uint8_t ModbusDataWriteBit(MODBUS_BIT_DEVICE device, uint32_t address, uint8_t value) { uint8_t *byte; uint8_t mask; uint32_t interruptState; if (ModbusDataValidateBits(device, address, 1UL) == 0U) { return 0U; } byte = &ModbusBitImages[(uint32_t)device][address >> 3U]; mask = (uint8_t)(1U << (address & 7UL)); interruptState = ModbusDataEnterShortCritical(); if (value != 0U) { *byte |= mask; } else { *byte &= (uint8_t)(~mask); } MODBUS_DATA_BARRIER(); ModbusDataExitShortCritical(interruptState); return 1U; }