Index: NVDriver.c =================================================================== diff -u --- NVDriver.c (revision 0) +++ NVDriver.c (revision eed05c4b7fb5648a8b599286fc2bbcfbce9259ff) @@ -0,0 +1,180 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVDriver.c +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ + +#include "F021.h" // For fapi operations +#include "FapiFunctions.h" +#include "NVDriver.h" +#include "system.h" // For fapi operations + +/** + * @addtogroup NVDriver + * @{ + */ + +// ********** private definitions ********** + +// The clock frequency comes from HCLK_FREQ and it has to be rounded up to the nearest number +#define ROUNDED_HCLK_FREQ FLOAT_TO_INT_WITH_ROUND(HCLK_FREQ) ///< Rounded HCLK for EERPOM clock. +#define BANK7_SECTOR_0_31_ENABLE_BIT_MASK 0x0000000F ///< Bank7 sector 0 t0 31 enable mask. +#define BANK7_SECTOR_32_63_ENABLE_BIT_MASK 0x00000000 ///< Bank7 sector 32 to 63 enable mask. + +// ********** private function prototypes ********** + +static BOOL isSectorStartAddress( U32* recordFlashAddress ); + +/*********************************************************************//** + * @brief + * The initNVDriver function initializes the Non Volatile Memory Driver module. + * It configures and enables flash bank 7 and its EEPROM sectors. + * @details \b Inputs: none + * @details \b Outputs: none + * @return none + *************************************************************************/ +void initNVDriver( void ) +{ + // Initialize and activate flash bank 7 + Fapi_initializeFlashBanks( ROUNDED_HCLK_FREQ ); + Fapi_setActiveFlashBank( Fapi_FlashBank7 ); + Fapi_enableEepromBankSectors( BANK7_SECTOR_0_31_ENABLE_BIT_MASK, BANK7_SECTOR_32_63_ENABLE_BIT_MASK ); +} + +/*********************************************************************//** + * @brief + * The eraseSector function erases a flash sector at the specified address. + * It validates the address and issues an erase command if valid. + * @details \b Inputs: none + * @details \b Outputs: none + * @param recordFlashAddress Flash address of the sector to erase + * @return none + *************************************************************************/ +void eraseSector( U32* recordFlashAddress ) +{ + BOOL isFlashAddressValid = isSectorStartAddress( recordFlashAddress ); + + if ( TRUE == isFlashAddressValid) + { + Fapi_issueAsyncCommandWithAddress( Fapi_EraseSector, recordFlashAddress ); + } +} + +/*********************************************************************//** + * @brief + * The writeSector function programs data to a flash sector at the specified address. + * It validates the address and buffer before issuing the programming command. + * @details \b Inputs: none + * @details \b Outputs: none + * @param recordFlashAddress Flash address where data will be written + * @param bufferAddress Pointer to the data buffer to be written + * @param bufferSize Size of the buffer to be written + * @return none + *************************************************************************/ +void writeSector( U32* recordFlashAddress, U08* bufferAddress, U32 bufferSize ) +{ + // Create a copy only for validation + U32 sectorStartAddress = *recordFlashAddress; + BOOL isFlashAddressValid = isSectorStartAddress( §orStartAddress ); + + if ( ( TRUE == isFlashAddressValid ) && ( NULL != bufferAddress ) && ( bufferSize > 0 ) ) + { + Fapi_issueProgrammingCommand( recordFlashAddress, bufferAddress, bufferSize, 0x00, 0, Fapi_DataOnly ); + } +} + +/*********************************************************************//** + * @brief + * The readSector function reads data from a flash sector at the specified address. + * It validates the address and buffer before performing the read operation. + * @details \b Inputs: none + * @details \b Outputs: none + * @param recordFlashAddress Flash address to read from + * @param bufferAddress Pointer to the buffer where data will be stored + * @param bufferSize Size of the data to be read + * @return none + *************************************************************************/ +void readSector( U32* recordFlashAddress, U32* bufferAddress, U32 bufferSize ) +{ + // Create a copy only for validation + U32 sectorStartAddress = *recordFlashAddress; + BOOL isFlashAddressValid = isSectorStartAddress( §orStartAddress ); + + if ( ( TRUE == isFlashAddressValid ) && ( NULL != bufferAddress ) && ( bufferSize > 0 ) ) + { + Fapi_doMarginRead( recordFlashAddress, bufferAddress, bufferSize, Fapi_NormalRead ); + } +} + +/*********************************************************************//** + * @brief + * The isFlashReady function checks whether the flash FSM is ready. + * It returns the readiness status of the flash state machine. + * @details \b Inputs: none + * @details \b Outputs: none + * @return value indicating flash ready status (TRUE if ready, FALSE otherwise) + *************************************************************************/ +BOOL isFlashReady( void ) +{ + return ( Fapi_Status_FsmReady == FAPI_CHECK_FSM_READY_BUSY ); +} + +/*********************************************************************//** + * @brief + * The isSectorStartAddress function validates and aligns a flash address + * to a sector start. It checks if the address falls within defined + * sectors and updates it accordingly. + * @details \b Inputs: none + * @details \b Outputs: none + * @param recordFlashAddress Pointer to the flash address to validate + * and update + * @return isFlashAddressValid indicating address validity (TRUE if valid, FALSE + * otherwise) + *************************************************************************/ +static BOOL isSectorStartAddress( U32* recordFlashAddress ) +{ + BOOL isFlashAddressValid = TRUE; + + if ( NULL == recordFlashAddress ) + { + return FALSE; + } + + if ( ( BANK7_SECTOR0_START_ADDRESS <= *recordFlashAddress ) && + ( BANK7_SECTOR0_END_ADDRESS >= *recordFlashAddress ) ) + { + *recordFlashAddress = BANK7_SECTOR0_START_ADDRESS; + } + else if ( ( BANK7_SECTOR1_START_ADDRESS <= *recordFlashAddress ) && + ( BANK7_SECTOR1_END_ADDRESS >= *recordFlashAddress ) ) + { + *recordFlashAddress = BANK7_SECTOR1_START_ADDRESS; + } + else if ( ( BANK7_SECTOR2_START_ADDRESS <= *recordFlashAddress ) && + ( BANK7_SECTOR2_END_ADDRESS >= *recordFlashAddress ) ) + { + *recordFlashAddress = BANK7_SECTOR2_START_ADDRESS; + } + else if ( ( BANK7_SECTOR3_START_ADDRESS <= *recordFlashAddress ) && + ( BANK7_SECTOR3_END_ADDRESS >= *recordFlashAddress ) ) + { + *recordFlashAddress = BANK7_SECTOR3_START_ADDRESS; + } + else + { + isFlashAddressValid = FALSE; + } + + return isFlashAddressValid; +} + +/**@}*/ + Index: NVDriver.h =================================================================== diff -u --- NVDriver.h (revision 0) +++ NVDriver.h (revision eed05c4b7fb5648a8b599286fc2bbcfbce9259ff) @@ -0,0 +1,81 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVDriver.h +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ + +#ifndef _NV_DRIVER_H +#define _NV_DRIVER_H + +#include "Common.h" + +/** + * @defgroup NVDriver NVDriver + * @brief Non Volatile Memory Driver module provides an abstraction layer for + * accessing Flash memory, ncluding initialization, sector erase, read, and write operations. + * + * @addtogroup NVDriver + * @{ + */ + +// ********** public definitions ********** + +#define BANK7_SECTOR0_START_ADDRESS 0xF0200000 ///< Bank7 sector 0 start address. +#define BANK7_SECTOR0_END_ADDRESS 0xF0203FFF ///< Bank7 sector 0 end address. + +#define BANK7_SECTOR1_START_ADDRESS 0xF0204000 ///< Bank7 sector 1 start address. +#define BANK7_SECTOR1_END_ADDRESS 0xF0207FFF ///< Bank7 sector 1 end address. + +#define BANK7_SECTOR2_START_ADDRESS 0xF0208000 ///< Bank7 sector 2 start address. +#define BANK7_SECTOR2_END_ADDRESS 0xF020BFFF ///< Bank7 sector 2 end address. + +#define BANK7_SECTOR3_START_ADDRESS 0xF020C000 ///< Bank7 sector 3 start address. +#define BANK7_SECTOR3_END_ADDRESS 0xF020FFFF ///< Bank7 sector 3 end address. + +/// Non Volatile Memory operation modes enumeration. +typedef enum NVM_Operation +{ + NVM_OPERATION_NONE = 0, ///< Default mode to prevent any accidental ops. + NVM_OPERATION_WRITE, ///< Operation mode write. + NVM_OPERATION_READ, ///< Operation mode read. + NVM_OPERATION_ERASE, ///< Operation mode erase a sector. + NUM_OF_NVM_OPERATIONS ///< Total number of operation states. +} NVM_OPERATION_T; + +/// NVM record type enumeration +typedef enum NVM_Record_Type +{ + NVM_SYSTEM_RECORD = 0, ///< NVM process system record. + NVM_SERVICE_RECORD, ///< NVM process service record. + NVM_CALIBRATION_RECORD, ///< NVM process calibration record. + NVM_INSTITUTIONAL_RECORD, ///< NVM process institutional record. + NVM_USAGE_INFO_RECORD, ///< NVM process usage info record. + NUM_OF_NVM_RECORD_TYPES ///< Number of NVM records jobs. +} NVM_RECORD_TYPE_T; + +/// Process records job structure +typedef struct +{ + NVM_OPERATION_T memoryOperation; ///< Memory operation. + NVM_RECORD_TYPE_T recordJob; ///< Record job (i.e sector 0). +} PROCESS_RECORD_JOB_T; + +// ********** public function prototypes ********** + +void initNVDriver( void ); +void eraseSector( U32* recordFlashAddress ); +void writeSector( U32* recordFlashAddress, U08* bufferAddress, U32 bufferSize ); +void readSector( U32* recordFlashAddress, U32* bufferAddress, U32 bufferSize ); +BOOL isFlashReady( void ); + +/**@}*/ + +#endif /* _NV_DRIVER_H */ Index: NVJobQ.c =================================================================== diff -u --- NVJobQ.c (revision 0) +++ NVJobQ.c (revision eed05c4b7fb5648a8b599286fc2bbcfbce9259ff) @@ -0,0 +1,320 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVJobQ.c +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ + +#include // For memcpy +#include "Common.h" +#include "MsgDefs.h" +#include "NVJobQ.h" +#include "NVRecordsDD.h" + + + + +/** + * @addtogroup NVJobQ + * @{ + */ + +// ********** private definitions ********** + +// Once a new calibration data is available the driver, sets a signal for the defined time. Once the time is out, it turns the signal off. + +#define QUEUE_MAX_SIZE 20U ///< Max queue size. +#define QUEUE_START_INDEX 0U ///< Queue start index. +#define MAX_JOB_DATA_SIZE_BYTES 32U ///< Max bytes per job (32 bytes). + +/// DD institutional values structure. +typedef struct +{ + U32 minRORejectionRatioPCT; ///< Min RO rejection ratio in percent. + F32 minInletWaterCondAlarmLimitUSPCM; ///< Min inlet water conductivity alarm limit in uS/cm. +} DD_INSTITUTIONAL_VALUES_T; + +// ********** private data ********** + +static PROCESS_RECORD_JOB_T recordJobQueue[ QUEUE_MAX_SIZE ]; ///< Record queue jobs. +static U08 recordQueueRearIndex; ///< Record queue rear index. +static U08 recordQueueFrontIndex; ///< Record queue front index. +static U08 recordQueueCount; ///< Record queue count. +static PROCESS_RECORD_JOB_T recordCurrentJob; ///< Record queue current job. +static NVM_RECORDS_READ_STATUS_T recordsReadStatus; ///< NVM records read status. + +// ********** private function prototypes ********** + +/*********************************************************************//** + * @brief + * The initNVJobQ function initializes the NV message queue and related + * state variables. + * @details \b Inputs: none + * @details \b Outputs: recordQueueRearIndex, + * recordQueueFrontIndex, recordQueueCount, recordsReadStatus, + * @return none + *************************************************************************/ +void initNVJobQ ( void ) +{ + recordQueueRearIndex = QUEUE_START_INDEX; + recordQueueFrontIndex = QUEUE_START_INDEX; + recordQueueCount = 0; + recordsReadStatus = NVM_RECORDS_NOT_STARTED; +} + +/*********************************************************************//** + * @brief + * The enqueueRecordJob function enqueues a new record job into the + * record queue. It updates the queue with the new job and advances + * the rear index. + * @details \b Inputs: recordQueueRearIndex, recordQueueCount + * @details \b Outputs: recordJobQueue, recordQueueRearIndex, + * recordQueueCount + * @param ops memory operation (i.e write, read) + * @param job type of job (i.e write calibration data) + * @return none + *************************************************************************/ +void enqueueRecordJob( NVM_OPERATION_T ops, NVM_RECORD_TYPE_T job ) +{ + PROCESS_RECORD_JOB_T currentJob; + + currentJob.memoryOperation = ops; + currentJob.recordJob = job; + recordJobQueue[ recordQueueRearIndex ] = currentJob; + + recordQueueCount++; + recordQueueRearIndex = INC_WRAP( recordQueueRearIndex, 0, QUEUE_MAX_SIZE - 1 ); +} + +/*********************************************************************//** + * @brief + * The dequeueRecordJob function removes a record job from the queue. + * It updates the front index, retrieves the job, and updates the + * queue count. + * @details \b Inputs: recordQueueFrontIndex, recordQueueCount, + * recordJobQueue + * @details \b Outputs: recordQueueFrontIndex, recordQueueCount, + * recordCurrentJob + * @return none + *************************************************************************/ +void dequeueRecordJob( void ) +{ + U32 tempIndex; + + _disable_IRQ(); + tempIndex = recordQueueFrontIndex; + + if ( FALSE == isRecordQueueEmpty() ) + { + recordQueueFrontIndex = INC_WRAP( recordQueueFrontIndex, 0, QUEUE_MAX_SIZE - 1 ); + recordCurrentJob = recordJobQueue[ tempIndex ]; + } + if ( recordQueueCount > 0 ) + { + recordQueueCount--; + } + _enable_IRQ(); +} + +/*********************************************************************//** + * @brief + * The isRecordQueueEmpty function checks whether the record queue is + * empty. It returns the status based on the queue count. + * @details \b Inputs: recordQueueCount + * @details \b Outputs: none + * @return TRUE if queue is empty otherwise FALSE + *************************************************************************/ +BOOL isRecordQueueEmpty( void ) +{ + BOOL isEmpty = TRUE; + + if ( recordQueueCount > 0 ) + { + isEmpty = FALSE; + } + + return isEmpty; +} + +/*********************************************************************//** + * @brief + * The isRecordQueueFull function checks whether the record queue is + * full based on the maximum queue size. + * @details \b Inputs: recordQueueCount + * @details \b Outputs: none + * @return TRUE if queue is full otherwise FALSE + *************************************************************************/ +BOOL isRecordQueueFull( void ) +{ + BOOL isFull = FALSE; + + if ( recordQueueCount >= ( QUEUE_MAX_SIZE - 1 ) ) + { + isFull = TRUE; + } + + return isFull; +} + +/*********************************************************************//** + * @brief + * The getAvailableRecordQueueCount function returns the number of + * available record queue slots based on the current queue count. + * @details \b Inputs: recordQueueCount + * @details \b Outputs: none + * @return available record queues + *************************************************************************/ + +U32 getAvailableRecordQueueCount( void ) +{ + return QUEUE_MAX_SIZE - recordQueueCount; +} + +/*********************************************************************//** + * @brief + * The enqueueEraseAndWriteSector function enqueues erase and write + * operations for the given record type. + * @details \b Inputs: none + * @details \b Outputs: none + * @param job Record type for which erase and write operations are scheduled + * @return TRUE if the operations were successfully enqueued otherwise FALSE + *************************************************************************/ +BOOL enqueueEraseAndWriteSector( NVM_RECORD_TYPE_T job ) +{ + BOOL status = FALSE; + + if ( getAvailableRecordQueueCount() >= MIN_JOBS_NEEDED_TO_WRITE_A_RECORD ) + { + // Service and Calibration record are stored in the same sector. + if ( ( job == NVM_SERVICE_RECORD ) || ( job == NVM_CALIBRATION_RECORD ) ) + { + enqueueRecordJob( NVM_OPERATION_ERASE, NVM_SERVICE_RECORD ); + enqueueRecordJob( NVM_OPERATION_WRITE, NVM_SERVICE_RECORD ); + enqueueRecordJob( NVM_OPERATION_WRITE, NVM_CALIBRATION_RECORD ); + } + else + { + enqueueRecordJob( NVM_OPERATION_ERASE, job ); + enqueueRecordJob( NVM_OPERATION_WRITE, job ); + } + + status = TRUE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The enqueuewriteAllRecords function enqueues erase and write + * operations for all records. It schedules jobs for all record types + * if sufficient queue space is available. + * @details \b Inputs: none + * @details \b Outputs: none + * @return TRUE if all operations were successfully enqueued otherwise FALSE + *************************************************************************/ +BOOL enqueuewriteAllRecords( void ) +{ + BOOL status = FALSE; + + if ( getAvailableRecordQueueCount() >= MIN_JOBS_NEEDED_TO_WRITE_ALL_RECORDS ) + { + NVM_RECORD_TYPE_T record; + + // Erase all the sectors + for ( record = NVM_SYSTEM_RECORD; record < NUM_OF_NVM_RECORD_TYPES; record++ ) + { + enqueueRecordJob( NVM_OPERATION_ERASE, record ); + } + + // Write all the records + for ( record = NVM_SYSTEM_RECORD; record < NUM_OF_NVM_RECORD_TYPES; record++ ) + { + enqueueRecordJob( NVM_OPERATION_WRITE, record ); + } + + status = TRUE; + } + + return status; +} + +/*********************************************************************//** + * @brief + * The enqueueReadAllRecords function enqueues all NV records to be read + * during POST. It schedules read jobs for all record types if sufficient + * queue space is available. + * @details \b Inputs: none + * @details \b Outputs: recordsReadStatus + * @return TRUE if records were successfully enqueued otherwise FALSE + *************************************************************************/ +BOOL enqueueReadAllRecords( void ) +{ + BOOL status = FALSE; + NVM_RECORD_TYPE_T record; + + if ( getAvailableRecordQueueCount() >= MIN_JOBS_NEEDED_TO_READ_ALL_RECORDS ) + { + for ( record = NVM_SYSTEM_RECORD; record < NUM_OF_NVM_RECORD_TYPES; record++ ) + { + enqueueRecordJob( NVM_OPERATION_READ, record ); + } + + status = TRUE; + } + + // Set the status to records were queued successfully + recordsReadStatus = NVM_RECORDS_QUEUED; + + return status; +} + +/*********************************************************************//** + * @brief + * The getNVRecordsReadStatus function returns the current NV records + * read status of the POST process. + * @details \b Inputs: recordsReadStatus + * @details \b Outputs: none + * @return recordsReadStatus + *************************************************************************/ +NVM_RECORDS_READ_STATUS_T getNVRecordsReadStatus( void ) +{ + return recordsReadStatus; +} + +/*********************************************************************//** + * @brief + * The updateRecordReadStatus function updates the NV records read + * status with the provided value. + * @details \b Inputs: none + * @details \b Outputs: recordsReadStatus + * @param status Record read status to be updated + * @return none + *************************************************************************/ +void updateRecordReadStatus( NVM_RECORDS_READ_STATUS_T status ) +{ + recordsReadStatus = status; +} + +/*********************************************************************//** + * @brief + * The getCurrentProcessRecordJob function gets the current record job + * from the processing queue. + * @details \b Inputs: recordCurrentJob + * @details \b Outputs: none + * @return recordCurrentJob + *************************************************************************/ +PROCESS_RECORD_JOB_T getCurrentProcessRecordJob ( void ) +{ + return recordCurrentJob; +} + + +/**@}*/ Index: NVJobQ.h =================================================================== diff -u --- NVJobQ.h (revision 0) +++ NVJobQ.h (revision eed05c4b7fb5648a8b599286fc2bbcfbce9259ff) @@ -0,0 +1,81 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVJobQ.h +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ + +#ifndef _NV_JOB_Q_H_ +#define _NV_JOB_Q_H_ + +#include "Common.h" +#include "DDDefs.h" +#include "NVDriver.h" + +/** + * @defgroup NVJobQ NVJobQ + * @brief This module manages the NV record job queue. It provides interfaces + * to enqueue, dequeue, and monitor record operations such as read, + * write, and erase. It also maintains queue state, availability, and + * current job tracking for NV data processing. + * + * @addtogroup NVJobQ + * @{ + */ + +// ********** public definitions ********** + +#define NV_RECORD_SLOT_SIZE 4096 ///< Size of each NV record slot (4KB) + +#define SYSTEM_RECORD_OFFSET (0U * NV_RECORD_SLOT_SIZE) ///< Offset of system record within sector +#define SERVICE_RECORD_OFFSET (0U * NV_RECORD_SLOT_SIZE) ///< Offset of service record within sector +#define CAL_RECORD_OFFSET (1U * NV_RECORD_SLOT_SIZE) ///< Offset of calibration record within sector +#define INSTIT_RECORD_OFFSET (0U * NV_RECORD_SLOT_SIZE) ///< Offset of institutional record within sector +#define USAGE_RECORD_OFFSET (0U * NV_RECORD_SLOT_SIZE) ///< Offset of usage record within sector + +#define SYSTEM_RECORD_NV_MEM_START_ADDRESS ( BANK7_SECTOR0_START_ADDRESS + SYSTEM_RECORD_OFFSET ) ///< Start address of system record in NV memory +#define SERVICE_RECORD_NV_MEM_START_ADDRESS ( BANK7_SECTOR1_START_ADDRESS + SERVICE_RECORD_OFFSET ) ///< Start address of service record in NV memory +#define CAL_RECORD_NV_MEM_START_ADDRESS ( BANK7_SECTOR1_START_ADDRESS + CAL_RECORD_OFFSET ) ///< Start address of calibration record in NV memory +#define INSTIT_RECORD_NV_MEM_START_ADDRESS ( BANK7_SECTOR2_START_ADDRESS + INSTIT_RECORD_OFFSET ) ///< Start address of institutional record in NV memory +#define USAGE_INFO_START_ADDRESS ( BANK7_SECTOR3_START_ADDRESS + USAGE_RECORD_OFFSET ) ///< Start address of usage info record in NV memory + +#define MAX_NUM_OF_RECORD_IN_A_SECTOR 2 ///< Maximum number of records stored in one sector +#define MIN_JOBS_NEEDED_TO_WRITE_A_RECORD ( MAX_NUM_OF_RECORD_IN_A_SECTOR * 2 ) ///< Minimum queue jobs to erase and write one record +#define MIN_JOBS_NEEDED_TO_WRITE_ALL_RECORDS ( NUM_OF_NVM_RECORD_TYPES * 2 ) ///< Minimum queue jobs to erase and write all records +#define MIN_JOBS_NEEDED_TO_READ_ALL_RECORDS ( NUM_OF_NVM_RECORD_TYPES ) ///< Minimum queue jobs to read all records + +/// NVM records read status +typedef enum NVM_NV_Records_Read_Status +{ + NVM_RECORDS_NOT_STARTED = 0, ///< NVM records not started. + NVM_RECORDS_QUEUED, ///< NVM records queued. + NVM_RECORDS_READ, ///< NVM records read. + NVM_RECORDS_CRC_CHECKED, ///< NVM records CRC checked. + NUM_OF_NVM_READ_RECORDS_STATES, ///< Number of NVM read records states. +} NVM_RECORDS_READ_STATUS_T; + +// ********** public function prototypes ********** + +void initNVJobQ( void ); +// Record operations queue functions +void enqueueRecordJob( NVM_OPERATION_T ops, NVM_RECORD_TYPE_T job ); +void dequeueRecordJob( void ); +BOOL isRecordQueueEmpty( void ); +BOOL isRecordQueueFull( void ); +U32 getAvailableRecordQueueCount( void ); +BOOL enqueueEraseAndWriteSector( NVM_RECORD_TYPE_T job ); +BOOL enqueuewriteAllRecords( void ); +BOOL enqueueReadAllRecords( void ); +void updateRecordReadStatus( NVM_RECORDS_READ_STATUS_T status ); +PROCESS_RECORD_JOB_T getCurrentProcessRecordJob ( void ); + +/**@}*/ + +#endif /* _NV_JOB_Q_H_ */ Index: NVMgmt.c =================================================================== diff -u --- NVMgmt.c (revision 0) +++ NVMgmt.c (revision eed05c4b7fb5648a8b599286fc2bbcfbce9259ff) @@ -0,0 +1,519 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVMgmt.c +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ + +#include // For memcpy +#include "Common.h" +#include "NVDriver.h" +#include "NVJobQ.h" +#include "NVMessagingDD.h" +#include "NVMgmt.h" +#include "NVRecordsDD.h" +#include "Timers.h" + +/** + * @addtogroup NVMgmt + * @{ + */ + +// ********** private definitions ********** + +/// EEPROM functions use the buffer length as the size of U32. So before send the length to any of FAPI functions, it should be divided by 4. +#define EEPROM_OPS_SIZE_OF_CONVERTER 4 +#define COMMAND_TIME_OUT (1 * MS_PER_SECOND) ///< Timeout for an EEPROM in ms. + +/// NVM Exec states enumeration. +typedef enum NVM_Exec_State +{ + NVM_EXEC_STATE_IDLE = 0, ///< Exec state Idle. + NVM_EXEC_STATE_WRITE, ///< Exec state write to EEPROM. + NVM_EXEC_STATE_VERIFY_WRITE, ///< Exec state verify EEPROM write. + NVM_EXEC_STATE_READ, ///< Exec state read from EEPROM. + NVM_EXEC_STATE_ERASE, ///< Exec state erase EEPROM. + NUM_OF_NVM_EXEC_STATES ///< Total number of exec states. +} NVM_EXEC_STATE_T; + +/// NVM write record validity check states +typedef enum NVM_Write_Record_Validity_Check +{ + NVM_RECORD_NOT_CHECKED = 0, ///< NVM (written) record not checked. + NVM_RECORD_VALID, ///< NVM record is valid. + NVM_RECORD_NOT_VALID, ///< NVM record is not valid. + NUM_OF_NVM_RECORD_VALIDITY_CHECK ///< Number of NVM validity check states. +} NVM_RECORD_VALIDITY_CHECK_T; + +// ********** private data ********** + +static NVM_EXEC_STATE_T nvmExecState; ///< NVM exec state variable. +static volatile BOOL powerOffIsImminent; ///< Power off warning has been signaled. Non-volatile memory operations should be completed ASAP and then ceased. +static NVM_RECORD_VALIDITY_CHECK_T writtenRecordStatus; ///< Record data write validity check. +static U32 currentTime; ///< Current time. +static U32 recordAddressOffset; ///< Record address offset. +static U08 writtenRecordCheckBuffer[ MAX_EEPROM_WRITE_BUFFER_BYTES ]; ///< Written record validity check buffer. + +// ********** private function prototypes ********** + +// Exec functions +static NVM_EXEC_STATE_T handleExecIdleState( void ); +static NVM_EXEC_STATE_T handleExecEraseState( void ); +static NVM_EXEC_STATE_T handleExecWriteState( void ); +static NVM_EXEC_STATE_T handleExecReadState( void ); +static NVM_EXEC_STATE_T handleExecVerifyWriteState( void ); + +// Helper functions +static BOOL didCommandTimeout( NVM_EXEC_STATE_T state ); + +/*********************************************************************//** + * @brief + * The initNVMgmt function initializes the NV management module. + * It resets internal state variables, initializes driver, + * message queue, records, and enqueues read operations. + * @details \b Inputs: none + * @details \b Outputs: nvmExecState, powerOffIsImminent, + * currentTime, writtenRecordStatus, + * recordAddressOffset + * @return none + *************************************************************************/ +void initNVMgmt( void ) +{ + nvmExecState = NVM_EXEC_STATE_IDLE; + powerOffIsImminent = FALSE; + currentTime = 0; + writtenRecordStatus = NVM_RECORD_NOT_CHECKED; + recordAddressOffset = 0; + + initNVDriver(); + initNVJobQ(); + initNVMessagingDD(); + initNVRecordsDD(); + enqueueReadAllRecords(); +} + +/*********************************************************************//** + * @brief + * The signalPowerOffWarning signals this module that system power off is + * imminent and any non-volatile data writes in progress should be wrapped + * up quickly and any pending non-volatile data writes should not be started. + * @details Inputs: none + * @details Outputs: powerOffIsImminent + * @return none + *************************************************************************/ +void signalPowerOffWarning( void ) +{ + powerOffIsImminent = TRUE; +} + +/*********************************************************************//** + * @brief + * The execNVM function executes the main NV management state machine. + * It handles different execution states for erase, write, read, and + * verification of NV memory operations. + * @details \b Inputs: nvmExecState + * @details \b Outputs: nvmExecState + * @return none + *************************************************************************/ +void execNVM( void ) +{ + switch ( nvmExecState ) + { + case NVM_EXEC_STATE_IDLE: + nvmExecState = handleExecIdleState(); + break; + + case NVM_EXEC_STATE_ERASE: + nvmExecState = handleExecEraseState(); + break; + + case NVM_EXEC_STATE_WRITE: + nvmExecState = handleExecWriteState(); + break; + + case NVM_EXEC_STATE_READ: + nvmExecState = handleExecReadState(); + break; + + case NVM_EXEC_STATE_VERIFY_WRITE: + nvmExecState = handleExecVerifyWriteState(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_NVM_INVALID_EXEC_STATE, ( U32 )nvmExecState ); + nvmExecState = NVM_EXEC_STATE_IDLE; + break; + } +} + +/*********************************************************************//** + * @brief + * The resetNVMPOSTState function resets the NV data management POST + * state and sets the self-test to start reading all the records. + * @details \b Inputs: none + * @details \b Outputs: none + * @return none + *************************************************************************/ + +void resetNVMPOSTState( void ) +{ + updateNVSelfTestResult( SELF_TEST_STATUS_IN_PROGRESS ); + updateNVSelfTestState( NVM_SELF_TEST_STATE_READ_RECORDS ); +} + +/*********************************************************************//** + * @brief + * The isNewCalibrationRecordAvailable function returns the status of + * new calibration record availability. + * @details \b Inputs: none + * @details \b Outputs: none + * @return TRUE if new calibration is available otherwise FALSE + *************************************************************************/ +BOOL isNewCalibrationRecordAvailable( void ) +{ + return getNewCalRecordAvalability(); +} + +/*********************************************************************//** + * @brief + * The getNVRecord2Driver function retrieves NV data by calling the + * record handler after validating input parameters. + * @details \b Inputs: none + * @details \b Outputs: none + * @param nvData The non-volatile data identifier + * @param bufferAddress Pointer to the buffer where data will be copied + * @param bufferLength Length of the provided buffer + * @param numOfSnsrs2Check Number of sensors to check + * @param nvAlarm Alarm to raise if data is invalid + * @return TRUE if the operation is successful otherwise FALSE + *************************************************************************/ +BOOL getNVRecord2Driver( NV_DATA_T nvData, U08* bufferAddress, U32 bufferLength, + U08 numOfSnsrs2Check, ALARM_ID_T nvAlarm ) +{ + BOOL status = FALSE; + + if ( ( nvData < NUM_OF_NV_DD_DATA ) && + ( NULL != bufferAddress ) && + ( bufferLength > 0U ) ) + { + status = getNVMRecord(nvData, bufferAddress, + bufferLength, numOfSnsrs2Check, nvAlarm); + } + + return status; +} + +/*********************************************************************//** + * @brief + * The execNVMProcessRecord function executes the NVM process record + * handler. + * @details \b Inputs: none + * @details \b Outputs: none + * @return none + *************************************************************************/ +void execNVMProcessRecord( void ) +{ + execNVMPSendReceiveRecord(); +} + +/*********************************************************************//** + * @brief + * The execNVMSelfTest function executes the NV management self-test + * handler. + * @details \b Inputs: none + * @details \b Outputs: none + * @return SELF_TEST_STATUS_T self-test result + *************************************************************************/ +SELF_TEST_STATUS_T execNVMSelfTest( void ) +{ + return execNVMRecordsSelfTest(); +} + +/*********************************************************************//** +* @brief +* The handleExecIdleState function processes the idle state of the +* NVM execution state machine. It checks for pending record jobs and +* initiates erase, write, or read operations. +* @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT if invalid operation +* is encountered +* @details \b Inputs: powerOffIsImminent +* @details \b Outputs: recordAddressOffset, currentTime +* @return state next state of the state machine +*************************************************************************/ +static NVM_EXEC_STATE_T handleExecIdleState( void ) +{ + NVM_EXEC_STATE_T state = NVM_EXEC_STATE_IDLE; + BOOL areQueuesNotEmpty = FALSE; + NVM_OPERATION_T ops; + U32 recordFlashAddress; + U08* bufferAddress; + PROCESS_RECORD_JOB_T recordCurrentJob; + NVM_RECORD_TYPE_T job; + PROCESS_RECORD_SPECS_T jobSpecs; + + // If power off command has been issued, do not process any new jobs + if ( powerOffIsImminent != TRUE ) + { + // If the record processing queue is not empty, process the queues + if ( ( FALSE == isRecordQueueEmpty() ) && ( TRUE == isFlashReady() ) ) + { + dequeueRecordJob(); + recordCurrentJob = getCurrentProcessRecordJob(); + job = recordCurrentJob.recordJob; + jobSpecs = getProcessRecord( job ); + // Set the record address offset to 0 since a job will just be started + recordAddressOffset = 0; + areQueuesNotEmpty = TRUE; + ops = recordCurrentJob.memoryOperation; + currentTime = getMSTimerCount(); + recordFlashAddress = jobSpecs.recordFlashAddress + recordAddressOffset; + bufferAddress = jobSpecs.structAddressPtr + recordAddressOffset; + } + } + + // Check if a queue job is available + if ( TRUE == areQueuesNotEmpty ) + { + switch ( ops ) + { + case NVM_OPERATION_ERASE: + eraseSector( (U32*)recordFlashAddress ); + state = NVM_EXEC_STATE_ERASE; + break; + + case NVM_OPERATION_WRITE: + writeSector( (U32*)recordFlashAddress, (U08*)bufferAddress, MAX_EEPROM_WRITE_BUFFER_BYTES ); + currentTime = getMSTimerCount(); + state = NVM_EXEC_STATE_WRITE; + break; + + case NVM_OPERATION_READ: + readSector( (U32*)recordFlashAddress, (U32*)bufferAddress, ( jobSpecs.sizeofRecord / EEPROM_OPS_SIZE_OF_CONVERTER ) ); + currentTime = getMSTimerCount(); + state = NVM_EXEC_STATE_READ; + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_NVM_INVALID_OPS, ( U32 )nvmExecState ); + break; + } + } + + // Update Self Test Read Records Flag + if ( ( TRUE == isRecordQueueEmpty() ) && ( state == NVM_EXEC_STATE_IDLE ) ) + { + updateSelfTestReadRecordsFlag( TRUE ); + } + else + { + updateSelfTestReadRecordsFlag( FALSE ); + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleExecEraseState function handles the erase state of the + * NVM execution state machine. It issues an erase command to EEPROM on entry, + * waits for erase completion and if the erase was successful, + * it transitions back to idle when done or on timeout. + * @details \b Inputs: none + * @details \b Outputs: none + * @return state next state of the state machine + *************************************************************************/ +static NVM_EXEC_STATE_T handleExecEraseState( void ) +{ + NVM_EXEC_STATE_T state = NVM_EXEC_STATE_ERASE; + BOOL timeoutStatus = didCommandTimeout( state ); + + if ( TRUE == isFlashReady() || timeoutStatus == TRUE ) + { + state = NVM_EXEC_STATE_IDLE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleExecWriteState function handles the write state of + * the NVM execution state machine. It verifies written data and + * continues writing remaining data. if the write was successful, + * it sets the state to Idle. + * @details \b Inputs: recordAddressOffset, + * writtenRecordStatus + * @details \b Outputs: recordAddressOffset, currentTime, + * writtenRecordCheckBuffer, writtenRecordStatus + * @return state next state of the state machine + *************************************************************************/ +static NVM_EXEC_STATE_T handleExecWriteState( void ) +{ + NVM_EXEC_STATE_T state = NVM_EXEC_STATE_WRITE; + BOOL timeoutStatus = didCommandTimeout( state ); + PROCESS_RECORD_JOB_T recordCurrentJob = getCurrentProcessRecordJob(); + NVM_RECORD_TYPE_T job = recordCurrentJob.recordJob; + PROCESS_RECORD_SPECS_T jobSpecs = getProcessRecord( job ); + + // Check if the fapi has finished + if ( TRUE == isFlashReady() ) + { + // Check the integrity of data (the 16 bytes that were written to + // EEPROM should be read and be checked for each byte) + if ( writtenRecordStatus == NVM_RECORD_NOT_CHECKED ) + { + currentTime = getMSTimerCount(); + U32 recordFlashAddress = jobSpecs.recordFlashAddress + recordAddressOffset; + U32 maxBufferLength = MAX_EEPROM_WRITE_BUFFER_BYTES / EEPROM_OPS_SIZE_OF_CONVERTER; + + // Clear the buffer from the previous content + memset( writtenRecordCheckBuffer, 0, sizeof( writtenRecordCheckBuffer ) ); + + // Issue a FAPI read command but only the bytes that were written, so use maxBufferLength + readSector( (U32*)recordFlashAddress, + (U32*)writtenRecordCheckBuffer, + maxBufferLength ); + + state = NVM_EXEC_STATE_VERIFY_WRITE; + } + else if ( writtenRecordStatus == NVM_RECORD_VALID ) + { + // If the data is valid, and if it is at the end of the write, change to idle + if ( jobSpecs.sizeofRecord == recordAddressOffset ) + { + state = NVM_EXEC_STATE_IDLE; + } + else + { + // Update the variables and issue the next write command + currentTime = getMSTimerCount(); + recordAddressOffset += MAX_EEPROM_WRITE_BUFFER_BYTES; + U32 memoryPtr = jobSpecs.recordFlashAddress + recordAddressOffset; + U08* structPtr = jobSpecs.structAddressPtr + recordAddressOffset; + writtenRecordStatus = NVM_RECORD_NOT_CHECKED; + + // Issue the write command + writeSector( (U32*)memoryPtr, structPtr, MAX_EEPROM_WRITE_BUFFER_BYTES ); + } + } + } + // If timed out, get back to Idle + else if ( timeoutStatus == TRUE ) + { + recordAddressOffset = 0; + state = NVM_EXEC_STATE_IDLE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleExecReadState function handles the read state of the NVM + * execution state machine. It waits for read completion and transitions + * to idle when done or on timeout. + * @details \b Inputs: none + * @details \b Outputs: none + * @return state next state of the state machine + *************************************************************************/ +static NVM_EXEC_STATE_T handleExecReadState( void ) +{ + NVM_EXEC_STATE_T state = NVM_EXEC_STATE_READ; + BOOL timeoutStatus = didCommandTimeout( state ); + + if ( ( TRUE == isFlashReady() ) || ( TRUE == timeoutStatus ) ) + { + state = NVM_EXEC_STATE_IDLE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleExecVerifyWriteState function verifies the data written to + * EEPROM by comparing it with the source data. It schedules a rewrite + * if mismatch is detected or continues the write process if valid. + * @details \b Inputs: recordAddressOffset,writtenRecordCheckBuffer + * @details \b Outputs: writtenRecordStatus + * @return state next state of the state machine + *************************************************************************/ +static NVM_EXEC_STATE_T handleExecVerifyWriteState( void ) +{ + NVM_EXEC_STATE_T state = NVM_EXEC_STATE_VERIFY_WRITE; + BOOL timeoutStatus = didCommandTimeout( state ); + PROCESS_RECORD_JOB_T recordCurrentJob = getCurrentProcessRecordJob(); + NVM_RECORD_TYPE_T job = recordCurrentJob.recordJob; + PROCESS_RECORD_SPECS_T jobSpecs = getProcessRecord( job ); + + // Check if the flash is ready + if ( TRUE == isFlashReady() ) + { + U32 i; + + U08* bufferPtr = jobSpecs.structAddressPtr + recordAddressOffset; + + // Loop through the bytes in the buffer + for ( i = 0; i < MAX_EEPROM_WRITE_BUFFER_BYTES; i++ ) + { + // Check if data in the buffer is not the same as the data in the structure + if ( writtenRecordCheckBuffer[ i ] != *bufferPtr ) + { + // Data is not valid. Schedule an erase and write of the record + writtenRecordStatus = NVM_RECORD_NOT_VALID; + enqueueEraseAndWriteSector( job ); + state = NVM_EXEC_STATE_IDLE; + // Exit the loop since there is no point to check the rest of data + break; + } + else + { + // Everything is good increment the pointer to check the next byte. + bufferPtr++; + // Record data is valid so far + writtenRecordStatus = NVM_RECORD_VALID; + // Go back write to EEPROM state to continue writing the record data + state = NVM_EXEC_STATE_WRITE; + } + } + } + + return state; +} + + + +/*********************************************************************//** + * @brief + * The didCommandTimeout function checks whether the command has timed + * out. If a timeout occurs, it sets an alarm and returns TRUE. + * @details \b Alarms: ALARM_ID_DD_SOFTWARE_FAULT if command timeout + * occurs + * @details \b Inputs: currentTime + * @details \b Outputs: none + * @param state The state in which the command timed out + * @return TRUE if a command timed out otherwise FALSE + *************************************************************************/ +static BOOL didCommandTimeout( NVM_EXEC_STATE_T state ) +{ + BOOL status = FALSE; + + if ( TRUE == didTimeout( currentTime, COMMAND_TIME_OUT ) ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, + SW_FAULT_ID_NVM_OPS_TIMEOUT, + ( U32 )state ) + status = TRUE; + } + + return status; +} + +/**@}*/ Index: NVMgmt.h =================================================================== diff -u --- NVMgmt.h (revision 0) +++ NVMgmt.h (revision eed05c4b7fb5648a8b599286fc2bbcfbce9259ff) @@ -0,0 +1,49 @@ +/************************************************************************** +* +* Copyright (c) 2026-2027 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file NVMgmt.h +* +* @author (original) Arpita Srivastava +* @date (original) 31-Mar-2026 +* +***************************************************************************/ + +#ifndef _NV_MGMT_DD_H_ +#define _NV_MGMT_DD_H_ + +#include "AlarmDefs.h" +#include "NVRecordsDD.h" + +/** + * @defgroup NVMgmt NVMgmt + * @brief This module provides the top-level NV data management interface used + * by other system modules. + * It initializes and executes NV operations, manages POST self-test, + * handles power-off conditions, and provides APIs to access NV records. + * It also integrates lower-level NV messaging and record handling modules. + * + * @addtogroup NVMgmt + * @{ + */ + +// ********** public function prototypes ********** + +void initNVMgmt( void ); +void signalPowerOffWarning( void ); +void execNVM( void ); +void resetNVMPOSTState( void ); + +BOOL isNewCalibrationRecordAvailable( void ); +BOOL getNVRecord2Driver( NV_DATA_T nvData, U08* bufferAddress, U32 bufferLength, + U08 numOfSnsrs2Check, ALARM_ID_T nvAlarm ); + +// Wrapper functions to other NVM files +void execNVMProcessRecord( void ); +SELF_TEST_STATUS_T execNVMSelfTest( void ); +/**@}*/ + +#endif /* _NV_MGMT_DD_H_ */