FLEX-FORD-OBC-BM/Source/bsw/Fbl/fbl_mem.c

6013 lines
278 KiB
C
Raw Permalink Normal View History

2026-03-19 11:49:16 +01:00
/***********************************************************************************************************************
* FILE DESCRIPTION
* ------------------------------------------------------------------------------------------------------------------*/
/** \file
* \brief Library containing common functionality for memory programming:
* - Erase of memory region
* - Data processing (e.g. decryption and decompression)
* - Segmentation and alignment
* - Signature / checksum verification over RAM and ROM contents
* - Pipelined programming
*
* Used by OEM dependent diagnostics to program incoming download data.
*
* --------------------------------------------------------------------------------------------------------------------
* COPYRIGHT
* --------------------------------------------------------------------------------------------------------------------
* \par Copyright
* \verbatim
* Copyright (c) 2025 by Vector Informatik GmbH. All rights reserved.
*
* This software is copyright protected and proprietary to Vector Informatik GmbH.
* Vector Informatik GmbH grants to you only those rights as set out in the license conditions.
* All other rights remain with Vector Informatik GmbH.
* \endverbatim
*/
/**********************************************************************************************************************/
/***********************************************************************************************************************
* REVISION HISTORY
* --------------------------------------------------------------------------------------------------------------------
* Version Date Author Change Id Description
* --------------------------------------------------------------------------------------------------------------------
* 01.00.00 2012-03-23 visjhg - Initial release
* 01.00.01 2012-03-26 visjhg - Added description to file header
* - Added unsigned qualifier to constants
* 01.01.00 2012-04-05 visjhg ESCAN00057963 Initialization of return value in segment end indication when
* remainder handling is disabled
* Changes and optimizations after code review
* 01.02.00 2012-04-27 visjhg ESCAN00058452 Added support for processed length
* ESCAN00058621 Added __ApplFblMemCopyBuffer
* 01.03.00 2012-06-22 visjhg ESCAN00059475 Restore preamble in segment end indication
* ESCAN00059477 Restore default preamble offset in segment end indication
* Disable remainder handling for volatile memory
* Check for potential buffer overflow in FblMemGetActiveBuffer
* 01.04.00 2013-02-01 visjhg ESCAN00064290 Extensions for multi processor systems (pass-through)
* Rework of block start indication interface
* Pass additional info in structure
* visjhg ESCAN00064292 Added block erase
* visjhg ESCAN00064296 Preamble length switchable at runtime
* visjhg ESCAN00064301 Added signature verification on ROM contents
* Rework of block verify interface
* Keep track of segment history (structure provided externally)
* Extended pass-through interface for BlockVerify to pass
* user-specific information
* visjhg ESCAN00061335 Accept buffer offsets differing from constant preamble length
* in data indication
* visjhg ESCAN00064333 Differentiate watchdog trigger w/ and w/o status
* visjhg ESCAN00061764 Rework of function return paths
* visjhg ESCAN00061814 Encapsulate FblMemQueuePrepend with FBL_ENABLE_DATA_PROCESSING
* visjhg ESCAN00064338 Added post handler for SegmentEndIndication
* visach ESCAN00062919 Adapted comments to use Doxygen
* visjhg ESCAN00064334 Added interface to remap error codes to OEM dependent value
* visjhg ESCAN00064339 Segmented data processing: data produced during one cycle may be
* smaller than buffer size
* visjhg ESCAN00064330 Explicit resume of suspended operation
* Added state "suspend pending"
* visjhg ESCAN00064343 Added support for multiple input sources
* visjhg ESCAN00064543 Release active buffer in case of failed processing
* visjhg ESCAN00064720 Replaced __ApplFblMemCheckDfi by __ApplFblMemIsDataProcessingRequired
* 01.05.00 2013-04-10 visjhg ESCAN00064871 Reserve space for remainder data to be programmed
* visjhg ESCAN00064890 Move processing queue handle to global context
* visjhg - Minor fixes
* visjhg ESCAN00065830 Encapsulate setting of gProgContext
* visjhg ESCAN00066375 Restore original data after programming padded buffer
* Relocate buffer offset or search matching input buffer
* visjhg ESCAN00066377 Encapsulate erase functionality
* visjhg ESCAN00066379 Added interface version compatibility check
* visjhg ESCAN00066380 Exported FblMemInit
* 01.06.00 2013-07-22 visase ESCAN00066743 Fixed compiler warning regarding gProgContext
* visjhg ESCAN00067433 Added FblMemDeinit
* visjhg ESCAN00068321 Force response pending for erase operation
* visjhg ESCAN00069161 Added pipelined verification
* Rework of block start and verify interface
* Changed processing queue from FIFO to priority queue
* Added jobs for finalization of data processing and remainder
* General refactoring
* visjhg ESCAN00069190 Rework suspend operation
* 01.07.00 2013-08-16 visjhg ESCAN00069507 Corrected input type of FblMemSetInteger
* visjhg ESCAN00069803 Enable/disable verification at run-time
* visjhg ESCAN00069781 Corrected net size of data processing buffer
* Added canary word to detect buffer overflows
* visjhg ESCAN00069797 Set low priority of data processing equal to input priority
* visjhg ESCAN00069843 Limit restoring of data after padding to segmented input use-case
* Use dedicated buffer to store temporary data
* 02.00.00 2013-12-12 visjhg ESCAN00069945 Encapsulate write finalization entry in gLengthLimits
* visjhg ESCAN00072568 Perform input verification initialization on first segment start
* visjhg ESCAN00071344 Processed length: Limit programming to area defined by block
* visjhg - Additional canary word in front of buffer
* visjhg ESCAN00072569 Raised major version to match API version
* visjhg ESCAN00072156 Encapsulation of FblMemGetSpecificRemainder
* visjhg ESCAN00072570 Removed encapsulation of gProgContext
* visjhg ESCAN00072631 Set programming state to pending in FblMemProgramBuffer
* 02.01.00 2014-03-12 visjhg ESCAN00073504 No changes
* visjhg ESCAN00074066 Explicitly resume operation for finalization of pipelined verification
* 02.02.00 2014-05-09 visjhg ESCAN00075225 Restore default input offset for RAM data
* visjhg - Remove explicit memory qualifiers from __ApplFblMemCopyBuffer
* 03.00.00 2015-03-03 visjhg ESCAN00076591 Added support for external stream output
* visjhg ESCAN00077689 Finalize data processing when remainder handling is disabled
* - Support combination of stream output and pipelined verification
* visjhg ESCAN00077891 Added job for handling of segment address (pipelined verification)
* visjhg ESCAN00081491 Added support for resumable programming
* visjhg ESCAN00081494 Added support for verification on processed input data
* visjhg ESCAN00081493 Added selective pipelined programming (forced flush when disabled)
* 03.01.00 2015-04-23 visjhg ESCAN00082572 Introduced job class for handling of pipelined queues
* visjhg ESCAN00082605 Added support for reporting of progress information
* visjhg ESCAN00082606 Added support for gap fill (requires ordered segments)
* 03.01.01 2015-04-27 visjhg ESCAN00082700 Removed assertion for consumed buffer in FblProcessJob
* 03.01.02 2015-05-27 visjhg ESCAN00083138 Reworked padding byte handling and gap fill calculations
* 03.01.03 2015-06-13 visjhg ESCAN00083358 Don't update position after hashing of segment address (pipelined verification)
* visjhg ESCAN00083390 Update buffer size of pipelined verification read jobs
* visjhg ESCAN00083391 Unconditionally unblock pipelined verification queue when
* FBL_MEM_ENABLE_VERIFY_PIPELINED_ADDRESS_LENGTH is enabled
* visjhg ESCAN00083392 Pass correct block length to output verification
* 03.02.00 2015-07-21 visjhg ESCAN00084101 Unconditionally limit write length for
* FBL_ENABLE_ADAPTIVE_DATA_TRANSFER_RCRRP
* visjhg ESCAN00084102 No changes
* 03.02.01 2015-08-19 visjhg ESCAN00084279 Re-initialize data processing on snapshot restoration
* visjhg ESCAN00084559 Clear write remainder on snapshot creation
* visjhg ESCAN00084560 Force finalization when programming unprocessed data
* 03.02.02 2015-09-04 visjhg ESCAN00084994 Postpone remainder handling after checkpoint indication
* 04.00.00 2015-09-17 visjhg ESCAN00085249 Support combination of input and processed verification
* visjhg ESCAN00085250 Run-time decision whether address and length info is included in verification
* visjhg ESCAN00085251 Pass external segment info to __ApplFblMemIsPipelinedProgrammingDisabled
* 04.01.00 2016-04-01 visjhg ESCAN00087997 Rx interrupt: Critical section around crucial accesses to programming state
* visjhg ESCAN00088935 Corrected segment info pointer passed to __ApplFblMemIsPipelinedProgrammingDisabled
* 04.02.00 2016-10-06 visjhg ESCAN00090120 Changed segmentIndex parameter of FblMemPrepareVerifyPipeJob to vuintx type
* vishrf ESCAN00091253 Change allowed state when all data has been processed within the
* last chunk of data
* 04.02.01 2017-05-31 visjhg ESCAN00094695 Explicitly align gRemainderBuffer
* visjhg ESCAN00095201 Limit data processing input to less than 64 kB
* visjhg ESCAN00095356 Disable length check in FblMemDataIndication when stream output is active
* 04.03.00 2017-07-26 visjhg ESCAN00095772 Disable remainder handling for processed input data
* visjhg ESCAN00095774 Added FblMemFlushInputData
* visjhg ESCAN00095683 Place FblMemResumeIndication in RAMCODE section
* visjhg ESCAN00096075 Re-initialize input verification routines on snapshot restoration
* 04.03.01 2017-08-07 visjhg ESCAN00096209 Only update running input data length when data is unprocessed
* 04.04.00 2018-08-22 visjhg ESCAN00100464 Only store segments in snapshot when BlockStartIndication was executed before
* visjhg ESCAN00097115 Increase gap fill buffer to maximum segment size when necessary
* visjhg ESCAN00100482 Added hook __ApplFblMemConditionCheck before and after memory operations
* 04.04.01 2018-09-25 visjhg ESCAN00100850 No changes
* 04.05.00 2018-11-30 visjhg ESCAN00101500 No changes
* 04.05.01 2019-01-23 visjhg ESCAN00101568 Update segment information for pass-through use-case
* 04.05.02 2019-08-20 vistbe ESCAN00103967 Reinitialization is not executed on slave instances of LibMem
* 04.05.03 2019-10-02 visrie ESCAN00104203 Fixed resumable pipelined verification
* 04.06.00 2019-10-29 vistmo FBL-813 Migration to MISRA 2012
* 04.06.01 2020-03-18 visrie ESCAN00105683 Fixed pipelined verification handling
* 04.06.02 2020-05-11 visjdn ESCAN00105967 Fixed 0 length pipelined verification.
* 04.06.03 2020-07-03 vistmo ESCAN00106050 TransferData might be rejected
* 04.07.00 2021-04-12 lhopfhauer FBL-2187 Add support for Spec 1.3
* Added/adapted MemMap sections
* 04.08.00 2021-06-11 visjdn FBL-3560 No changes
* 04.09.00 2021-07-09 lhopfhauer FBL-3442 Add option to avoid abortion of verification procedure in case of error
* Updated MISRA justifications
* 04.09.01 2021-11-05 lhopfhauer ESCAN00110663 Verification failure for flash driver download
* 04.10.00 2022-05-02 lhopfhauer FBL-5067 Support embedded signatures with processed verification
* 04.10.01 2022-08-03 fmenke ESCAN00112185 Wrong verification result may be returned if any verification fails
* 04.10.02 2023-05-08 lhopfhauer ESCAN00114573 Processed verification not working with embedded signatures/checksums
* 04.11.00 2023-08-04 visrie FBL-5709 Allow Input length verification in resumable + pipelined
* programming config
* 04.12.00 2023-09-04 vistmo FBL-7572 Allow processed verification in combination with stream output
* 04.12.01 2024-02-02 lhopfhauer ESCAN00116407 Out of bounds write access
* ESCAN00116408 Resume may not work
* ESCAN00116457 Assertion missing in FblMemGet/SetInteger
* 04.13.00 2025-09-16 pharring FBL-10120 Add support for FblLib_Logger
* 04.13.01 2025-11-07 aeller ESCAN00121551 Download may fail when decompression is invoked with pending metadata only byte
* ESCAN00121700 No changes
* 04.14.00 2025-11-25 fmenke FBL-11808 Support passthrough HSM update
**********************************************************************************************************************/
#define FBL_MEM_SOURCE
/***********************************************************************************************************************
* INCLUDES
**********************************************************************************************************************/
#include "fbl_inc.h"
#include "fbl_mem.h"
/***********************************************************************************************************************
* VERSION
**********************************************************************************************************************/
#if ( FBLLIB_MEM_VERSION != 0x0414u ) || \
( FBLLIB_MEM_RELEASE_VERSION != 0x00u )
# error "Error in fbl_mem.c: Source and Header file are inconsistent!"
#endif
#if ( FBLLIB_MEM_VERSION != _FBLLIB_MEM_VERSION ) || \
( FBLLIB_MEM_RELEASE_VERSION != _FBLLIB_MEM_RELEASE_VERSION )
# error "Error in fbl_mem.c: Source and v_ver.h are inconsistent!"
#endif
/* Interface version compatibility check */
#if defined( FBL_MEM_API_REFERENCE_VERSION_MAJOR ) && \
defined( FBL_MEM_API_REFERENCE_VERSION_MINOR )
#else
# error "Error in fbl_mem.c: Interface version requirements not defined!"
#endif
#if ( FBL_MEM_API_REFERENCE_VERSION_MAJOR != FBL_MEM_API_VERSION_MAJOR) || \
( FBL_MEM_API_REFERENCE_VERSION_MINOR > FBL_MEM_API_VERSION_MINOR)
# error "Error in fbl_mem.c: Interface version compatibility check failed!"
#endif
/***********************************************************************************************************************
* DEFINES
**********************************************************************************************************************/
/** Internal maximum segment size */
#define FBL_MEM_SEGMENT_SIZE FBL_MAX_SEGMENT_SIZE
/*-- Remap configuration switches--------------------------------------------*/
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING ) || \
defined( FBL_ENABLE_UNALIGNED_DATA_TRANSFER )
/** Input buffer changed after data indication: Store and restore preamble */
# define FBL_MEM_ENABLE_PREAMBLE_HANDLING
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING || FBL_ENABLE_UNALIGNED_DATA_TRANSFER */
#if defined( FBL_ENABLE_DATA_PROCESSING ) || \
defined( FBL_ENABLE_UNALIGNED_DATA_TRANSFER )
/* No remainder handling necessary for single byte segments */
# if ( FBL_MEM_SEGMENT_SIZE > 1u )
/** Programming may result in remainder which has to be stored for next cycle */
# define FBL_MEM_ENABLE_REMAINDER_HANDLING
# endif /* FBL_MEM_SEGMENT_SIZE > 1u */
#endif /* FBL_ENABLE_DATA_PROCESSING || FBL_ENABLE_UNALIGNED_DATA_TRANSFER */
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING ) || \
defined( FBL_ENABLE_ADAPTIVE_DATA_TRANSFER_RCRRP )
/** Split buffer operations (write, data processing, verification and pass-through) into smaller pieces */
# define FBL_MEM_ENABLE_SEGMENTATION
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING || FBL_ENABLE_ADAPTIVE_DATA_TRANSFER_RCRRP */
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING ) || \
defined( FBL_ENABLE_PROCESSED_DATA_LENGTH )
/** Keep track of running remaining length of input data */
# define FBL_MEM_ENABLE_INPUT_LENGTH
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING || FBL_ENABLE_PROCESSED_DATA_LENGTH */
#if defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER ) && \
defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
/** Allow use of any available input buffer and not only active one */
# define FBL_MEM_ENABLE_VARYING_INPUT_BUFFER
#endif
#if defined( FBL_MEM_WRITE_SEGMENTATION )
#else
/** Set segmentation to default value */
# define FBL_MEM_WRITE_SEGMENTATION (FBL_MEM_BUFFER_SIZE + FBL_MEM_SEGMENT_SIZE)
#endif /* FBL_MEM_WRITE_SEGMENTATION */
#if defined( FBL_ENABLE_DATA_PROCESSING )
# if defined( FBL_MEM_ENABLE_SEGMENTATION )
# define FBL_MEM_INTERNAL_PROC_SEGMENTATION FBL_MEM_PROC_SEGMENTATION
# if ( FBL_MEM_INTERNAL_PROC_SEGMENTATION == FBL_MEM_PROC_BUFFER_SIZE)
/* Segmented processing enabled, but data processing segmentation matches buffer size */
# else
/* Split data processing into smaller pieces */
# define FBL_MEM_ENABLE_PROC_SEGMENTATION
# endif
# else
/* Fill data processing buffer at once */
# define FBL_MEM_INTERNAL_PROC_SEGMENTATION FBL_MEM_PROC_BUFFER_SIZE
# endif /* FBL_MEM_ENABLE_SEGMENTATION */
# if defined( FBL_MEM_TRY_COUNTER_LIMIT_DATA_PROC )
# else
/* Try counter limit for data processing to avoid endless loop */
# define FBL_MEM_TRY_COUNTER_LIMIT_DATA_PROC 1u
# endif /* FBL_MEM_TRY_COUNTER_LIMIT_DATA_PROC */
#endif /* FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/** Stream verification is active */
# define FBL_MEM_ENABLE_VERIFY_STREAM
#endif /* FBL_MEM_ENABLE_VERIFY_INPUT | FBL_MEM_ENABLE_VERIFY_PROCESSED | FBL_MEM_ENABLE_VERIFY_PIPELINED */
#if defined( FBL_MEM_ENABLE_VERIFY_STREAM ) || \
defined( FBL_MEM_ENABLE_VERIFY_OUTPUT )
/** Verification is active */
# define FBL_MEM_ENABLE_VERIFICATION
#endif /* FBL_MEM_ENABLE_VERIFY_STREAM | FBL_MEM_ENABLE_VERIFY_OUTPUT */
#if defined( FBL_MEM_VERIFY_SEGMENTATION )
# if defined( FBL_MEM_VERIFY_INPUT_SEGMENTATION )
# else
/** Set input verification length to common segmentation length */
# define FBL_MEM_VERIFY_INPUT_SEGMENTATION FBL_MEM_VERIFY_SEGMENTATION
# endif /* FBL_MEM_VERIFY_INPUT_SEGMENTATION */
# if defined( FBL_MEM_VERIFY_PIPELINED_SEGMENTATION )
# else
/** Set pipelined verification length to common segmentation length */
# define FBL_MEM_VERIFY_PIPELINED_SEGMENTATION FBL_MEM_VERIFY_SEGMENTATION
# endif /* FBL_MEM_VERIFY_PIPELINED_SEGMENTATION */
#endif /* FBL_MEM_VERIFY_SEGMENTATION */
#if defined( FBL_MEM_VERIFY_INPUT_SEGMENTATION )
#else
/** Set input verification length to default value */
# define FBL_MEM_VERIFY_INPUT_SEGMENTATION 64u
#endif /* FBL_MEM_VERIFY_INPUT_SEGMENTATION */
#if defined( FBL_MEM_VERIFY_PIPELINED_SEGMENTATION )
#else
/** Set pipelined verification length to default value */
# define FBL_MEM_VERIFY_PIPELINED_SEGMENTATION 64u
#endif /* FBL_MEM_VERIFY_PIPELINED_SEGMENTATION */
/** Response pending handling */
# define FBL_MEM_ENABLE_RESPONSE_PENDING
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
# if defined( FBL_MEM_ENABLE_SELECTIVE_PIPELINED_PROGRAMMING )
# define FBL_MEM_ENABLE_INPUT_DATA_FLUSH
# endif /* FBL_MEM_ENABLE_SELECTIVE_PIPELINED_PROGRAMMING */
#else
/** Flush input data in data indication function */
# define FBL_MEM_ENABLE_INPUT_DATA_FLUSH
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
#if defined( FBL_MEM_ENABLE_GAP_FILL )
# if defined( FBL_MEM_GAP_FILL_SEGMENTATION )
# else
/* Buffer size for gap fill function */
# if defined( FBL_MEM_WRITE_SEGMENTATION )
# if (FBL_MEM_SEGMENT_SIZE > FBL_MEM_WRITE_SEGMENTATION)
# define FBL_MEM_GAP_FILL_SEGMENTATION FBL_MEM_SEGMENT_SIZE
# else
# define FBL_MEM_GAP_FILL_SEGMENTATION FBL_MEM_WRITE_SEGMENTATION
# endif
# else
# define FBL_MEM_GAP_FILL_SEGMENTATION FBL_MEM_SEGMENT_SIZE
# endif /* FBL_MEM_WRITE_SEGMENTATION */
# endif /* FBL_MEM_GAP_FILL_SEGMENTATION */
#endif /* FBL_MEM_ENABLE_GAP_FILL */
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
# if defined( FBL_MEM_PROGRESS_ERASE )
# else
/** Progress percentage of erase operation */
# define FBL_MEM_PROGRESS_ERASE 10u
# endif /* FBL_MEM_PROGRESS_ERASE */
# if defined( FBL_MEM_PROGRESS_VERIFY )
# else
/** Progress percentage of final verify operation */
# define FBL_MEM_PROGRESS_VERIFY 10u
# endif /* FBL_MEM_PROGRESS_VERIFY */
# if defined( FBL_MEM_PROGRESS_INITIAL )
# else
/** Initial progress value of overall progress and partial progress */
# define FBL_MEM_PROGRESS_INITIAL 0u
# endif /* FBL_MEM_PROGRESS_INITIAL */
# if defined( FBL_MEM_PROGRESS_COMPLETE )
# else
/** Final progress value of overall progress and partial progress */
# define FBL_MEM_PROGRESS_COMPLETE 100u
# endif /* FBL_MEM_PROGRESS_COMPLETE */
# if defined( FBL_MEM_PROGRESS_PROGRAM )
# else
/** Progress percentage of program operation */
# define FBL_MEM_PROGRESS_PROGRAM (FBL_MEM_PROGRESS_COMPLETE - FBL_MEM_PROGRESS_ERASE - FBL_MEM_PROGRESS_VERIFY)
# endif /* FBL_MEM_PROGRESS_PROGRAM */
# if defined( FBL_MEM_PROGRESS_THRESHOLD_BYTES )
# else
/** Threshold for consecutive calls to reporting callback (in bytes) */
# define FBL_MEM_PROGRESS_THRESHOLD_BYTES 1024u
# endif /* FBL_MEM_PROGRESS_THRESHOLD_BYTES */
# if defined( FBL_MEM_PROGRESS_THRESHOLD_PERCENTAGE )
# else
/** Threshold for consecutive calls to reporting callback (in percentage points) */
# define FBL_MEM_PROGRESS_THRESHOLD_PERCENTAGE 5u
# endif /* FBL_MEM_PROGRESS_THRESHOLD_PERCENTAGE */
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
/*-- Processing queue -------------------------------------------------------*/
/* Number of input buffers */
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
# if defined( FBL_MEM_PIPE_PROG_BUFFER_COUNT )
/** Overwrite default value */
# define FBL_MEM_QUEUE_ENTRIES_INPUT FBL_MEM_PIPE_PROG_BUFFER_COUNT
# else
/** Default value: Alternating input buffers */
# define FBL_MEM_QUEUE_ENTRIES_INPUT 2u
# endif /* FBL_MEM_PIPE_PROG_BUFFER_COUNT */
#else
/** Single buffer use-case */
# define FBL_MEM_QUEUE_ENTRIES_INPUT 1u
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
/** Actual buffer count
* Input queue shared amongst all input sources */
#define FBL_MEM_BUFFER_COUNT_INPUT ((FBL_MEM_QUEUE_ENTRIES_INPUT + FBL_MEM_SOURCE_COUNT) - 1u)
# define FBL_MEM_QUEUE_ENTRIES_VERIFY_INPUT 0u
# define FBL_MEM_QUEUE_ENTRIES_VERIFY_PROCESSED 0u
#if defined( FBL_ENABLE_DATA_PROCESSING )
/** Reserve entries for write and finalization jobs */
# define FBL_MEM_QUEUE_ENTRIES_DATA_PROC 2u
#else
# define FBL_MEM_QUEUE_ENTRIES_DATA_PROC 0u
#endif /* FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
/** Reserve entries for write and finalization jobs */
# define FBL_MEM_QUEUE_ENTRIES_STREAM_OUTPUT 2u
#else
# define FBL_MEM_QUEUE_ENTRIES_STREAM_OUTPUT 0u
#endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/** Reserve entry for write remainder finalization
* Only added in case processing queue already used */
# define FBL_MEM_QUEUE_ENTRIES_REMAINDER 1u
#else
# define FBL_MEM_QUEUE_ENTRIES_REMAINDER 0u
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
# define FBL_MEM_QUEUE_ENTRIES_CHECKPOINT 0u
#if defined( FBL_MEM_ENABLE_GAP_FILL )
/** Reserve entry for gap fill */
# define FBL_MEM_QUEUE_ENTRIES_GAP_FILL 1u
#else
# define FBL_MEM_QUEUE_ENTRIES_GAP_FILL 0u
#endif /* FBL_MEM_ENABLE_GAP_FILL */
/** Total number of processing queue entries */
#define FBL_MEM_QUEUE_ENTRIES_PROCESSING \
( FBL_MEM_QUEUE_ENTRIES_VERIFY_INPUT + FBL_MEM_QUEUE_ENTRIES_VERIFY_PROCESSED + FBL_MEM_QUEUE_ENTRIES_DATA_PROC \
+ FBL_MEM_QUEUE_ENTRIES_STREAM_OUTPUT + FBL_MEM_QUEUE_ENTRIES_INPUT + FBL_MEM_QUEUE_ENTRIES_VERIFY_PIPE \
+ FBL_MEM_QUEUE_ENTRIES_CHECKPOINT + FBL_MEM_QUEUE_ENTRIES_GAP_FILL )
#if ( FBL_MEM_QUEUE_ENTRIES_PROCESSING > 1u )
/** Activate processing queue */
# define FBL_MEM_ENABLE_PROC_QUEUE
/** Add remainder handling to processing queue */
# define FBL_MEM_QUEUE_ENTRIES_TOTAL (FBL_MEM_QUEUE_ENTRIES_PROCESSING + FBL_MEM_QUEUE_ENTRIES_REMAINDER)
#else
/** Single buffer use-case: no processing queue required */
# define FBL_MEM_DISABLE_PROC_QUEUE
#endif /* FBL_MEM_QUEUE_ENTRIES_PROCESSING > 1u */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
# if defined( FBL_MEM_ENABLE_PROC_QUEUE )
# else
/** Update single write job to trigger remainder flush */
# define FBL_MEM_ENABLE_REMAINDER_HANDLING_SINGLE_JOB
# endif /* FBL_MEM_ENABLE_PROC_QUEUE */
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
/* Processing queue specific */
/** Reserve two entries in data structure for explicit free and used head */
# define FBL_MEM_RESERVED_QUEUE_ENTRIES 2u
/* Handles for free and used head */
# define FBL_MEM_QUEUE_HANDLE_HEAD_USED 0u
# define FBL_MEM_QUEUE_HANDLE_HEAD_FREE 1u
/* Start handle for actual entries */
# define FBL_MEM_QUEUE_HANDLE_ENTRY_OFFSET 2u
/** Size of data structure for processing queue */
# define FBL_MEM_QUEUE_SIZE_PROCESSING (FBL_MEM_RESERVED_QUEUE_ENTRIES + FBL_MEM_QUEUE_ENTRIES_TOTAL)
# if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
/** Size of data structure for pipelined programming queue */
# define FBL_MEM_QUEUE_SIZE_PIPE_PROG (FBL_MEM_RESERVED_QUEUE_ENTRIES + FBL_MEM_QUEUE_ENTRIES_INPUT)
# endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
# define FBL_MEM_QUEUE_SIZE_PIPE_VERIFY (FBL_MEM_RESERVED_QUEUE_ENTRIES + FBL_MEM_QUEUE_ENTRIES_VERIFY_PIPE_READ)
# endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
/*
Access macros to check for empty queues
A queue is empty if the head references itself (cyclic double linked list)
*/
# define FblMemQueueIsEmpty(queue) (FBL_MEM_QUEUE_HANDLE_HEAD_USED == (queue)[FBL_MEM_QUEUE_HANDLE_HEAD_USED].next)
# define FblMemQueueIsFull(queue) (FBL_MEM_QUEUE_HANDLE_HEAD_FREE == (queue)[FBL_MEM_QUEUE_HANDLE_HEAD_FREE].next)
/* Access macros to get handle of first entry */
# define FblMemQueueGetFirstUsedHandle(queue) ((queue)[FBL_MEM_QUEUE_HANDLE_HEAD_USED].next)
# define FblMemQueueGetLastUsedHandle(queue) ((queue)[FBL_MEM_QUEUE_HANDLE_HEAD_USED].prev)
# define FblMemQueueGetFirstFreeHandle(queue) ((queue)[FBL_MEM_QUEUE_HANDLE_HEAD_FREE].next)
/** Access macro to entry using handle */
# define FblMemQueueGetEntry(queue, handle) ((queue)[handle])
/* Access macros to get first or last entry */
# define FblMemQueueGetFirstUsedEntry(queue) (FblMemQueueGetEntry((queue), FblMemQueueGetFirstUsedHandle(queue)))
# define FblMemQueueGetLastUsedEntry(queue) (FblMemQueueGetEntry((queue), FblMemQueueGetLastUsedHandle(queue)))
# define FblMemQueueGetFirstFreeEntry(queue) (FblMemQueueGetEntry((queue), FblMemQueueGetFirstFreeHandle(queue)))
#endif /* FBL_MEM_ENABLE_PROC_QUEUE */
/* Default priorities for queue entries */
# define FBL_MEM_QUEUE_PRIO_LOWEST 0x00u
# define FBL_MEM_QUEUE_PRIO_HIGHEST 0xFFu
/** Null queue entry pointer */
# define FBL_MEM_QUEUE_NULL ((V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 *)V_NULL)
/** Null job pointer */
# define FBL_MEM_JOB_NULL ((V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 *)V_NULL)
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
#else
/** Remap fill buffer for single input buffer use-case */
# define FblMemGetPendingInputJob() (&FBL_MEM_INPUT_JOB[0])
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
/*-- Verification -----------------------------------------------------------*/
/** Null verification status pointer */
#define FBL_MEM_VERIFY_STATUS_NULL ((V_MEMRAM1 tFblMemVerifyStatus V_MEMRAM2 V_MEMRAM3 *)V_NULL)
#if defined( FBL_MEM_ENABLE_VERIFY_STREAM )
/** Null input verification function pointer (use of 0 avoid cast from pointer to object) */
# define FBL_MEM_VERIFY_FCT_INPUT_NULL ((tFblMemVerifyFctInput)0)
#endif /* FBL_MEM_ENABLE_VERIFY_STREAM */
#if defined( FBL_MEM_ENABLE_VERIFY_OUTPUT )
/** Null input verification function pointer (use of 0 avoid cast from pointer to object) */
# define FBL_MEM_VERIFY_FCT_OUTPUT_NULL ((tFblMemVerifyFctOutput)0)
#endif /* FBL_MEM_ENABLE_VERIFY_OUTPUT */
/*-- Segment and platform alignment -----------------------------------------*/
/** Calculate remainder for given address range and segment size */
#define FblMemGetRemainder(address, length, segSize) ((tFblLength)(((address) + (tFblAddress)(length)) & ((tFblAddress)(segSize) - 1u)))
/** Remainder for global segment size */
#define FblMemGetGlobalRemainder(address, length) FblMemGetRemainder((address), (length), FBL_MEM_SEGMENT_SIZE)
/* Use global or device specific segment size for remainder determination? */
# if defined( FBL_ENABLE_MULTIPLE_MEM_DEVICES ) && \
defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
# define FblMemGetWriteRemainder(address, length) FblMemGetSpecificRemainder((address), (length))
# else
/** Only one device present, global remainder equals device specific remainder */
# define FblMemGetWriteRemainder(address, length) FblMemGetGlobalRemainder((address), (length))
# endif /* FBL_ENABLE_MULTIPLE_MEM_DEVICES || FBL_MEM_ENABLE_REMAINDER_HANDLING */
/* Buffer padding for potential write remainder */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/* Reserve space for remainder from previous cycle and one for buffer padding for last write */
# define FBL_MEM_REMAINDER_PADDING (FBL_MEM_SEGMENT_SIZE - 1u)
/* Reserve space for buffer padding of last write */
# define FBL_MEM_WRITE_PADDING (FBL_MEM_SEGMENT_SIZE - 1u)
#else
# define FBL_MEM_REMAINDER_PADDING 0u
# define FBL_MEM_WRITE_PADDING 0u
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
#define FBL_MEM_TOTAL_PADDING (FBL_MEM_REMAINDER_PADDING + FBL_MEM_WRITE_PADDING)
#if defined( FBL_ENABLE_SYSTEM_CHECK )
/** Magic value of "canary" word used to detect buffer overflows (ASCII "Bird") */
# define FBL_MEM_CANARY_VALUE 0x42697264uL
#endif /* FBL_ENABLE_SYSTEM_CHECK */
#if defined( C_CPUTYPE_32BIT ) || \
defined( C_CPUTYPE_16BIT )
# define FBL_MEM_PLATFORM_ALIGN 4u
# define FBL_MEM_PLATFORM_ALIGN_MASK 0x03u
#else /* C_CPUTYPE_8BIT */
# define FBL_MEM_PLATFORM_ALIGN 1u
# define FBL_MEM_PLATFORM_ALIGN_MASK 0x00u
#endif /* C_CPUTYPE_32BIT || C_CPUTYPE_16BIT */
#if defined( FBL_ENABLE_SYSTEM_CHECK )
/** Macro to generate basic data type including buffer alignment to platform requirements */
# define FBL_MEM_ALIGNED_BUFFER_TYPE(size) \
struct \
{ \
/** Magic value of "canary" word used to detect buffer overflows */ \
vuint32 canaryFront; \
/** Reserve buffer for configured size and potential alignment */ \
vuint8 data[size]; \
/** Magic value of "canary" word used to detect buffer overflows */ \
vuint32 canaryBack; \
}
#else
/* Force alignment to 32 bit boundary (if required by platform) */
# if defined( C_CPUTYPE_32BIT ) || \
defined( C_CPUTYPE_16BIT )
/** Macro to generate basic data type including buffer alignment to platform requirements */
# define FBL_MEM_ALIGNED_BUFFER_TYPE(size) \
struct \
{ \
vuint32 alignDummy; \
/** Reserve buffer for configured size and potential alignment */ \
vuint8 data[size]; \
}
# else /* C_CPUTYPE_8BIT */
/** Macro to generate basic data type including buffer alignment to platform requirements */
# define FBL_MEM_ALIGNED_BUFFER_TYPE(size) \
struct \
{ \
/** Reserve buffer for configured size and potential alignment */ \
vuint8 data[size]; \
}
# endif /* C_CPUTYPE_32BIT || C_CPUTYPE_16BIT */
#endif /* FBL_ENABLE_SYSTEM_CHECK */
/** Round length up to next multiple of alignment */
#define FBL_MEM_LENGTH_ALIGN(length, align) (((((length) - 1u) / (align)) + 1u) * (align))
/** Align space used by preamble to multiple of platform requirement */
#define FBL_MEM_PREAMBLE_ALIGN(length) FBL_MEM_LENGTH_ALIGN((length), FBL_MEM_PLATFORM_ALIGN)
/** As preamble always lies within the actual buffer it can be subtracted from the total length */
#define FBL_MEM_PREAMBLE_OFFSET(length) (FBL_MEM_PREAMBLE_ALIGN(length) - (length))
/** Padded buffer size for input buffers (including maximum platform alignment) */
#define FBL_MEM_PADDED_BUFFER_SIZE (FBL_MEM_BUFFER_SIZE + (FBL_MEM_PLATFORM_ALIGN - 1u) + FBL_MEM_TOTAL_PADDING)
#if defined( FBL_ENABLE_DATA_PROCESSING )
/** Padded buffer size for data processing buffers */
# define FBL_MEM_PADDED_PROC_BUFFER_SIZE (FBL_MEM_PROC_BUFFER_SIZE + FBL_MEM_TOTAL_PADDING)
#endif /* FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_MULTI_SOURCE )
/** Dynamic input source */
# define FBL_MEM_ACTIVE_SOURCE gActiveSource
#else
/** Fixed input source */
# define FBL_MEM_ACTIVE_SOURCE 0u
# if defined( FBL_MEM_SOURCE_COUNT )
# else
/** Set input source count if not already defined */
# define FBL_MEM_SOURCE_COUNT 1u
# endif
#endif /* FBL_MEM_ENABLE_MULTI_SOURCE */
#if defined( FBL_MEM_ENABLE_DYNAMIC_PREAMBLE_LENGTH )
/** Dynamic preamble length stored per input source */
# define FBL_MEM_PREAMBLE_LENGTH gPreambleLength[FBL_MEM_ACTIVE_SOURCE]
#else
/** Fixed preamble length */
# define FBL_MEM_PREAMBLE_LENGTH FBL_MEM_DEFAULT_PREAMBLE_LENGTH
#endif /* FBL_MEM_ENABLE_DYNAMIC_PREAMBLE_LENGTH */
/** Substitution macro to access input buffers depending on active input source */
#define FBL_MEM_INPUT_JOB gInputJobs[FBL_MEM_ACTIVE_SOURCE]
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
/** Substitution macro to access pipelined programming queue depending on active input source */
# define FBL_MEM_PIPE_PROG_QUEUE gPipeProgQueue[FBL_MEM_ACTIVE_SOURCE]
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
/** Helper macro to get number of array entries */
#define FBL_MEM_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#if defined( FBL_MEM_ENABLE_SEGMENTATION ) || \
defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
# if defined( FBL_ENABLE_ADAPTIVE_DATA_TRANSFER_RCRRP )
/** Unconditionally limited for proper RCR-RP handling */
# define FBL_MEM_FINALIZE_UNLIMITED_MODE kFblMemOperationMode_Unconditional
# else
/** Write complete remaining data at once during finalization */
# define FBL_MEM_FINALIZE_UNLIMITED_MODE kFblMemOperationMode_Finalize
# endif /* FBL_ENABLE_ADAPTIVE_DATA_TRANSFER_RCRRP */
#else
/** Do not limit input length, process complete buffer */
# define FblMemLimitLength(inputLen, type, finalize) (inputLen)
#endif /* FBL_MEM_ENABLE_SEGMENTATION || FBL_MEM_ENABLE_VERIFY_PIPELINED */
#if defined( __ApplFblMemCopyBuffer )
#else
/* PRQA S 0602, 0603 2 */ /* MD_FblMem_0602_0603 */
/** Copy input buffer to destination address */
# define __ApplFblMemCopyBuffer(address, data, length) (void)MEMCPY((address), (data), (length))
#endif /* __ApplFblMemCopyBuffer */
/** Null buffer pointer */
# define FBL_MEM_BUFFER_NULL ((tFblMemRamData)V_NULL)
/*-- Indication function order ----------------------------------------------*/
/*
Allowed indication order:
Init(PowerOn)
|
+-------->+--------+
| V |
| +-> BlockErase |
| +-------+ |
| V |
| BlockStart <-+
| |
| V
| SegmentStart <-+
| | |
| V |
| +-> Data |
| +-----+ |
| V |
| SegmentEnd |
| +---------+
| V
| BlockEnd
| |
| V
| BlockVerify <-+
| | |
+---------+--------+
*/
/* Bitmasks for allowed states */
#define FBL_MEM_ALLOWED_NONE 0x00u
#define FBL_MEM_ALLOWED_BLOCK_START 0x01u
#define FBL_MEM_ALLOWED_SEGMENT_START 0x02u
#define FBL_MEM_ALLOWED_DATA_IND 0x04u
#define FBL_MEM_ALLOWED_SEGMENT_END 0x08u
#define FBL_MEM_ALLOWED_BLOCK_END 0x10u
#define FBL_MEM_ALLOWED_BLOCK_VERIFY 0x20u
#define FBL_MEM_ALLOWED_BLOCK_ERASE 0x40u
/* Access macros for handling of allowed states */
/** Reset all states */
#define FblMemResetAllowed() FblMemSetAllowed(FBL_MEM_ALLOWED_NONE)
/** Set states according to bitmask (overwriting previous settings) */
# define FblMemSetAllowed(mask) (gAllowedInd = (tFblMemAllowedInd)(mask))
/** Add states according to bitmask (previous settings unchanged) */
# define FblMemAddAllowed(mask) (gAllowedInd |= (tFblMemAllowedInd)(mask))
/** Clear states according to bitmask */
#define FblMemClrAllowed(mask) (gAllowedInd &= FblInvertBits(mask, tFblMemAllowedInd))
/** Check for required states against bitmask */
#define FblMemIsAllowed(mask) ((gAllowedInd & (tFblMemAllowedInd)(mask)) == (tFblMemAllowedInd)(mask))
/** Check if any of the required states is currently allowed */
#define FblMemIsAnyAllowed(mask) ((gAllowedInd & (tFblMemAllowedInd)(mask)) != (tFblMemAllowedInd)(FBL_MEM_ALLOWED_NONE))
/*-- Error handling ---------------------------------------------------------*/
/** Dummy value for errors without extended status */
#define FBL_MEM_EXT_STATUS_NONE 0x00u
/** Remap errors without extended status to common macro */
#define FBL_MEM_SET_STATUS(status, var) FBL_MEM_SET_EXT_STATUS(status, FBL_MEM_EXT_STATUS_NONE, var)
/* Extended status configured? */
#if defined( FBL_MEM_ENABLE_EXT_STATUS )
# define FBL_MEM_SET_EXT_STATUS(status, ext, var) { \
FBL_MEM_EXT_STATUS_ ## status(ext); \
(var) = kFblMemStatus_ ## status; \
}
/* Pass extended info without setting error code */
# define FBL_MEM_SET_EXT_INFO(type, info) FBL_MEM_EXT_STATUS_ ## type(info) /* PRQA S 0342 */ /* MD_MSR_Rule20.10_0342 */
#else
/* Set error code, no extended status */
# define FBL_MEM_SET_EXT_STATUS(status, ext, var) (var) = kFblMemStatus_ ## status /* PRQA S 0342 */ /* MD_MSR_Rule20.10_0342 */
# define FBL_MEM_SET_EXT_INFO(type, info)
#endif /* FBL_MEM_ENABLE_EXT_STATUS */
/** Assertion codes */
#define kFblMemAssertParameterOutOfRange 0x01u
#define kFblMemAssertUserResultOutOfRange 0x02u
/*-- Critical sections ------------------------------------------------------*/
/* Allow override in configuration */
#if defined( __ApplFblMemEnterCriticalSection )
#else
/** Enter critical section: Do nothing in default configuration */
# define __ApplFblMemEnterCriticalSection() /* PRQA S 0602, 0603 */ /* MD_FblMem_0602_0603 */
#endif /* __ApplFblMemEnterCriticalSection */
/* Allow override in configuration */
#if defined( __ApplFblMemLeaveCriticalSection )
#else
/** Leave critical section: Do nothing in default configuration */
# define __ApplFblMemLeaveCriticalSection() /* PRQA S 0602, 0603 */ /* MD_FblMem_0602_0603 */
#endif /* __ApplFblMemLeaveCriticalSection */
/***********************************************************************************************************************
* TYPEDEFS
**********************************************************************************************************************/
/*-- Indication function order ----------------------------------------------*/
/** States for indication order */
typedef vuintx tFblMemAllowedInd;
/*-- Buffer handling --------------------------------------------------------*/
/** Job types */
typedef enum
{
kFblMemJobType_InputWrite = 0u /**< Input buffer, write through without any data processing */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
, kFblMemJobType_WriteFinalize /**< Trigger programming of write remainder */
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
#if defined( FBL_ENABLE_DATA_PROCESSING )
, kFblMemJobType_ProcInput /**< Input buffer, process data before writing */
, kFblMemJobType_ProcWrite /**< Write buffer for processed data */
, kFblMemJobType_ProcFinalize /**< Trigger for data processing finalization */
#endif /* FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
# if defined( FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH )
, kFblMemJobType_VerifyPipeInfo /**< Update pipelined verification hash with segment information */
# endif /* FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH */
, kFblMemJobType_VerifyPipeRead /**< Read back data already programmed for pipelined verification */
, kFblMemJobType_VerifyPipeUpdate /**< Update pipelined verification hash */
, kFblMemJobType_VerifyPipeFinalize /**< Trigger finalization of pipelined verification */
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
#if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
, kFblMemJobType_StreamInput /**< Input buffer, pass to stream output */
, kFblMemJobType_StreamProc /**< Pass processed data to stream output */
, kFblMemJobType_StreamFinalize /**< Trigger for data processing finalization */
#endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
#if defined( FBL_MEM_ENABLE_GAP_FILL )
, kFblMemJobType_GapFill /**< Fill gaps between programmed segments */
#endif /* FBL_MEM_ENABLE_GAP_FILL */
, kFblMemJobType_Max
} tFblMemJobType;
/** Job class, initiate special handling on completion */
#if defined( FBL_MEM_ENABLE_PIPELINING )
typedef enum
{
tFblMemJobClass_Default /**< Default job, no special handling required */
# if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
, tFblMemJobClass_PipeProg /**< Pipelined programming job */
# endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
# if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
, tFblMemJobClass_VerifyPipe /**< Pipelined verification job */
# endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
} tFblMemJobClass;
#endif /* FBL_MEM_ENABLE_PIPELINING */
/** Flag to indicate finalization to subfunction */
typedef enum
{
kFblMemOperationMode_Normal = 0u, /**< Normal operation, job complete as soon as "used" member reaches zero */
kFblMemOperationMode_Finalize, /**< Finalize operation */
kFblMemOperationMode_Unconditional /**< Job unconditionally completed after first run */
} tFblMemOperationMode;
/** Aligned input buffer type */
typedef FBL_MEM_ALIGNED_BUFFER_TYPE(FBL_MEM_PADDED_BUFFER_SIZE) tFblMemInputBuffer;
#if defined( FBL_ENABLE_DATA_PROCESSING )
/** Aligned buffer type for data processing */
typedef FBL_MEM_ALIGNED_BUFFER_TYPE(FBL_MEM_PADDED_PROC_BUFFER_SIZE) tFblMemProcBuffer;
#endif /* FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_GAP_FILL )
/** Aligned buffer type for gap fill */
typedef FBL_MEM_ALIGNED_BUFFER_TYPE(FBL_MEM_GAP_FILL_SEGMENTATION) tFblMemGapFillBuffer;
#endif /* FBL_MEM_ENABLE_GAP_FILL */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/** Aligned buffer type for remainder handling */
typedef FBL_MEM_ALIGNED_BUFFER_TYPE(FBL_MEM_SEGMENT_SIZE) tFblMemRemainderBuffer;
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
/** Job information */
typedef struct
{
tFblMemRamData buffer; /**< Pointer to referenced buffer */
tFblLength totalSize; /**< Total size of referenced buffer */
tFblLength netSize; /**< Size actually usable for input data */
tFblLength offset; /**< Offset of actual buffer content */
tFblLength position; /**< Current position in buffer */
tFblLength used; /**< Current data length */
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED ) || \
defined( FBL_MEM_ENABLE_GAP_FILL )
tFblAddress baseAddress; /**< Memory base address */
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED || FBL_MEM_ENABLE_RESUMABLE_PROGRAMMING || FBL_MEM_ENABLE_GAP_FILL */
tFblMemJobType type; /**< Job type */
tFblMemOperationMode completion; /**< Handling of job completion */
vuintx segmentIndex; /**< Index of segment associated with job */
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
#if defined( FBL_MEM_ENABLE_PIPELINING )
tFblMemJobClass jobClass; /**< Job class, initiate special handling on completion */
#endif /* FBL_MEM_ENABLE_PIPELINING */
} tFblMemJob;
/** Module internal segment information */
typedef struct
{
tFblMemSegmentInfo input; /**< Input segment information */
/* Internal attributes */
tFblAddress writeAddress; /**< Current address */
tFblLength writeRemainder; /**< Unwritten remainder */
tFblLength writeLength; /**< Remaining length */
#if defined( FBL_ENABLE_PROCESSED_DATA_LENGTH )
tFblLength writtenLength; /**< Total written length */
#endif /* FBL_ENABLE_PROCESSED_DATA_LENGTH */
#if defined( FBL_ENABLE_UNALIGNED_DATA_TRANSFER )
tFblLength inputAddress; /**< Running address of input data */
#endif /* FBL_ENABLE_UNALIGNED_DATA_TRANSFER */
#if defined( FBL_MEM_ENABLE_INPUT_LENGTH )
tFblLength inputLength; /**< Running remaining length of input data */
#endif /* FBL_MEM_ENABLE_INPUT_LENGTH */
#if defined( FBL_ENABLE_DATA_PROCESSING ) || \
defined( FBL_MEM_ENABLE_STREAM_OUTPUT ) || \
defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
tFblMemJobType jobType; /**< Remember input job types */
#endif /* FBL_ENABLE_DATA_PROCESSING || FBL_MEM_ENABLE_STREAM_OUTPUT || FBL_MEM_ENABLE_PASSTHROUGH || FBL_MEM_ENABLE_REMAINDER_HANDLING */
vuintx ownIndex; /**< Index of segment */
vuintx nextIndex; /**< Index of next segment */
} tFblMemSegmentInternal;
/*-- Processing queue -------------------------------------------------------*/
/** Priority of queue entry */
typedef vuint8 tFblMemQueuePrio;
/** Job priorities */
/* PRQA S 0724 TAG_FblMem_0724 */ /* MD_FblMem_0724_EnumValNotUnique */
typedef enum
{
/* PRQA S 3205 1 */ /* MD_FblMem_3205_IdentifierNotUsed */
kFblMemJobPrio_Lowest = FBL_MEM_QUEUE_PRIO_LOWEST, /**< Lowest job priority */
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
kFblMemJobPrio_VerifyPipeFinalize, /**< Finalization of pipelined verification */
kFblMemJobPrio_VerifyPipeInput, /**< Not actually used, but acts as a common priority
* for all pipelined verification jobs which
* generate input data for update job */
# if defined( FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH )
kFblMemJobPrio_VerifyPipeInfo = kFblMemJobPrio_VerifyPipeInput, /**< Pipelined verification segment information */
# endif /* FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH */
kFblMemJobPrio_VerifyPipeRead = kFblMemJobPrio_VerifyPipeInput, /**< Pipelined verification read */
kFblMemJobPrio_VerifyPipeUpdate, /**< Pipelined verification update */
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
kFblMemJobPrio_Write, /**< Helper entry for flushing of all pending write jobs */
#if defined( FBL_MEM_ENABLE_GAP_FILL )
kFblMemJobPrio_GapFill, /**< Fill gaps between programmed segments */
#endif /* FBL_MEM_ENABLE_GAP_FILL */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/* PRQA S 3205 1 */ /* MD_FblMem_3205_IdentifierNotUsed */
kFblMemJobPrio_WriteFinalize, /**< Finalize remainder write */
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
#if defined( FBL_ENABLE_DATA_PROCESSING )
kFblMemJobPrio_ProcFinalize, /**< Finalization of data processing */
#endif /* FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
kFblMemJobPrio_StreamFinalize, /**< Finalization of stream output */
#endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
kFblMemJobPrio_Input, /**< Not actually used, but acts as a common priority
* for all input jobs */
/* PRQA S 3205 1 */ /* MD_FblMem_3205_IdentifierNotUsed */
kFblMemJobPrio_InputWrite = kFblMemJobPrio_Input, /**< Input: Write through */
#if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
/* PRQA S 3205 2 */ /* MD_FblMem_3205_IdentifierNotUsed */
kFblMemJobPrio_StreamInput = kFblMemJobPrio_Input, /**< Input: Stream output */
kFblMemJobPrio_StreamProcLow = kFblMemJobPrio_Input, /**< Stream output of processed data, low priority
* used when processing is split into smaller pieces
* Prevent overtaking by following input jobs */
#endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
#if defined( FBL_ENABLE_DATA_PROCESSING )
kFblMemJobPrio_ProcInput = kFblMemJobPrio_Input, /**< Input: Data processing */
/* PRQA S 3205 1 */ /* MD_FblMem_3205_IdentifierNotUsed */
kFblMemJobPrio_ProcWriteLow = kFblMemJobPrio_Input, /**< Write operation of data processing, low priority
* used when processing is split into smaller pieces
* Prevent overtaking by following input jobs */
kFblMemJobPrio_ProcWriteHigh, /**< Write operation of data processing, high priority
* used to flush buffer */
#endif /* FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
/* PRQA S 3205 1 */ /* MD_FblMem_3205_IdentifierNotUsed */
kFblMemJobPrio_StreamProcHigh, /**< Stream output of processed data, high priority
* used to flush buffer */
#endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
/* PRQA S 3205 1 */ /* MD_FblMem_3205_IdentifierNotUsed */
kFblMemJobPrio_Highest = FBL_MEM_QUEUE_PRIO_HIGHEST /**< Highest job priority */
} tFblMemJobPrio; /* PRQA S 3205 */ /* MD_FblMem_3205_IdentifierNotUsed */
/* PRQA L:TAG_FblMem_0724 */
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
/** Handle for queue entry */
typedef vuintx tFblMemQueueHandle;
/** Queue entry */
typedef struct
{
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * job; /**< Associated job */
tFblMemQueuePrio prio; /**< Entry priority */
tFblMemQueueHandle prev; /**< Handle of previous queue entry */
tFblMemQueueHandle next; /**< Handle of next queue entry */
} tFblMemQueueEntry;
#endif /* FBL_MEM_ENABLE_PROC_QUEUE */
#if defined( FBL_MEM_ENABLE_SEGMENTATION ) || \
defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/** Limitation of processed length per cycle */
typedef struct
{
tFblLength limit; /**< Length limit, 0 equals unlimited */
tFblMemOperationMode unlimitedMode; /**< Operation mode which returns unlimited length */
} tFblMemLengthLimit;
#endif /* FBL_MEM_ENABLE_SEGMENTATION || FBL_MEM_ENABLE_VERIFY_PIPELINED */
/*-- Pipelined programming --------------------------------------------------*/
#if defined( FBL_MEM_ENABLE_PIPELINING )
/*
Context of active programming operation
Generation of RCR-RP message only applicable in service context
Otherwise service dispatching may not be in a state where RCR-RP is allowed
*/
typedef enum
{
kFblMemContext_Service = 0u, /**< Programming directly initiated by service dispatcher */
kFblMemContext_Background /**< Background programming while data transfer is in progress */
} tFblMemContext;
#endif /* FBL_MEM_ENABLE_PIPELINING */
/*-- Progress information ---------------------------------------------------*/
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/** Progress reporting state */
typedef enum
{
kFblMemProgressState_Disabled, /**< Disable progress reporting */
kFblMemProgressState_Enabled /**< Enable progress reporting */
} tFblMemProgressState;
/** Internal progress information */
typedef struct
{
vuint32 target; /**< Expected maximum value of partial operation in arbitrary unit (e.g. bytes) */
vuint8 totalOffset; /**< Percentage offset of total operation for current operation type */
vuint8 totalPercentage; /**< Percentage contribution to total operation of current operation type */
} tFblMemProgressInfoInternal;
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
/*-- Resumable programming --------------------------------------------------*/
/*-- Error handling ---------------------------------------------------------*/
/***********************************************************************************************************************
* GLOBAL DATA
**********************************************************************************************************************/
#define FBLLIB_MEM_START_SEC_VAR
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
/** State of programming operation */
V_MEMRAM0 V_MEMRAM1 tFblMemProgState V_MEMRAM2 fblMemProgState;
/***********************************************************************************************************************
* LOCAL DATA
**********************************************************************************************************************/
/* PRQA S 3218 TAG_FblMem_3218 */ /* MD_FblMem_3218 */
/** States for indication order */
V_MEMRAM0 static V_MEMRAM1 tFblMemAllowedInd V_MEMRAM2 gAllowedInd;
/** Error status for programming operations (potentially ran in background) */
V_MEMRAM0 static V_MEMRAM1 tFblMemStatus V_MEMRAM2 gErrorStatus;
#if defined( FBL_MEM_ENABLE_PIPELINING )
/** Context of programming operation (service context / background context) */
V_MEMRAM0 static V_MEMRAM1 tFblMemContext V_MEMRAM2 gProgContext;
#endif /* FBL_MEM_ENABLE_PIPELINING */
#if defined( FBL_MEM_ENABLE_GLOBAL_BLOCK_INFO )
/** Block information */
V_MEMRAM0 static V_MEMRAM1 tFblMemBlockInfo V_MEMRAM2 gBlockInfo;
#endif /* FBL_MEM_ENABLE_GLOBAL_BLOCK_INFO */
/** Segment information */
V_MEMRAM0 static V_MEMRAM1 tFblMemSegmentInternal V_MEMRAM2 gSegInfo;
#if defined( FBL_MEM_ENABLE_MULTI_SOURCE )
/** Active input source */
V_MEMRAM0 static V_MEMRAM1 tFblMemInputSource V_MEMRAM2 gActiveSource;
#endif
#if defined( FBL_MEM_ENABLE_DYNAMIC_PREAMBLE_LENGTH )
/** Preamble length stored per input source */
V_MEMRAM0 static V_MEMRAM1 tFblLength V_MEMRAM2 gPreambleLength[FBL_MEM_SOURCE_COUNT];
#endif /* FBL_MEM_ENABLE_DYNAMIC_PREAMBLE_LENGTH */
#if defined( FBL_MEM_ENABLE_PREAMBLE_HANDLING )
/** Temporary buffer for storage of preamble */
V_MEMRAM0 static V_MEMRAM1 vuint8 V_MEMRAM2 gPreambleBuffer[FBL_MEM_MAX_PREAMBLE_LENGTH];
#endif /* FBL_MEM_ENABLE_PREAMBLE_HANDLING */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/** Temporary buffer for write remainder */
V_MEMRAM0 static V_MEMRAM1 tFblMemRemainderBuffer V_MEMRAM2 gRemainderBuffer;
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
#if defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER )
/** Temporary buffer for data overwritten by buffer padding */
V_MEMRAM0 static V_MEMRAM1 vuint8 V_MEMRAM2 gPaddingBuffer[FBL_MEM_SEGMENT_SIZE];
#endif /* FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER */
/*-- Input buffers ----------------------------------------------------------*/
/** Data buffer(s) */
V_MEMRAM0 static V_MEMRAM1 tFblMemInputBuffer V_MEMRAM2 gBasicInputBuffer[FBL_MEM_BUFFER_COUNT_INPUT];
/** Input jobs */
V_MEMRAM0 static V_MEMRAM1 tFblMemJob V_MEMRAM2 gInputJobs[FBL_MEM_SOURCE_COUNT][FBL_MEM_QUEUE_ENTRIES_INPUT];
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/** Write finalization job */
V_MEMRAM0 static V_MEMRAM1 tFblMemJob V_MEMRAM2 gWriteFinalizeJob;
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
/*-- On-the-fly verification ------------------------------------------------*/
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/** Pipelined verification jobs */
V_MEMRAM0 static V_MEMRAM1 tFblMemJob V_MEMRAM2 gVerifyPipeReadJob[FBL_MEM_QUEUE_ENTRIES_VERIFY_PIPE_READ];
V_MEMRAM0 static V_MEMRAM1 tFblMemJob V_MEMRAM2 gVerifyPipeUpdateJob;
V_MEMRAM0 static V_MEMRAM1 tFblMemJob V_MEMRAM2 gVerifyPipeFinalizeJob;
V_MEMRAM0 static V_MEMRAM1 vuint8 V_MEMRAM2 gVerifyPipeReadBuffer[FBL_MEM_VERIFY_PIPELINED_SEGMENTATION];
# if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED_ADDRESS_LENGTH )
/** Data buffer(s) for segment information */
V_MEMRAM0 static V_MEMRAM1 vuint8 V_MEMRAM2 gVerifyPipeInfoBuffer[FBL_MEM_QUEUE_ENTRIES_VERIFY_PIPE_READ][FBL_MEM_VERIFY_ADDRESS_LENGTH_BUFFER_SIZE];
# endif /* FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH */
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
#if defined( FBL_ENABLE_DATA_PROCESSING )
/*-- Data processing --------------------------------------------------------*/
V_MEMRAM0 static V_MEMRAM1 tProcParam V_MEMRAM2 gProcParam;
/* Data processing jobs */
V_MEMRAM0 static V_MEMRAM1 tFblMemJob V_MEMRAM2 gProcWriteJob;
V_MEMRAM0 static V_MEMRAM1 tFblMemJob V_MEMRAM2 gProcFinalizeJob;
/* Data processing buffer */
V_MEMRAM0 static V_MEMRAM1 tFblMemProcBuffer V_MEMRAM2 gProcBuffer;
# if defined( FBL_MEM_ENABLE_PROC_SEGMENTATION )
/** Handle of data processing write job in processing queue
Defined globally as information has to be persistent across multiple calls of FblMemProcessJob */
V_MEMRAM0 static V_MEMRAM1 tFblMemQueueHandle V_MEMRAM2 gProcHandle;
# endif /* FBL_MEM_ENABLE_PROC_SEGMENTATION */
#endif /* FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
/*-- Stream output ----------------------------------------------------------*/
V_MEMRAM0 static V_MEMRAM1 tFblMemStreamProcessing V_MEMRAM2 gStreamParam;
/* Stream output jobs */
# if defined( FBL_ENABLE_DATA_PROCESSING )
V_MEMRAM0 static V_MEMRAM1 tFblMemJob V_MEMRAM2 gStreamProcJob;
# endif /* FBL_ENABLE_DATA_PROCESSING */
V_MEMRAM0 static V_MEMRAM1 tFblMemJob V_MEMRAM2 gStreamFinalizeJob;
#endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
#if defined( FBL_MEM_ENABLE_GAP_FILL )
V_MEMRAM0 static V_MEMRAM1 tFblMemJob V_MEMRAM2 gGapFillJob;
V_MEMRAM0 static V_MEMRAM1 tFblMemGapFillBuffer V_MEMRAM2 gGapFillBuffer;
#endif /* FBL_MEM_ENABLE_GAP_FILL */
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/*-- Progress information ---------------------------------------------------*/
/** Current progress information */
V_MEMRAM0 static V_MEMRAM1 tFblMemProgressInfo V_MEMRAM2 gProgressInfo;
/** Previously reported progress information */
V_MEMRAM0 static V_MEMRAM1 tFblMemProgressInfo V_MEMRAM2 gPrevProgressInfo;
/** Explicitely enable or disable reporting of erase progress */
V_MEMRAM0 static V_MEMRAM1 tFblMemProgressState V_MEMRAM2 gProgressState;
/** Internal progress information (current percentage contribution, target value) */
V_MEMRAM0 static V_MEMRAM1 tFblMemProgressInfoInternal V_MEMRAM2 gProgressInfoInternal;
/** Value of progress remainder during previous update */
V_MEMRAM0 static V_MEMRAM1 vuint32 V_MEMRAM2 gProgressPrevRemainder;
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
/*-- Processing queue -------------------------------------------------------*/
/** Processing queue */
V_MEMRAM0 static V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 gProcessingQueue[FBL_MEM_QUEUE_SIZE_PROCESSING];
# if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
/** Pipelined programming queue */
V_MEMRAM0 static V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 gPipeProgQueue[FBL_MEM_SOURCE_COUNT][FBL_MEM_QUEUE_SIZE_PIPE_PROG];
# endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
# if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
V_MEMRAM0 static V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 gVerifyPipeQueue[FBL_MEM_QUEUE_SIZE_PIPE_VERIFY];
# endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
#endif /* FBL_MEM_ENABLE_PROC_QUEUE */
#define FBLLIB_MEM_STOP_SEC_VAR
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
#define FBLLIB_MEM_START_SEC_CONST
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
/** Mapping of priorities to job types
* Has to be same order as tFblMemJobType */
V_MEMROM0 static V_MEMROM1 tFblMemQueuePrio V_MEMROM2 gJobPrio[] =
{
(vuint8)kFblMemJobPrio_InputWrite /**< InputWrite */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
, (vuint8)kFblMemJobPrio_WriteFinalize /**< WriteFinalize */
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
# if defined( FBL_ENABLE_DATA_PROCESSING )
, (vuint8)kFblMemJobPrio_ProcInput /**< ProcInput */
# if defined( FBL_MEM_ENABLE_PROC_SEGMENTATION )
, (vuint8)kFblMemJobPrio_ProcWriteLow /**< ProcWrite, low priority when processing is split into smaller pieces */
/* kFblMemJobPrio_ProcWriteHigh not explicitely mapped as it is not used when inserting job,
* but only when temporarily updating the priority because of a full processing buffer */
# else
, (vuint8)kFblMemJobPrio_ProcWriteHigh /**< ProcWrite, high priority used to flush buffer */
/* kFblMemJobPrio_ProcWriteLow not used */
# endif /* FBL_MEM_ENABLE_PROC_SEGMENTATION */
, (vuint8)kFblMemJobPrio_ProcFinalize /**< ProcFinalize */
# endif /* FBL_ENABLE_DATA_PROCESSING */
# if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
# if defined( FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH )
, (vuint8)kFblMemJobPrio_VerifyPipeInfo /**< VerifyPipeInfo */
# endif /* FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH */
, (vuint8)kFblMemJobPrio_VerifyPipeRead /**< VerifyPipeRead */
, (vuint8)kFblMemJobPrio_VerifyPipeUpdate /**< VerifyPipeUpdate */
, (vuint8)kFblMemJobPrio_VerifyPipeFinalize /**< VerifyPipeFinalize */
# endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
#if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
, (vuint8)kFblMemJobPrio_StreamInput /**< StreamInput */
# if defined( FBL_MEM_ENABLE_PROC_SEGMENTATION )
, (vuint8)kFblMemJobPrio_StreamProcLow /**< StreamProc, low priority when processing is split into smaller pieces */
/* kFblMemJobPrio_StreamProcHigh not explicitely mapped as it is not used when inserting job,
* but only when temporarily updating the priority because of a full processing buffer */
# else
, (vuint8)kFblMemJobPrio_StreamProcHigh /**< StreamProc, high priority used to flush buffer */
/* kFblMemJobPrio_StreamProcLow not used */
# endif /* FBL_MEM_ENABLE_PROC_SEGMENTATION */
, (vuint8)kFblMemJobPrio_StreamFinalize /**< StreamFinalize */
#endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
#if defined( FBL_MEM_ENABLE_GAP_FILL )
, (vuint8)kFblMemJobPrio_GapFill /**< GapFill */
#endif /* FBL_MEM_ENABLE_GAP_FILL */
, (vuint8)kFblMemJobPrio_Lowest /**< Concluding entry */
};
#endif /* FBL_MEM_ENABLE_PROC_QUEUE */
#if defined( FBL_MEM_ENABLE_SEGMENTATION ) || \
defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/** Limitation of processed length per cycle
* Has to be same order as tFblMemJobType */
V_MEMROM0 static V_MEMROM1 tFblMemLengthLimit V_MEMROM2 gLengthLimits[] =
{
{ FBL_MEM_WRITE_SEGMENTATION, FBL_MEM_FINALIZE_UNLIMITED_MODE } /**< InputWrite */
# if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
, { 0u, kFblMemOperationMode_Normal } /**< WriteFinalize, no actual input data */
# endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
# if defined( FBL_ENABLE_DATA_PROCESSING )
, { 0xFFFFu, kFblMemOperationMode_Unconditional } /**< ProcInput, limited to 16 bit */
, { FBL_MEM_WRITE_SEGMENTATION, FBL_MEM_FINALIZE_UNLIMITED_MODE } /**< ProcWrite */
, { 0u, kFblMemOperationMode_Normal } /**< ProcFinalize, no actual input data */
# endif /* FBL_ENABLE_DATA_PROCESSING */
# if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
# if defined( FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH )
, { FBL_MEM_VERIFY_ADDRESS_LENGTH_BUFFER_SIZE, kFblMemOperationMode_Unconditional } /**< VerifyPipeInfo, unconditionally limited by size of segment information */
# endif /* FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH */
, { FBL_MEM_VERIFY_PIPELINED_SEGMENTATION, kFblMemOperationMode_Unconditional } /**< VerifyPipeRead, unconditionally limited by temporary buffer */
, { FBL_MEM_VERIFY_PIPELINED_SEGMENTATION, kFblMemOperationMode_Unconditional } /**< VerifyPipeUpdate, limited by VerifyPipeRead */
, { 0u, kFblMemOperationMode_Normal } /**< VerifyPipeFinalize, no actual input data */
# endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
# if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
, { FBL_MEM_WRITE_SEGMENTATION, FBL_MEM_FINALIZE_UNLIMITED_MODE } /**< StreamInput */
, { FBL_MEM_WRITE_SEGMENTATION, FBL_MEM_FINALIZE_UNLIMITED_MODE } /**< StreamProc */
, { 0u, kFblMemOperationMode_Normal } /**< StreamFinalize, no actual input data */
# endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
#if defined( FBL_MEM_ENABLE_GAP_FILL )
, { 0u, kFblMemOperationMode_Normal } /**< GapFill, limit applied in job processing */
#endif /* FBL_MEM_ENABLE_GAP_FILL */
, { 0u, kFblMemOperationMode_Normal } /**< Concluding entry */
};
#endif /* FBL_MEM_ENABLE_SEGMENTATION || FBL_MEM_ENABLE_VERIFY_PIPELINED */
/*-- Error handling ---------------------------------------------------------*/
/* PRQA L:TAG_FblMem_3218 */
#define FBLLIB_MEM_STOP_SEC_CONST
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
#define FBLLIB_MEM_START_SEC_CODE
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
/***********************************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
**********************************************************************************************************************/
static tFblMemTriggerStatus FblMemTriggerWatchdogExt( void );
static void FblMemTriggerWatchdog( void );
#if defined( FBL_MEM_ENABLE_RESPONSE_PENDING )
static void FblMemResponsePending( void );
#endif /* FBL_MEM_ENABLE_RESPONSE_PENDING */
static void FblMemInitInputQueue( void );
static void FblMemInitStates( void );
static tFblMemRamData FblMemInitInternal( void );
static tFblMemStatus FblMemQueueBuffer( tFblMemConstRamData buffer, tFblLength offset, tFblLength length );
static void FblMemProcessQueue( tFblMemOperationMode mode );
static void FblMemFlushQueueByPrio( tFblMemQueuePrio prio );
#if defined( FBL_MEM_ENABLE_PIPELINING )
static void FblMemUnblockQueue( const V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue );
#endif /* FBL_MEM_ENABLE_PIPELINING */
#if defined( FBL_MEM_ENABLE_VERIFY_STREAM )
static tFblMemStatus FblMemVerifyInput( V_MEMRAM1 tFblMemVerifyRoutineInput V_MEMRAM2 V_MEMRAM3 * routine,
const V_MEMRAM1 tFblMemVerifyData V_MEMRAM2 V_MEMRAM3 * data, vuint8 state,
V_MEMRAM1 tFblMemVerifyStatus V_MEMRAM2 V_MEMRAM3 * result );
static tFblMemStatus FblMemInitVerifyInput( void );
#endif /* FBL_MEM_ENABLE_VERIFY_STREAM */
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
static V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * FblMemPrepareVerifyPipeJob( vuintx segmentIndex, tFblAddress address );
static V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * FblMemUpdateVerifyPipeJob( vuintx segmentIndex, tFblAddress address, tFblLength length );
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
static tFblMemStatus FblMemProcessJob( V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * activeJob, tFblMemOperationMode mode );
static tFblMemStatus FblMemProgramStream( const V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * programJob,
V_MEMRAM1 tFblLength V_MEMRAM2 V_MEMRAM3 * programLength, tFblMemOperationMode mode );
static tFblLength FblMemPadLength( tFblAddress address, tFblLength length );
static tFblLength FblMemPadBuffer( tFblAddress address, tFblLength length, tFblMemRamData data );
#if defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER )
static void FblMemUnpadBuffer( tFblMemRamData data, tFblLength padLen );
#endif /* FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER */
static tFblMemStatus FblMemCopyBuffer( tFblAddress programAddress,
const V_MEMRAM1 tFblLength V_MEMRAM2 V_MEMRAM3 * programLength, tFblMemConstRamData programData );
static tFblMemStatus FblMemEraseRegionInternal( tFblAddress eraseAddress, tFblLength eraseLength );
static tFblMemStatus FblMemProgramBufferInternal( tFblAddress programAddress,
V_MEMRAM1 tFblLength V_MEMRAM2 V_MEMRAM3 * programLength, tFblMemRamData programData,
tFblMemProgState checkPointState );
# if defined( FBL_ENABLE_MULTIPLE_MEM_DEVICES ) && \
defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
static tFblLength FblMemGetSpecificRemainder( tFblAddress address, tFblLength length );
# endif /* FBL_ENABLE_MULTIPLE_MEM_DEVICES && FBL_MEM_ENABLE_REMAINDER_HANDLING */
#if defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER )
static tFblMemStatus FblMemRelocateBufferOffset( V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * activeJob,
tFblMemConstRamData buffer, tFblLength offset, tFblLength length );
#endif /* FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER */
#if defined( FBL_MEM_ENABLE_VARYING_INPUT_BUFFER )
static tFblMemStatus FblMemSearchInputBuffer( tFblMemConstRamData buffer, tFblLength offset, tFblLength length );
#endif /* FBL_MEM_ENABLE_VARYING_INPUT_BUFFER */
#if defined( FBL_MEM_ENABLE_SEGMENTATION ) || \
defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
static tFblLength FblMemLimitLength( tFblLength inputLen, tFblMemJobType type, tFblMemOperationMode mode );
#endif /* FBL_MEM_ENABLE_SEGMENTATION || FBL_MEM_ENABLE_VERIFY_PIPELINED */
#if defined( FBL_MEM_ENABLE_PREAMBLE_HANDLING )
static void FblMemStorePreamble( void );
static void FblMemRestorePreamble( void );
#endif /* FBL_MEM_ENABLE_PREAMBLE_HANDLING */
static tFblResult FblMemCheckAllowed( tFblMemAllowedInd check, tFblMemAllowedInd clear );
static tFblMemRamData FblMemGetBuffer( const V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * job );
#if defined( FBL_ENABLE_SYSTEM_CHECK )
static void FblMemInitBufferIntegrity( void );
static tFblMemStatus FblMemVerifyBufferIntegrity( void );
#endif /* FBL_ENABLE_SYSTEM_CHECK */
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
static void FblMemQueueInit( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue, tFblMemQueueHandle length );
static tFblMemQueueHandle FblMemQueueMove( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue,
tFblMemQueueHandle handle, tFblMemQueueHandle prevNew );
# if defined( FBL_MEM_ENABLE_PIPELINING )
static tFblMemQueueHandle FblMemQueueMoveFirstFreeEntry( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue,
tFblMemQueueHandle prevNew );
static tFblMemQueueHandle FblMemQueueAppend( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue );
# endif /* FBL_MEM_ENABLE_PIPELINING */
static tFblMemQueueHandle FblMemQueueRemove( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue,
tFblMemQueueHandle handle );
static tFblMemQueueHandle FblMemQueuePrioUpdate( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue,
tFblMemQueueHandle handle, tFblMemQueuePrio prio );
static tFblMemQueueHandle FblMemQueuePrioInsert( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue,
tFblMemQueuePrio prio, V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * job );
static tFblMemQueueHandle FblMemQueueDefaultPrioInsert( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue,
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * job, vuintx segmentIndex );
#endif /* FBL_MEM_ENABLE_PROC_QUEUE */
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
static V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * FblMemGetPendingInputJob( void );
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
static void FblMemInitJob( V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * job, tFblMemRamData buffer,
tFblLength size, tFblMemJobType type );
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
static vuint8 FblMemCalculateProgress( vuint32 current, vuint32 total, vuint8 percentage );
static void FblMemInitProgress( void );
static void FblMemReportProgress( void );
static void FblMemSetupProgress( tFblMemProgressType type, tFblAddress logicalAddress, vuint32 segmentCount,
vuint8 totalOffset, vuint8 totalPercentage, vuint32 target );
static void FblMemOffsetProgress( vuint32 totalDone, vuint32 totalTarget );
static void FblMemUpdateProgress( vuint32 remainderPart );
static void FblMemConcludeProgress( void );
# if defined( FBL_MEM_ENABLE_VERIFY_OUTPUT )
# if defined( FBL_MEM_ENABLE_SWITCH_READMEMORY_PARAM )
/* Parameters order changed in comparison to HIS security module specification */
static tFblMemVerifySize FblMemProgressRead( tFblMemVerifyAddr address, tFblMemVerifySize length, tFblMemVerifyDataPtr buffer );
# else
/* Parameters order as defined by HIS security module specification */
static tFblMemVerifySize FblMemProgressRead( tFblMemVerifyAddr address, tFblMemVerifyDataPtr buffer, tFblMemVerifySize length );
# endif /* FBL_MEM_ENABLE_SWITCH_READMEMORY_PARAM */
# endif /* FBL_MEM_ENABLE_VERIFY_OUTPUT */
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
/***********************************************************************************************************************
* EXTERNAL DATA
**********************************************************************************************************************/
/***********************************************************************************************************************
* LOCAL FUNCTIONS
**********************************************************************************************************************/
/***********************************************************************************************************************
* FblMemTriggerWatchdogExt
**********************************************************************************************************************/
/*! \brief Call configured watchdog trigger routine
* \details If pipelined programming is supported and the trigger is called while a background programming operation
* is active, the default watchdog trigger is executed.
* Otherwise an erroneous RCR-RP could be generated before service dispatching is finished.
* E.g. ISO 14229 enforces that certain message checks (length, ...) are performed before the first RCR-RP
* transmission.
* For all other cases (non-pipelined programming, service context) the configured routine to support an
* adaptive RCR-RP generation is called instead.
* \return Watchdog trigger status
**********************************************************************************************************************/
static tFblMemTriggerStatus FblMemTriggerWatchdogExt( void )
{
tFblMemTriggerStatus result;
#if defined( FBL_MEM_ENABLE_PIPELINING ) && \
defined( __ApplFblMemWdTrigger )
/* Do not generate RCR-RP while programming in background context */
if (kFblMemContext_Background == gProgContext)
{
/* Default watchdog trigger */
result = __ApplFblMemWdTrigger();
}
else
#endif /* FBL_MEM_ENABLE_PIPELINING && __ApplFblMemWdTrigger */
{
#if defined( __ApplFblMemAdaptiveRcrRp )
/* Trigger watchdog and dynamically generate RCR-RP */
result = __ApplFblMemAdaptiveRcrRp();
#else
/* Callback not configured, return default value */
result = FBL_MEM_WD_TRIGGER_DEFAULT;
#endif /* __ApplFblMemAdaptiveRcrRp */
}
return result;
}
/***********************************************************************************************************************
* FblMemTriggerWatchdog
**********************************************************************************************************************/
/*! \brief Call configured watchdog trigger routine
* \details Suppress watchdog trigger status (see FblMemTriggerWatchdogExt)
**********************************************************************************************************************/
static void FblMemTriggerWatchdog( void )
{
(void)FblMemTriggerWatchdogExt();
}
#if defined( FBL_MEM_ENABLE_RESPONSE_PENDING )
/***********************************************************************************************************************
* FblMemResponsePending
**********************************************************************************************************************/
/*! \brief Force RCR-RP by calling configured routine
* \details If pipelined programming is supported only execute function if called in service context. Otherwise an
* erroneous RCR-RP could be generated before service dispatching is finished
**********************************************************************************************************************/
static void FblMemResponsePending( void )
{
# if defined( __ApplFblMemForcedRcrRp )
# if defined( FBL_MEM_ENABLE_PIPELINING )
/* Do not force RCR-RP while programming in background context */
if (kFblMemContext_Service == gProgContext)
# endif /* FBL_MEM_ENABLE_PIPELINING */
{
# if defined( __ApplFblMemIsRcrRpActive )
/* Do not force RCR-RP if already active */
if (kFblOk != __ApplFblMemIsRcrRpActive())
# endif /* __ApplFblMemIsRcrRpActive */
{
/* Force RCR-RP */
__ApplFblMemForcedRcrRp();
}
}
# endif /* __ApplFblMemForcedRcrRp */
}
#endif /* FBL_MEM_ENABLE_RESPONSE_PENDING */
/***********************************************************************************************************************
* FblMemGetBuffer
**********************************************************************************************************************/
/*! \brief Get a pointer to the referenced buffer, taking into account the current position value
* \param[in] job Pointer to job information
* \return Pointer to current buffer position
* Null pointer in case referenced buffer is not defined
**********************************************************************************************************************/
static tFblMemRamData FblMemGetBuffer( const V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * job )
{
tFblMemRamData jobBuffer;
/* Get referenced buffer */
jobBuffer = job->buffer;
/* Check for undefined buffer (null pointer) */
if (FBL_MEM_BUFFER_NULL != jobBuffer)
{
/* Check for potential buffer overflow */
assertFblInternal((job->offset <= (job->totalSize - job->netSize)), kFblMemAssertParameterOutOfRange);
assertFblInternal((job->used <= (job->totalSize - job->offset)), kFblMemAssertParameterOutOfRange);
/* Valid buffer pointer
Evaluate position */
jobBuffer = &jobBuffer[job->position];
}
/* Return pointer to current buffer position */
return jobBuffer;
}
#if defined( FBL_ENABLE_SYSTEM_CHECK )
/***********************************************************************************************************************
* FblMemInitBufferIntegrity
**********************************************************************************************************************/
/*! \brief Initialize "canary" words placed after aligned buffer contents
* \details "Canary" value is placed behind actual buffer contents and contains a magic value.
* In case of a buffer overrun it is likely that the "canary" is modified too. This can be used to
* detect the overrun.
**********************************************************************************************************************/
static void FblMemInitBufferIntegrity( void )
{
vuintx idx;
/* Data buffer(s) */
for (idx = 0u; idx < FBL_MEM_BUFFER_COUNT_INPUT; idx++)
{
gBasicInputBuffer[idx].canaryFront = FBL_MEM_CANARY_VALUE;
gBasicInputBuffer[idx].canaryBack = FBL_MEM_CANARY_VALUE;
}
# if defined( FBL_ENABLE_DATA_PROCESSING )
/* Data processing buffer */
gProcBuffer.canaryFront = FBL_MEM_CANARY_VALUE;
gProcBuffer.canaryBack = FBL_MEM_CANARY_VALUE;
# endif /* FBL_ENABLE_DATA_PROCESSING */
# if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/* Remainder buffer */
gRemainderBuffer.canaryFront = FBL_MEM_CANARY_VALUE;
gRemainderBuffer.canaryBack = FBL_MEM_CANARY_VALUE;
# endif /* FBL_ENABLE_DATA_PROCESSING */
# if defined( FBL_MEM_ENABLE_GAP_FILL )
/* Gap fill buffer */
gGapFillBuffer.canaryFront = FBL_MEM_CANARY_VALUE;
gGapFillBuffer.canaryBack = FBL_MEM_CANARY_VALUE;
# endif /* FBL_ENABLE_DATA_PROCESSING */
}
/***********************************************************************************************************************
* FblMemVerifyBufferIntegrity
**********************************************************************************************************************/
/*! \brief Verify "canary" words placed before and after aligned buffer contents
* \details "Canary" values are placed in front and behind actual buffer contents and contain a magic value.
* In case of a buffer overrun it is likely that the "canary" is modified too. This can be used to
* detect the overrun.
* \return kFblMemStatus_Ok if "canary" words of all aligned buffers are intact,
* kFblMemStatus_Failed otherwise
**********************************************************************************************************************/
static tFblMemStatus FblMemVerifyBufferIntegrity( void )
{
tFblMemStatus retVal;
vuint32 aggregated;
vuintx idx;
retVal = kFblMemStatus_Ok;
aggregated = 0x00uL;
/* Data buffer(s) */
for (idx = 0u; idx < FBL_MEM_BUFFER_COUNT_INPUT; idx++)
{
aggregated |= (FBL_MEM_CANARY_VALUE ^ gBasicInputBuffer[idx].canaryFront);
aggregated |= (FBL_MEM_CANARY_VALUE ^ gBasicInputBuffer[idx].canaryBack);
}
# if defined( FBL_ENABLE_DATA_PROCESSING )
/* Data processing buffer */
aggregated |= (FBL_MEM_CANARY_VALUE ^ gProcBuffer.canaryFront);
aggregated |= (FBL_MEM_CANARY_VALUE ^ gProcBuffer.canaryBack);
# endif /* FBL_ENABLE_DATA_PROCESSING */
# if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/* Remainder buffer */
aggregated |= (FBL_MEM_CANARY_VALUE ^ gRemainderBuffer.canaryFront);
aggregated |= (FBL_MEM_CANARY_VALUE ^ gRemainderBuffer.canaryBack);
# endif /* FBL_ENABLE_DATA_PROCESSING */
# if defined( FBL_MEM_ENABLE_GAP_FILL )
/* Gap fill buffer */
aggregated |= (FBL_MEM_CANARY_VALUE ^ gGapFillBuffer.canaryFront);
aggregated |= (FBL_MEM_CANARY_VALUE ^ gGapFillBuffer.canaryBack);
# endif /* FBL_ENABLE_DATA_PROCESSING */
if (0x00uL != aggregated)
{
retVal = kFblMemStatus_Failed;
}
return retVal;
}
#endif /* FBL_ENABLE_SYSTEM_CHECK */
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
/* Queue handling ************************************************************/
/***********************************************************************************************************************
* FblMemQueueInit
**********************************************************************************************************************/
/*! \brief Initialize queue structure
* \details Implements two double linked lists, representing a used and a free queue.
* \param[in,out] queue Pointer to queue array
* \param[in] length Total length of queue, including used and free head
**********************************************************************************************************************/
static void FblMemQueueInit( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue, tFblMemQueueHandle length )
{
tFblMemQueueHandle handle;
tFblMemQueueHandle prevHandle;
tFblMemQueueHandle nextHandle;
/* Start with empty queue by self-reference */
queue[FBL_MEM_QUEUE_HANDLE_HEAD_USED].next = FBL_MEM_QUEUE_HANDLE_HEAD_USED;
queue[FBL_MEM_QUEUE_HANDLE_HEAD_USED].prev = FBL_MEM_QUEUE_HANDLE_HEAD_USED;
/* Set default values */
queue[FBL_MEM_QUEUE_HANDLE_HEAD_USED].job = FBL_MEM_JOB_NULL;
queue[FBL_MEM_QUEUE_HANDLE_HEAD_USED].prio = FBL_MEM_QUEUE_PRIO_HIGHEST;
/* Setup double linked list of empty queue entries */
/* Head references last entry as predecessor */
prevHandle = length - 1u;
nextHandle = FBL_MEM_QUEUE_HANDLE_HEAD_FREE;
/* Append available entries to free queue */
for (handle = FBL_MEM_QUEUE_HANDLE_HEAD_FREE; handle < length; handle++)
{
nextHandle++;
/* Set predecessor and successor */
queue[handle].prev = prevHandle;
queue[handle].next = nextHandle;
/* Set default values */
queue[handle].job = FBL_MEM_JOB_NULL;
queue[handle].prio = FBL_MEM_QUEUE_PRIO_LOWEST;
prevHandle = handle;
}
/* Last entry references head as successor */
queue[length - 1u].next = FBL_MEM_QUEUE_HANDLE_HEAD_FREE;
}
/***********************************************************************************************************************
* FblMemQueueMove
**********************************************************************************************************************/
/*! \brief Cut queue entry from current position and place it at a different location (either in same or other queue)
* \pre FblMemQueueInit executed before, source queue not empty
* Passed handles have to represent actual predecessor and successor relationship
* \param[in,out] queue Pointer to queue array
* \param[in] handle Handle of queue entry to be moved
* \param[in] prevNew Handle of new predecessor
* \return Handle of affected entry
**********************************************************************************************************************/
static tFblMemQueueHandle FblMemQueueMove( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue,
tFblMemQueueHandle handle, tFblMemQueueHandle prevNew )
{
tFblMemQueueHandle prevOld;
tFblMemQueueHandle nextOld;
tFblMemQueueHandle nextNew;
/* Check for matching handles */
if (handle == prevNew)
{
/* Entry placed at exact same location
No need to change anything */
}
else
{
/* Get current predecessor and successor */
prevOld = queue[handle].prev;
nextOld = queue[handle].next;
/* Old queue empty? */
assertFblInternal((queue[prevOld].next != prevOld), kFblMemAssertParameterOutOfRange);
/* Remove entry from old queue */
queue[prevOld].next = nextOld;
queue[nextOld].prev = prevOld;
/* Get new predecessor */
nextNew = queue[prevNew].next;
/* Insert entry into new queue */
queue[handle].prev = prevNew;
queue[prevNew].next = handle;
queue[handle].next = nextNew;
queue[nextNew].prev = handle;
}
return handle;
}
# if defined( FBL_MEM_ENABLE_PIPELINING )
/***********************************************************************************************************************
* FblMemQueueMoveFirstFreeEntry
**********************************************************************************************************************/
/*! \brief Take first free entry, remove it from free queue and insert it into defined location in used queue.
* \pre FblMemQueueInit executed before, queue not full
* \param[in,out] queue Pointer to queue array
* \param[in] prevNew Handle of new predecessor
* \return Handle of moved entry
**********************************************************************************************************************/
static tFblMemQueueHandle FblMemQueueMoveFirstFreeEntry( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue,
tFblMemQueueHandle prevNew )
{
/* Free queue empty? */
assertFblInternal((!FblMemQueueIsFull(queue)), kFblMemAssertParameterOutOfRange);
/* Move first free entry to new position */
return FblMemQueueMove(queue, queue[FBL_MEM_QUEUE_HANDLE_HEAD_FREE].next, prevNew);
}
/***********************************************************************************************************************
* FblMemQueueAppend
**********************************************************************************************************************/
/*! \brief Append entry to end of queue
* \details Take first free entry, remove it from free queue and insert it as last entry into used queue.
* \pre FblMemQueueInit executed before, queue not full
* \param[in,out] queue Pointer to queue array
* \return Handle of appended entry
**********************************************************************************************************************/
static tFblMemQueueHandle FblMemQueueAppend( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue )
{
/* Insert at end of used queue
Place between current last entry and tail (== head) */
return FblMemQueueMoveFirstFreeEntry(queue, queue[FBL_MEM_QUEUE_HANDLE_HEAD_USED].prev);
}
# endif /* FBL_MEM_ENABLE_PIPELINING */
/***********************************************************************************************************************
* FblMemQueueRemove
**********************************************************************************************************************/
/*! \brief Remove specific entry from queue
* \details Take specific entry, remove it from queue and insert it as last entry into free queue.
* \pre FblMemQueueInit executed before, queue not empty
* \param[in,out] queue Pointer to queue array
* \param[in] handle Queue entry handle
* \return Handle of removed entry
**********************************************************************************************************************/
static tFblMemQueueHandle FblMemQueueRemove( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue,
tFblMemQueueHandle handle )
{
/* Insert at end of free queue
Place between current last entry and tail (== head) */
return FblMemQueueMove(queue, handle, queue[FBL_MEM_QUEUE_HANDLE_HEAD_FREE].prev);
}
/***********************************************************************************************************************
* FblMemQueuePrioUpdate
**********************************************************************************************************************/
/*! \brief Update priority of a queue entry
* \details Take specific entry, remove it from queue and re-insert it according to the updated priority.
* \pre FblMemQueueInit executed before, queue not empty
* \param[in,out] queue Pointer to queue array
* \param[in] handle Queue entry handle
* \param[in] prio New priority value
* \return Handle of updated entry
**********************************************************************************************************************/
static tFblMemQueueHandle FblMemQueuePrioUpdate( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue,
tFblMemQueueHandle handle, tFblMemQueuePrio prio )
{
tFblMemQueueHandle prevHandle;
/* Start search at last entry */
prevHandle = queue[FBL_MEM_QUEUE_HANDLE_HEAD_USED].prev;
/* Skip all entries with lower priority
Remark: Search is assured to stop at head because it has the highest possible priority */
while (queue[prevHandle].prio < prio)
{
prevHandle = queue[prevHandle].prev;
}
/* Append after first entry with higher or equal priority */
(void)FblMemQueueMove(queue, handle, prevHandle);
/* Update priority of inserted entry */
queue[handle].prio = prio;
return handle;
}
/***********************************************************************************************************************
* FblMemQueuePrioInsert
**********************************************************************************************************************/
/*! \brief Insert entry into queue using given priority
* \details Take first free entry, remove it from free queue and insert it according to the given priority.
* Additionally set job associated with queue entry
* \pre FblMemQueueInit executed before, queue not empty
* \param[in,out] queue Pointer to queue array
* \param[in] prio Priority value
* \param[in] job Job associated with queue entry
* \return Handle of inserted entry
**********************************************************************************************************************/
static tFblMemQueueHandle FblMemQueuePrioInsert( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue,
tFblMemQueuePrio prio, V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * job )
{
tFblMemQueueHandle handle;
/* Free queue empty? */
assertFblInternal((!FblMemQueueIsFull(queue)), kFblMemAssertParameterOutOfRange);
/* Relocate first free entry according to given priority */
handle = FblMemQueuePrioUpdate(queue, queue[FBL_MEM_QUEUE_HANDLE_HEAD_FREE].next, prio);
/* Set job of entry */
queue[handle].job = job;
return handle;
}
/***********************************************************************************************************************
* FblMemQueueDefaultPrioInsert
**********************************************************************************************************************/
/*! \brief Insert entry into queue using default priority
* \details Take first free entry, remove it from free queue and insert it according to the default priority of
the given job's type. Additionally set job associated with queue entry and update segment index of job.
* \pre FblMemQueueInit executed before, queue not empty
* \param[in,out] queue Pointer to queue array
* \param[in] job Job associated with queue entry
* \param[in] segmentIndex Segment index assigned to job
* \return Handle of inserted entry
**********************************************************************************************************************/
static tFblMemQueueHandle FblMemQueueDefaultPrioInsert( V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue,
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * job, vuintx segmentIndex )
{
/* Valid job type? */
assertFblInternal((job->type < kFblMemJobType_Max), kFblMemAssertParameterOutOfRange);
/* Remember segment index associated with job */
job->segmentIndex = segmentIndex;
return FblMemQueuePrioInsert(queue, gJobPrio[job->type], job);
}
#endif /* FBL_MEM_ENABLE_PROC_QUEUE */
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
/***********************************************************************************************************************
* FblMemGetPendingInputJob
**********************************************************************************************************************/
/*! \brief Get information of current pending job
* \details This is the very first entry in the free queue
* \pre Pipelined programming queue initialized
* \return Pointer to pending input job
**********************************************************************************************************************/
static V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * FblMemGetPendingInputJob( void )
{
/* Free queue empty? */
assertFblInternal((!FblMemQueueIsFull(FBL_MEM_PIPE_PROG_QUEUE)), kFblMemAssertParameterOutOfRange);
/* Return job information of first free entry */
return FblMemQueueGetFirstFreeEntry(FBL_MEM_PIPE_PROG_QUEUE).job;
}
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
/***********************************************************************************************************************
* FblMemInitJob
**********************************************************************************************************************/
/*! \brief Initialize job information structure
* \details Assign given buffer to job and set type
* \param[in,out] job Job information to be initialized
* \param[in] buffer Buffer associated with job
* \param[in] size Size of given buffer
* \param[in] type Type of job
**********************************************************************************************************************/
static void FblMemInitJob( V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * job, tFblMemRamData buffer, tFblLength size,
tFblMemJobType type )
{
/* Assign given buffer */
job->buffer = buffer;
job->totalSize = size;
job->netSize = size;
/* Default values */
job->offset = 0u;
job->position = 0u;
job->used = 0u;
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
#if defined( FBL_MEM_ENABLE_PIPELINING )
/* Default job */
job->jobClass = tFblMemJobClass_Default;
#endif /* FBL_MEM_ENABLE_PIPELINING */
/* Set job type */
job->type = type;
/* Job complete as soon as "used" member reaches zero */
job->completion = kFblMemOperationMode_Normal;
}
/***********************************************************************************************************************
* FblMemInitInputQueue
**********************************************************************************************************************/
/*! \brief Initialize input queue
* \details Additionally job information of input buffers is prepared.
* Will also be carried out for single buffer use-case
**********************************************************************************************************************/
static void FblMemInitInputQueue( void )
{
vuintx idx;
vuintx actualBasicIdx;
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
vuintx basicIdx;
tFblMemQueueHandle queueHandle;
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
/* First input buffer assigned to specified input source */
actualBasicIdx = FBL_MEM_ACTIVE_SOURCE;
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
/* Setup pipelined programming queue */
FblMemQueueInit(FBL_MEM_PIPE_PROG_QUEUE, FBL_MEM_ARRAY_SIZE(FBL_MEM_PIPE_PROG_QUEUE));
queueHandle = (tFblMemQueueHandle)FBL_MEM_QUEUE_HANDLE_ENTRY_OFFSET;
basicIdx = FBL_MEM_SOURCE_COUNT;
for (idx = 0u; idx < FBL_MEM_QUEUE_ENTRIES_INPUT; idx++)
{
if (0u != idx)
{
actualBasicIdx = basicIdx;
basicIdx++;
}
#else
idx = 0u;
{
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
/* Reset job information and assign actual buffers */
/* PRQA S 2801, 2851 2 */ /* MD_FblMem_FalseFinding */
FblMemInitJob( &FBL_MEM_INPUT_JOB[idx], gBasicInputBuffer[actualBasicIdx].data,
FBL_MEM_ARRAY_SIZE(gBasicInputBuffer[actualBasicIdx].data), kFblMemJobType_InputWrite );
/* Overwrite net size, to exclude overhead for remainder and padding */
FBL_MEM_INPUT_JOB[idx].netSize = FBL_MEM_BUFFER_SIZE;
/* Default offset to align actual data to platform requirements */
FBL_MEM_INPUT_JOB[idx].offset = FBL_MEM_PREAMBLE_OFFSET(FBL_MEM_PREAMBLE_LENGTH);
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
/* Set dedicated job class */
FBL_MEM_INPUT_JOB[idx].jobClass = tFblMemJobClass_PipeProg;
/* Assign job to input queue entry */
FblMemQueueGetEntry(FBL_MEM_PIPE_PROG_QUEUE, queueHandle).job = &FBL_MEM_INPUT_JOB[idx];
queueHandle++;
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
}
}
/***********************************************************************************************************************
* FblMemInitStates
**********************************************************************************************************************/
/*! \brief Setup all data structures
* \pre FblMemInitPowerOn executed before
**********************************************************************************************************************/
static void FblMemInitStates( void )
{
/* Setup idle state */
fblMemProgState = kFblMemProgState_Idle;
gErrorStatus = kFblMemStatus_Ok;
#if defined( FBL_MEM_ENABLE_PIPELINING )
gProgContext = kFblMemContext_Service;
#endif /* FBL_MEM_ENABLE_PIPELINING */
/* No operations allowed */
FblMemResetAllowed();
}
/***********************************************************************************************************************
* FblMemInitInternal
**********************************************************************************************************************/
/*! \brief Setup all data structures
* \pre FblMemInitPowerOn executed before
* \return Pointer to active input buffer
**********************************************************************************************************************/
static tFblMemRamData FblMemInitInternal( void )
{
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
vuintx idx;
tFblMemQueueHandle queueHandle;
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
/* Setup idle state */
FblMemInitStates();
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
/* Setup processing queue */
FblMemQueueInit(gProcessingQueue, FBL_MEM_ARRAY_SIZE(gProcessingQueue));
#endif /* FBL_MEM_ENABLE_PROC_QUEUE */
FblMemInitInputQueue();
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
FblMemInitJob(&gWriteFinalizeJob, gRemainderBuffer.data, FBL_MEM_SEGMENT_SIZE, kFblMemJobType_WriteFinalize);
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
#if defined( FBL_ENABLE_DATA_PROCESSING )
/* Reset current data length, no full re-initialization necessary */
gProcWriteJob.used = 0u;
#endif /* FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_STREAM_OUTPUT ) && \
defined( FBL_ENABLE_DATA_PROCESSING )
/* Reset current data length, no full re-initialization necessary */
gStreamProcJob.used = 0u;
#endif /* FBL_MEM_ENABLE_STREAM_OUTPUT && FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/* Setup pipelined verification queue */
FblMemQueueInit(gVerifyPipeQueue, FBL_MEM_ARRAY_SIZE(gVerifyPipeQueue));
queueHandle = (tFblMemQueueHandle)FBL_MEM_QUEUE_HANDLE_ENTRY_OFFSET;
# if ( FBL_MEM_QUEUE_ENTRIES_VERIFY_PIPE_READ == 1u )
idx = 0u;
# else
for (idx = 0u; idx < FBL_MEM_QUEUE_ENTRIES_VERIFY_PIPE_READ; idx++)
# endif /* FBL_MEM_QUEUE_ENTRIES_VERIFY_PIPE_READ */
{
/* Initialize job */
# if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED_ADDRESS_LENGTH )
/* Assign dedicated buffer for segment information */
FblMemInitJob(&gVerifyPipeReadJob[idx], gVerifyPipeInfoBuffer[idx], FBL_MEM_VERIFY_ADDRESS_LENGTH_BUFFER_SIZE, kFblMemJobType_VerifyPipeRead);
# else
FblMemInitJob(&gVerifyPipeReadJob[idx], FBL_MEM_BUFFER_NULL, 0, kFblMemJobType_VerifyPipeRead);
# endif
/* Set dedicated job class */
gVerifyPipeReadJob[idx].jobClass = tFblMemJobClass_VerifyPipe;
/* Assign job to verify queue entry */
FblMemQueueGetEntry(gVerifyPipeQueue, queueHandle).job = &gVerifyPipeReadJob[idx];
# if ( FBL_MEM_QUEUE_ENTRIES_VERIFY_PIPE_READ > 1u )
queueHandle++;
# endif /* FBL_MEM_QUEUE_ENTRIES_VERIFY_PIPE_READ */
}
/* Initialize jobs for update and finalize */
FblMemInitJob(&gVerifyPipeUpdateJob, gVerifyPipeReadBuffer, FBL_MEM_VERIFY_PIPELINED_SEGMENTATION, kFblMemJobType_VerifyPipeUpdate);
FblMemInitJob(&gVerifyPipeFinalizeJob, FBL_MEM_BUFFER_NULL, 0, kFblMemJobType_VerifyPipeFinalize);
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
#if defined( FBL_ENABLE_SYSTEM_CHECK )
FblMemInitBufferIntegrity();
#endif /* FBL_ENABLE_SYSTEM_CHECK */
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
FblMemInitProgress();
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
/* Allow block start / erase indication */
FblMemSetAllowed(FBL_MEM_ALLOWED_BLOCK_START | FBL_MEM_ALLOWED_BLOCK_ERASE);
return FblMemGetActiveBuffer();
}
#if defined( FBL_MEM_ENABLE_PREAMBLE_HANDLING )
/***********************************************************************************************************************
* FblMemStorePreamble
**********************************************************************************************************************/
/*! \brief Store the current buffer preamble (protocol information before actual data)
* \details Active fill buffer may be exchanged by subsequent operations, so preamble has to be restored for
* calling instance
* \pre FblMemInitPowerOn executed before, fill buffer available
**********************************************************************************************************************/
static void FblMemStorePreamble( void )
{
#if defined( FBL_MEM_ENABLE_DYNAMIC_PREAMBLE_LENGTH )
/* Verify preamble fits into reserved buffer */
assertFblGen(FBL_MEM_PREAMBLE_LENGTH <= FBL_MEM_MAX_PREAMBLE_LENGTH, kFblMemAssertParameterOutOfRange);
#endif /* FBL_MEM_ENABLE_DYNAMIC_PREAMBLE_LENGTH */
/* Copy preamble from active fill to temporary buffer */
(void)MEMCPY(gPreambleBuffer, FblMemGetActiveBuffer(), FBL_MEM_PREAMBLE_LENGTH); /* PRQA S 0314 */ /* MD_FblMem_0314_0326_MemCpy */
}
/***********************************************************************************************************************
* FblMemRestorePreamble
**********************************************************************************************************************/
/*! \brief Restore the previously stored preamble (protocol information before actual data) into the (new) active
* fill buffer
* \details Active buffer could be exchanged by preceding operations, so preamble has to be restored for calling
* instance
* \pre FblMemInitPowerOn executed before, fill buffer available
**********************************************************************************************************************/
static void FblMemRestorePreamble( void )
{
/* Copy previously stored preamble from temporary to active fill buffer */
(void)MEMCPY(FblMemGetActiveBuffer(), gPreambleBuffer, FBL_MEM_PREAMBLE_LENGTH); /* PRQA S 0314 */ /* MD_FblMem_0314_0326_MemCpy */
}
#endif /* FBL_MEM_ENABLE_PREAMBLE_HANDLING */
#if defined( FBL_MEM_ENABLE_SEGMENTATION ) || \
defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/***********************************************************************************************************************
* FblMemLimitLength
**********************************************************************************************************************/
/*! \brief Length will be truncated if limit is exceeded
* \details Original value will be returned if finalization operation mode is set
* \param[in] inputLen Original length
* \param[in] type Type of active job
* \param[in] mode Current operation mode (used for finalization)
* \return Input length limited to given range
**********************************************************************************************************************/
static tFblLength FblMemLimitLength( tFblLength inputLen, tFblMemJobType type, tFblMemOperationMode mode )
{
tFblLength lengthLimit;
tFblLength localInputLen;
localInputLen = inputLen;
# if defined( V_ENABLE_USE_DUMMY_STATEMENT )
/* Parameters not used: avoid compiler warning */
# if defined( FBL_MEM_ENABLE_SEGMENTATION )
# else
(void)mode;
# endif /* FBL_MEM_ENABLE_SEGMENTATION */
# endif /* V_ENABLE_USE_DUMMY_STATEMENT */
/* Valid job type? */
assertFblInternal((type < kFblMemJobType_Max), kFblMemAssertParameterOutOfRange);
lengthLimit = gLengthLimits[type].limit;
if (lengthLimit > 0u)
{
# if defined( FBL_MEM_ENABLE_SEGMENTATION )
/* Segmentation explicitly enabled
Truncate to given limit, unless unlimited mode for job type (typically finalize) is set */
if ((gLengthLimits[type].unlimitedMode != mode) && (localInputLen > lengthLimit))
# else
/* Truncate to given limit, if unconditional mode is configured for job type */
if ((gLengthLimits[type].unlimitedMode == kFblMemOperationMode_Unconditional) && (localInputLen > lengthLimit))
# endif /* FBL_MEM_ENABLE_SEGMENTATION */
{
localInputLen = lengthLimit;
}
}
return localInputLen;
}
#endif /* FBL_MEM_ENABLE_SEGMENTATION || FBL_MEM_ENABLE_VERIFY_PIPELINED */
/***********************************************************************************************************************
* FblMemPadLength
**********************************************************************************************************************/
/*! \brief Calculate number of bytes required to pad provided address range to memory segment size
* \param[in] address Start address of memory range
* \param[in] length Length of memory range
* \return Number of required padding bytes
**********************************************************************************************************************/
static tFblLength FblMemPadLength( tFblAddress address, tFblLength length )
{
tFblLength localAddress;
tFblLength localLength;
tFblLength padLen;
tFblAddress alignMask;
vsint16 oldSegment;
/* Local copy of length */
localLength = length;
/* Special handling required for zero length
Address used directly */
if (length > 0u)
{
localLength--;
}
/* Calculate end address */
localAddress = address + localLength;
/* Initialize padding length */
padLen = 0u;
/* Remember current memory segment */
oldSegment = memSegment;
/* Evaluate memory segment */
memSegment = FblMemSegmentNrGet(localAddress);
/* Check if segment was found */
if (memSegment >= 0)
{
/* Bit mask for memory segment alignment */
alignMask = (tFblAddress)(MemDriver_SegmentSize - 1uL);
/* Padding length calculated for end address of data to be padded */
/* Invert all masked bits */
padLen = localAddress ^ alignMask;
/* Modify padding length for special case (zero length) */
if (0u == length)
{
padLen++;
}
/* Apply mask to cut of unnecessary bits (e.g. additional address info, overflow of addition) */
padLen &= alignMask;
}
/* Restore memory segment */
memSegment = oldSegment;
return padLen;
}
/***********************************************************************************************************************
* FblMemPadBuffer
**********************************************************************************************************************/
/*! \brief Fill up the provided buffer with kFillChar if not aligned to the memory segment size
* \pre Buffer provided has to be large enough to hold added padding bytes
* \param[in] address Start address of memory range
* \param[in] length Length of memory range
* \param[in,out] data Pointer to last byte of actual data
* \return Number of padded bytes
**********************************************************************************************************************/
static tFblLength FblMemPadBuffer( tFblAddress address, tFblLength length, tFblMemRamData data )
{
tFblMemRamData padBuffer;
tFblLength padLen;
tFblLength idx;
/* Calculate number of required padding bytes */
padLen = FblMemPadLength(address, length);
/* In case data was already aligned the last buffer byte may be located at the very last memory address
Prevent address wrap around by relocating buffer not until padding is necessary */
if (padLen > 0u)
{
/* Input buffer points to last byte of actual data */
padBuffer = &data[1];
/* Append data to align buffer to segment size */
for (idx = 0u; idx < padLen; idx++)
{
#if defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER )
/* Save original data, restored after programming operation */
gPaddingBuffer[idx] = padBuffer[idx];
#endif /* FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER */
padBuffer[idx] = kFillChar;
}
}
return padLen;
}
#if defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER )
/***********************************************************************************************************************
* FblMemUnpadBuffer
**********************************************************************************************************************/
/*! \brief Restore previous state of buffer after padding with fill pattern
* \pre memSegment correctly initialized (only relevant for multiple memory devices configuration)
* FblMemPadBuffer called before with exact same parameters
* \param[in] data Pointer to last byte of actual data
* \param[in] padLen Number of previously padded bytes
**********************************************************************************************************************/
static void FblMemUnpadBuffer( tFblMemRamData data, tFblLength padLen )
{
/* In case data was already aligned the last buffer byte may be located at the very last memory address
Prevent address wrap around by relocating buffer not until padding is necessary */
if (padLen > 0u)
{
/* Restore original data, overwritten by padding */
(void)MEMCPY(&data[1u], gPaddingBuffer, padLen); /* PRQA S 0314 */ /* MD_FblMem_0314_0326_MemCpy */
}
}
#endif /* FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER */
/***********************************************************************************************************************
* FblMemCopyBuffer
**********************************************************************************************************************/
/*! \brief Performs program operation to volatile memory
* \param[in] programAddress Program address
* \param[in] programLength Length of data (output: length actually programmed)
* \param[in] programData Pointer to program data
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
static tFblMemStatus FblMemCopyBuffer( tFblAddress programAddress,
const V_MEMRAM1 tFblLength V_MEMRAM2 V_MEMRAM3 * programLength, tFblMemConstRamData programData )
{
/* Copy input buffer to destination address */
__ApplFblMemCopyBuffer(programAddress, programData, *programLength); /* PRQA S 0314, 0326 */ /* MD_FblMem_0314_0326_MemCpy */
return kFblMemStatus_Ok;
}
/***********************************************************************************************************************
* FblMemEraseRegionInternal
**********************************************************************************************************************/
/*! \brief Performs erase operation in non-volatile memory
* \details All memory segments fully or partially covered by given region are affected. Gaps in the memory segment
* definition are skipped.
* \pre Memory driver initialized
* \param[in] eraseAddress Start address of erase region
* \param[in] eraseLength Length of erase region
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
/* PRQA S 2889, 6010, 6030 1 */ /* MD_FblMem_2889, MD_MSR_STPTH, MD_MSR_STCYC */
static tFblMemStatus FblMemEraseRegionInternal( tFblAddress eraseAddress, tFblLength eraseLength )
{
tFblMemStatus retVal;
IO_ErrorType flashErrorCode;
tFblLength eraseRemainder;
tFblLength currentLength;
tFblLength distance;
vsint16 nextMemSegment;
tFblAddress localEraseAddress;
#if defined( __ApplFblMemConditionCheck )
tFblMemStatus customReturnCode;
#endif /* __ApplFblMemConditionCheck */
retVal = kFblMemStatus_Ok;
localEraseAddress = eraseAddress;
eraseRemainder = eraseLength;
memSegment = FblMemSegmentNrGet(localEraseAddress);
/* Check if there is a valid block for start address */
if (memSegment < 0)
{
FBL_MEM_SET_EXT_INFO(EraseAddress, localEraseAddress);
FBL_MEM_SET_STATUS(EraseOutsideFbt, retVal);
}
else
{
nextMemSegment = memSegment;
while (eraseRemainder > 0u)
{
/* Watchdog and response pending handling */
FblMemTriggerWatchdog();
/* Force response pending on first loop cycle */
FblMemResponsePending();
/* Initialize error address */
FBL_MEM_SET_EXT_INFO(EraseAddress, localEraseAddress);
#if defined( __ApplFblMemDriverReady )
/* Check if required flash driver is initialized */
if (kFblOk != __ApplFblMemDriverReady(memSegment))
{
/* Flash driver initialization failure */
FBL_MEM_SET_STATUS(EraseDriverNotReady, retVal);
return retVal;
}
#endif
/* Check if erase range crosses Flash block boundary */
currentLength = (FlashBlock[memSegment].end - localEraseAddress) + 1u;
if (eraseRemainder > currentLength)
{
nextMemSegment = memSegment + 1;
if (nextMemSegment >= (vsint16)kNrOfFlashBlock)
{
/* End of erase region lies behind defined memory */
FBL_MEM_SET_STATUS(EraseOutsideFbt, retVal);
return retVal;
}
/* Update remainder */
distance = FlashBlock[nextMemSegment].begin - localEraseAddress;
if (distance <= eraseRemainder)
{
eraseRemainder -= distance;
}
else
{
/* End of erase region lies in gap */
FBL_MEM_SET_STATUS(EraseOutsideFbt, retVal);
return retVal;
}
}
else
{
currentLength = eraseRemainder;
eraseRemainder = 0u;
}
#if defined( __ApplFblMemConditionCheck )
/* Check conditions before executing memory operation */
customReturnCode = __ApplFblMemConditionCheck();
if (kFblMemStatus_Ok != customReturnCode)
{
retVal = customReturnCode;
return retVal;
}
#endif /* __ApplFblMemConditionCheck */
#if defined( __ApplFblMemPreErase )
/* Perform actions directly before memory driver erase */
if (kFblOk != __ApplFblMemPreErase())
{
FBL_MEM_SET_STATUS(ErasePreErase, retVal);
return retVal;
}
#endif /* __ApplFblMemPreErase */
flashErrorCode = MemDriver_REraseSync(currentLength, localEraseAddress);
#if defined( __ApplFblMemPostErase )
/* Perform actions directly after memory driver erase */
if (kFblOk != __ApplFblMemPostErase())
{
FBL_MEM_SET_STATUS(ErasePostErase, retVal);
return retVal;
}
#endif /* __ApplFblMemPostErase */
#if defined( __ApplFblMemConditionCheck )
/* Check conditions after executing memory operation */
customReturnCode = __ApplFblMemConditionCheck();
if (kFblMemStatus_Ok != customReturnCode)
{
retVal = customReturnCode;
return retVal;
}
#endif /* __ApplFblMemConditionCheck */
if (IO_E_OK != flashErrorCode)
{
/* Error while erasing */
FBL_MEM_SET_EXT_STATUS(DriverErase, flashErrorCode, retVal);
return retVal;
}
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/* Only report progress when explicitly enabled */
if (kFblMemProgressState_Enabled == gProgressState)
{
FblMemUpdateProgress(eraseRemainder);
}
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
/* Continue with next segment */
memSegment = nextMemSegment;
localEraseAddress = FlashBlock[memSegment].begin;
}
}
return retVal;
}
/***********************************************************************************************************************
* FblMemProgramBufferInternal
**********************************************************************************************************************/
/*! \brief Performs program operation to non-volatile memory
* \details If the length is not aligned to the segment size the odd bytes are padded with the configured fill
* character.
* Programming may be suspended by an external event. In this case parameter programLength will be
* updated to reflect the length actually programmed
* In case resumable programming is active, crossing a flash block boundary leads to the setting
* of the programming state to the passed value.
* \pre Memory driver initialized, address aligned to memory segment size
* \param[in] programAddress Program address
* \param[in,out] programLength Length of data (output: length actually programmed)
* \param[in,out] programData Pointer to program data (contents are padded in case length is not aligned to memory
* segment size!)
* \param[in] checkPointState Programming state value to be set in case a checkpoint is reached
* Typical values are either kFblMemProgState_Checkpoint (suspend programming and indicate checkpoint)
* or kFblMemProgState_Pending (continue programming)
*
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
/* PRQA S 2889, 6010, 6030, 6050 1 */ /* MD_FblMem_2889, MD_MSR_STPTH, MD_MSR_STCYC, MD_MSR_STCAL */
static tFblMemStatus FblMemProgramBufferInternal( tFblAddress programAddress,
V_MEMRAM1 tFblLength V_MEMRAM2 V_MEMRAM3 * programLength, tFblMemRamData programData, tFblMemProgState checkPointState )
{
tFblMemStatus retVal;
tFblLength currentLength;
tFblLength localLength;
tFblLength segLength;
tFblLength padLength;
tFblLength bufferIndex;
tFblLength padOffset;
IO_ErrorType flashErrorCode;
tFblAddress localProgramAddress;
#if defined( __ApplFblMemConditionCheck )
tFblMemStatus customReturnCode;
#endif /* __ApplFblMemConditionCheck */
#if defined( V_ENABLE_USE_DUMMY_STATEMENT )
/* Parameters not used: avoid compiler warning */
(void)checkPointState;
#endif /* V_ENABLE_USE_DUMMY_STATEMENT */
retVal = kFblMemStatus_Ok;
padLength = 0u; /* PRQA S 2982 */ /* MD_FblMem_2982 */
padOffset = 0u; /* PRQA S 2982 */ /* MD_FblMem_2982 */
localProgramAddress = programAddress;
/* Copy requested length to local variable */
localLength = *programLength;
/* Start at beginning of buffer */
bufferIndex = 0u;
/* Loop while data left and operation not suspended by external event */
while ((localLength > 0u) && (kFblMemProgState_Pending == fblMemProgState))
{
/* Watchdog and response pending handling */
FblMemTriggerWatchdog();
#if defined( FBL_ENABLE_ADAPTIVE_DATA_TRANSFER_RCRRP )
/* Disable forced RCR-RP for adaptive mode */
#else
/* Force response pending on first loop cycle */
FblMemResponsePending();
#endif /* FBL_ENABLE_ADAPTIVE_DATA_TRANSFER_RCRRP */
/* Initialize error address */
FBL_MEM_SET_EXT_INFO(ProgramAddress, localProgramAddress);
/* Length to be programmed in current loop cycle */
currentLength = localLength;
/* Evaluate memory segment */
memSegment = FblMemSegmentNrGet(localProgramAddress);
/* Check if segment was found */
if (memSegment < 0)
{
FBL_MEM_SET_STATUS(ProgramOutsideFbt, retVal);
return retVal;
}
#if defined( __ApplFblMemDriverReady )
/* Check if required flash driver is initialized */
if (kFblOk != __ApplFblMemDriverReady(memSegment))
{
/* Flash driver initialization failure */
FBL_MEM_SET_STATUS(ProgramDriverNotReady, retVal);
return retVal;
}
#endif
/* Check if address is aligned to memory segment size */
if (0u != (localProgramAddress & ((tFblAddress)MemDriver_SegmentSize - 1u)))
{
FBL_MEM_SET_STATUS(ProgramUnalignedAddress, retVal);
return retVal;
}
/* Remaining length until end of current segment */
segLength = (FlashBlock[memSegment].end - localProgramAddress) + 1u;
/* Download memory overlaps segment: adjust to current segment */
if (segLength < currentLength)
{
/* Adapt currentLength to segment programming size */
currentLength = segLength;
}
#if defined( FBL_ENABLE_FUNC_VIRTUAL_MEMORY )
if (MemDriver_IsVirtualMem(localProgramAddress) == IO_E_OK)
{
/* Announce the end-address to the virtual memory driver */
if (MemDriver_RVirtualMemPayloadEndSync(localProgramAddress + currentLength - 1u) != IO_E_OK)
{
FBL_MEM_SET_STATUS(ProgramDriverNotReady, retVal);
return retVal;
}
}
#endif /* FBL_ENABLE_FUNC_VIRTUAL_MEMORY */
if (segLength > currentLength)
{
/* Offset to last byte of actual data */
padOffset = bufferIndex + (currentLength - 1u);
/* Pad buffer to memory segment */
padLength = FblMemPadBuffer(localProgramAddress, currentLength, &programData[padOffset]);
currentLength += padLength;
}
#if defined( __ApplFblMemConditionCheck )
/* Check conditions before executing memory operation */
customReturnCode = __ApplFblMemConditionCheck();
if (kFblMemStatus_Ok != customReturnCode)
{
retVal = customReturnCode;
return retVal;
}
#endif /* __ApplFblMemConditionCheck */
#if defined( __ApplFblMemPreWrite )
/* Perform actions directly before memory driver write */
if (kFblOk != __ApplFblMemPreWrite())
{
FBL_MEM_SET_STATUS(ProgramPreWrite, retVal);
return retVal;
}
#endif /* __ApplFblMemPreWrite */
/* Pass programming request to memory driver */
flashErrorCode = MemDriver_RWriteSync(&programData[bufferIndex], currentLength, localProgramAddress);
#if defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER )
/* Restore original data, overwritten by padding */
FblMemUnpadBuffer(&programData[padOffset], padLength);
#endif /* FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER */
#if defined( __ApplFblMemPostWrite )
/* Perform actions directly after memory driver write */
if (kFblOk != __ApplFblMemPostWrite())
{
FBL_MEM_SET_STATUS(ProgramPostWrite, retVal);
return retVal;
}
#endif /* __ApplFblMemPostWrite */
#if defined( __ApplFblMemConditionCheck )
/* Check conditions after executing memory operation */
customReturnCode = __ApplFblMemConditionCheck();
if (kFblMemStatus_Ok != customReturnCode)
{
retVal = customReturnCode;
return retVal;
}
#endif /* __ApplFblMemConditionCheck */
/* Check result of programming operation */
if (IO_E_OK != flashErrorCode)
{
FBL_MEM_SET_EXT_STATUS(DriverWrite, flashErrorCode, retVal);
return retVal;
}
/* Update address for next cycle */
localProgramAddress += (tFblAddress)currentLength;
if (localLength > currentLength)
{
/* Prepare buffer index for next loop */
bufferIndex += currentLength;
localLength -= currentLength;
}
else
{
/* Lengths should be exactly the same here */
assertFblInternal(((currentLength - padLength) == localLength), kFblMemAssertParameterOutOfRange);
/* Buffer completely processed */
localLength = 0u;
}
}
/* Return length actually written (without padding) */
*programLength -= localLength;
return retVal;
}
# if defined( FBL_ENABLE_MULTIPLE_MEM_DEVICES ) && \
defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/***********************************************************************************************************************
* FblMemGetSpecificRemainder
**********************************************************************************************************************/
/*! \brief Calculate remainder of given range specific for affected memory device
* \param[in] address Start address of memory range
* \param[in] length Length of memory range
* \return Remainder to full memory segment in byte
**********************************************************************************************************************/
static tFblLength FblMemGetSpecificRemainder( tFblAddress address, tFblLength length )
{
tFblLength localRemainder;
vsint16 tempSegment;
/* Initialize remainder */
localRemainder = 0u;
/* Re-evaluate memory segment */
tempSegment = memSegment;
memSegment = FblMemSegmentNrGet(address + (length - 1u));
if (memSegment >= 0)
{
/* Calculate remainder */
localRemainder = FblMemGetRemainder(address, length, MemDriver_SegmentSize);
}
/* Restore previous memory segment value */
memSegment = tempSegment;
return localRemainder;
}
# endif /* FBL_ENABLE_MULTIPLE_MEM_DEVICES && FBL_MEM_ENABLE_REMAINDER_HANDLING */
#if defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER )
/***********************************************************************************************************************
* FblMemRelocateBufferOffset
**********************************************************************************************************************/
/*! \brief Verify provided buffer lies within range of existing buffer and relocate offset accordingly
* \details Provided buffer has to reside completely in existing buffer
* \param[in,out] activeJob Information of buffer to be updated
* \param[in] buffer Pointer to provided buffer
* \param[in] offset Offset index into provided buffer
* \param[in] length Length of data in provided buffer
* \return kFblMemStatus_Ok if requested buffer lies inside provided information and offset could be relocated,
* kFblMemStatus_Failed otherwise
**********************************************************************************************************************/
static tFblMemStatus FblMemRelocateBufferOffset( V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * activeJob,
tFblMemConstRamData buffer, tFblLength offset, tFblLength length )
{
tFblMemStatus retVal;
tFblMemConstRamData activeBuffer;
tFblLength activeOffset;
retVal = kFblMemStatus_Failed;
activeBuffer = activeJob->buffer;
/* Provided buffer has to reside in raw input buffer */
if (buffer >= activeBuffer)
{
activeOffset = (tFblLength)(buffer - activeBuffer); /* PRQA S 0488 */ /* MD_FblMem_0488 */
/* Provided data has to lie within raw input buffer (excluding potential padding) */
if ( (length <= activeJob->netSize)
&& (offset <= (activeJob->netSize - length))
&& (activeOffset < activeJob->totalSize)
&& ((offset + length + FBL_MEM_REMAINDER_PADDING) <= (activeJob->totalSize - activeOffset)) )
{
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/* Remainder requires reserved area in front of actual data (defined by current offset) */
if ((activeOffset + offset) >= activeJob->offset)
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
{
/* Relocate offset to provided buffer */
activeJob->offset = activeOffset;
retVal = kFblMemStatus_Ok;
}
}
}
return retVal;
}
#endif /* FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER */
#if defined( FBL_MEM_ENABLE_VARYING_INPUT_BUFFER )
/***********************************************************************************************************************
* FblMemSearchInputBuffer
**********************************************************************************************************************/
/*! \brief Search matching input buffer for provided buffer
* \details Look for input buffer in which provided buffer completely resides, respective job information is
* moved to beginning of free queue and offset is relocated to match provided buffer
* \param[in] buffer Pointer to provided buffer
* \param[in] offset Offset index into provided buffer
* \param[in] length Length of data in provided buffer
* \return kFblMemStatus_Ok if requested buffer lies inside of available buffer and offset could be relocated,
* kFblMemStatus_Failed otherwise
**********************************************************************************************************************/
static tFblMemStatus FblMemSearchInputBuffer( tFblMemConstRamData buffer, tFblLength offset, tFblLength length )
{
tFblMemStatus retVal;
tFblMemQueueHandle queueHandle;
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * activeJob;
retVal = kFblMemStatus_Failed;
/* Get first buffer handle */
queueHandle = FblMemQueueGetFirstFreeHandle(FBL_MEM_PIPE_PROG_QUEUE);
/* Loop all free buffers */
while (FBL_MEM_QUEUE_HANDLE_HEAD_FREE != queueHandle)
{
activeJob = FBL_MEM_PIPE_PROG_QUEUE[queueHandle].job;
/* Check for matching input buffer */
if (kFblMemStatus_Ok == FblMemRelocateBufferOffset(activeJob, buffer, offset, length))
{
/* Move to beginning of free queue */
(void)FblMemQueueMove(FBL_MEM_PIPE_PROG_QUEUE, queueHandle, FBL_MEM_QUEUE_HANDLE_HEAD_FREE);
retVal = kFblMemStatus_Ok;
break;
}
/* Get next free buffer */
queueHandle = FBL_MEM_PIPE_PROG_QUEUE[queueHandle].next;
}
return retVal;
}
#endif /* FBL_MEM_ENABLE_VARYING_INPUT_BUFFER */
/***********************************************************************************************************************
* FblMemProgramStream
**********************************************************************************************************************/
/*! \brief Take a byte stream to be programmed and align it to the memory requirements
* \details If the length is not aligned to the segment size the odd bytes are stored in a remainder buffer,
* which will be programmed on the next call.
* Programming may be suspended by an external event. In this case parameter programLength will be
* updated to reflect the length actually programmed.
* \pre FblMemSegmentStartIndication executed before
* \param[in] programJob Information of buffer to be programmed
* \param[in,out] programLength Requested program length (output: length actually programmed)
* \param[in] mode Operation mode (include remainder in case of finalization)
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
/* PRQA S 6010, 6030 1 */ /* MD_MSR_STPTH, MD_MSR_STCYC */
static tFblMemStatus FblMemProgramStream( const V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * programJob,
V_MEMRAM1 tFblLength V_MEMRAM2 V_MEMRAM3 * programLength, tFblMemOperationMode mode )
{
tFblMemStatus retVal;
tFblMemRamData programData;
tFblLength localLength;
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
tFblLength requestLength;
tFblLength localRemainder;
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
#if defined( V_ENABLE_USE_DUMMY_STATEMENT )
/* Parameters not used: avoid compiler warning */
# if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
# else
(void)mode;
# endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
#endif /* V_ENABLE_USE_DUMMY_STATEMENT */
FblMemTriggerWatchdog();
/* Store requested length to local variable */
localLength = *programLength;
/* Pointer to current buffer position */
programData = FblMemGetBuffer(programJob);
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
localRemainder = 0u;
/* No remainder handling required for volatile memory */
if (kFblMemType_RAM != gSegInfo.input.type)
{
/*
Handle remainder from previous run
Special case where remainder is empty is not explicitly handled
as performing the following operations with a value of zero
doesn't have any side-effects
*/
/* Check available space in front of current data */
assertFblInternal(gSegInfo.writeRemainder <= programJob->position, kFblMemAssertParameterOutOfRange);
/* Redirect programming pointer to make room for remainder */
programData = &programJob->buffer[programJob->position - gSegInfo.writeRemainder];
/* Copy remainder in front of actual data */
(void)MEMCPY(programData, gRemainderBuffer.data, gSegInfo.writeRemainder); /* PRQA S 0314 */ /* MD_FblMem_0314_0326_MemCpy */
/* Update length to be programmed */
localLength += gSegInfo.writeRemainder;
/* Write complete data if explicit finalization is requested */
if ( (kFblMemOperationMode_Finalize == mode)
# if defined( FBL_ENABLE_PROCESSED_DATA_LENGTH )
# else
/* or end of requested segment is reached */
|| (localLength == gSegInfo.writeLength)
# endif /* FBL_ENABLE_PROCESSED_DATA_LENGTH */
)
{
/* No remainder left */
}
else
{
/* Calculate new remainder, not aligned to segment size */
localRemainder = FblMemGetWriteRemainder(gSegInfo.writeAddress, localLength);
/* Handle special case: When the segment was already finalized, the padding isn't included in write address
Remainder calculation thus includes data already written, resulting in a value larger than the requested length
This would cause two range overflows, but is only a cosmetic issue, as it cancels out in the end */
if (localRemainder > localLength)
{
localRemainder = 0u;
}
/* Update length to be programmed */
localLength -= localRemainder;
}
}
/* Remember actual programming length, may be altered by programming routine */
requestLength = localLength;
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
/* Verify complete programming length does not exceed previously requested memory range */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
if ((localLength + localRemainder) > gSegInfo.writeLength)
#else
if (localLength > gSegInfo.writeLength)
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
{
FBL_MEM_SET_STATUS(ProgramOverflow, retVal);
}
else
{
/*
Call programming function even if actual length is zero
In this case the input length will be completely moved to the remainder
*/
if (kFblMemType_RAM == gSegInfo.input.type)
{
/* Copy to volatile memory */
retVal = FblMemCopyBuffer(gSegInfo.writeAddress, &localLength, programData);
}
else
{
/* Program non-volatile memory */
retVal = FblMemProgramBufferInternal(gSegInfo.writeAddress, &localLength, programData, kFblMemProgState_Checkpoint);
}
}
/* Check result */
if (kFblMemStatus_Ok == retVal)
{
/* Returned length shall not exceed requested length */
assertFblInternal(localLength <= gSegInfo.writeLength, kFblMemAssertParameterOutOfRange);
/* Update segment information */
gSegInfo.writeAddress += (tFblAddress)localLength;
/* Reduce expected remainder */
gSegInfo.writeLength -= localLength;
#if defined( FBL_ENABLE_PROCESSED_DATA_LENGTH )
/* Length actually written */
gSegInfo.writtenLength += localLength;
#endif /* FBL_ENABLE_PROCESSED_DATA_LENGTH */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/* Everything consumed, program length already matches */
if (requestLength == localLength)
{
/* Everything written, store new remaining data */
gSegInfo.writeRemainder = localRemainder;
/* Call copy function even if remainder length is zero */
(void)MEMCPY(gRemainderBuffer.data, &programData[localLength], localRemainder); /* PRQA S 0314 */ /* MD_FblMem_0314_0326_MemCpy */
}
/* Data partially programmed (most likely suspended by external event) */
else if (localLength > 0u)
{
/* Store consumed length */
*programLength = localLength - gSegInfo.writeRemainder;
/* Some data written, no remainder */
gSegInfo.writeRemainder = 0u;
}
else
{
/* Nothing consumed at all */
*programLength = 0u;
/* Nothing written at all, remainder from previous run unchanged */
}
#else
/* Store consumed length */
*programLength = localLength;
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
}
return retVal;
}
#if defined( FBL_MEM_ENABLE_VERIFY_STREAM )
/***********************************************************************************************************************
* FblMemVerifyInput
**********************************************************************************************************************/
/*! \brief Execute input verification operation
* \details Pass input data to given input verification routine
* No operation will be carried out if null pointer is passed as function
* \param[in] routine Input verification operation to be executed
* \param[in] data Verification data
* - FBL_MEM_VERIFY_STATE_INIT / FBL_MEM_VERIFY_STATE_FINALIZE: Usually irrelevant
* - FBL_MEM_VERIFY_STATE_COMPUTE: Data to be hashed
* - FBL_MEM_VERIFY_STATE_VERIFY: Reference checksum / signature
* state Sub-operation to be executed
* \param[in,out] result Pointer to extended verification result
* FBL_MEM_VERIFY_STATUS_NULL if neither relevant nor used
* \return kFblMemStatus_Ok if operation successfully executed,
* kFblMemStatus_Failed otherwise
**********************************************************************************************************************/
/* PRQA S 3673 1 */ /* MD_FblMem_3673 */
static tFblMemStatus FblMemVerifyInput( V_MEMRAM1 tFblMemVerifyRoutineInput V_MEMRAM2 V_MEMRAM3 * routine,
const V_MEMRAM1 tFblMemVerifyData V_MEMRAM2 V_MEMRAM3 * data, vuint8 state,
V_MEMRAM1 tFblMemVerifyStatus V_MEMRAM2 V_MEMRAM3 * result )
{
tFblMemStatus retVal;
tFblMemVerifyStatus localResult;
retVal = kFblMemStatus_Ok;
localResult = FBL_MEM_VERIFY_OK;
/* Null pointer passed as verification function disables functionality */
if (FBL_MEM_VERIFY_FCT_INPUT_NULL != routine->function)
{
routine->param->sigState = state;
routine->param->sigSourceBuffer = data->data;
routine->param->sigByteCount = (tFblMemVerifyLength)data->length;
# if defined( FBL_MEM_ENABLE_EXT_TRIGGER_INPUT_VERIFY )
routine->param->wdTriggerFct = (tFblMemVerifyWdFct)&FblMemTriggerWatchdogExt; /* PRQA S 0313 */ /* MD_FblMem_0313 */
# else
routine->param->wdTriggerFct = (tFblMemVerifyWdFct)&FblMemTriggerWatchdog;
# endif /* FBL_MEM_ENABLE_EXT_TRIGGER_INPUT_VERIFY */
/* Call verification function and set extended status */
localResult = routine->function(routine->param);
if (FBL_MEM_VERIFY_OK != localResult)
{
retVal = kFblMemStatus_Failed;
}
}
if (FBL_MEM_VERIFY_STATUS_NULL != result)
{
/* Pass extended result */
*result |= localResult;
}
return retVal;
}
/***********************************************************************************************************************
* FblMemInitVerifyInput
**********************************************************************************************************************/
/*! \brief Initialize input verification(s)
* \return kFblMemStatus_Ok if operation successfully executed,
* kFblMemStatus_Failed otherwise
**********************************************************************************************************************/
static tFblMemStatus FblMemInitVerifyInput( void )
{
tFblMemStatus retVal;
tFblMemVerifyData verifyData;
retVal = kFblMemStatus_Ok;
/* Not used by initialization */
verifyData.data = FBL_MEM_BUFFER_NULL;
verifyData.length = 0u;
/* Inverted order of verification routines
Error code may be overwritten (simplifies implementation) */
# if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/* Initialize the calculation */
if (kFblMemStatus_Ok != FblMemVerifyInput( &gBlockInfo.verifyRoutinePipe, &verifyData,
FBL_MEM_VERIFY_STATE_INIT, FBL_MEM_VERIFY_STATUS_NULL ))
{
/* Overwrites previous error code */
retVal = kFblMemStatus_Failed;
}
# endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
return retVal;
}
#endif /* FBL_MEM_ENABLE_VERIFY_STREAM */
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/***********************************************************************************************************************
* FblMemPrepareVerifyPipeJob
**********************************************************************************************************************/
/*! \brief Get active pipelined verification job
* \details If no previous job is pending for the requested segment, a new job is queued, using the provided address
* as starting point.
* \param[in] segmentIndex Index of requested segment
* \param[in] address Address of newly programmed data
* \return Pointer to the current verify job
**********************************************************************************************************************/
static V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * FblMemPrepareVerifyPipeJob( vuintx segmentIndex, tFblAddress address )
{
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * verifyJob;
verifyJob = FBL_MEM_JOB_NULL;
/* Null pointer passed as verification function disables functionality */
if (FBL_MEM_VERIFY_FCT_INPUT_NULL != gBlockInfo.verifyRoutinePipe.function)
{
/* Check for error condition */
if (kFblMemStatus_Ok == gErrorStatus)
{
/* Verification job for current segment not yet included in processing queue */
if ( (FblMemQueueIsEmpty(gVerifyPipeQueue))
|| (FblMemQueueGetLastUsedEntry(gVerifyPipeQueue).job->segmentIndex != segmentIndex) )
{
/* Assert queue not full */
assertFblInternal((!FblMemQueueIsFull(gVerifyPipeQueue)), kFblMemAssertParameterOutOfRange);
/* Get first free job */
verifyJob = FblMemQueueGetFirstFreeEntry(gVerifyPipeQueue).job;
/* Start verification at current address */
verifyJob->baseAddress = address;
verifyJob->position = 0u;
verifyJob->netSize = 0u;
verifyJob->totalSize = 0u;
/* Add job to processing queue */
(void)FblMemQueueDefaultPrioInsert(gProcessingQueue, verifyJob, segmentIndex);
/* Append input info to separate queue, managing verification jobs exclusively */
(void)FblMemQueueAppend(gVerifyPipeQueue);
}
/* Get active verification job */
verifyJob = FblMemQueueGetLastUsedEntry(gVerifyPipeQueue).job;
}
}
return verifyJob;
}
/***********************************************************************************************************************
* FblMemUpdateVerifyPipeJob
**********************************************************************************************************************/
/*! \brief Update job of pipelined verification
* \details Add programmed data contents to queue, so it can be verified in parallel to data transmission.
* If no previous data is pending for the current segment, a new job is queued, using the provided address
* as starting point.
* The length is added to the last job in the queue.
* \param[in] segmentIndex Index of requested segment
* \param[in] address Address of newly programmed data
* \param[in] length Length of newly programmed data
* \return Pointer to the current verify job
**********************************************************************************************************************/
static V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * FblMemUpdateVerifyPipeJob( vuintx segmentIndex, tFblAddress address, tFblLength length )
{
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * verifyJob;
verifyJob = FBL_MEM_JOB_NULL;
if (length > 0u)
{
verifyJob = FblMemPrepareVerifyPipeJob(segmentIndex, address);
/* Check for null pointer */
if (FBL_MEM_JOB_NULL != verifyJob)
{
/* Update pipelined verification information */
verifyJob->used += length;
verifyJob->netSize += length;
verifyJob->totalSize += length;
}
}
return verifyJob;
}
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
/***********************************************************************************************************************
* FblMemProcessJob
**********************************************************************************************************************/
/*! \brief Perform processing operations on provided job
* \details Depending on the type and configuration one of the following operations will be carried out:
* - Update verification with buffer contents
* - Read already programmed data in temporary buffer for verification
* - Process buffer contents (e.g. decryption or decompression)
* Result placed in intermediate buffer, which is prepended to the processing queue
* - Program buffer contents
* - Flush program remainder
* - Pass data to other instance
* Buffer may be processed at once or split into smaller segments, requiring multiple call cycles to
* finish the buffer
* This depends on the configuration and finalization flag
* \pre FblMemSegmentStartIndication executed before
* \param[in] activeJob Information of processed buffer
* \param[in] mode Operation mode used to trigger finalization
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
/* PRQA S 6010, 6030, 6050, 6080 1 */ /* MD_MSR_STPTH, MD_MSR_STCYC, MD_MSR_STCAL, MD_MSR_STMIF */
static tFblMemStatus FblMemProcessJob( V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * activeJob, tFblMemOperationMode mode )
{
tFblLength actualLength;
#if defined( FBL_MEM_ENABLE_VERIFY_STREAM ) || \
defined( FBL_ENABLE_DATA_PROCESSING ) || \
defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
tFblMemRamData activeBuffer;
#endif /* FBL_MEM_ENABLE_VERIFY_STREAM || FBL_ENABLE_DATA_PROCESSING || FBL_MEM_ENABLE_STREAM_OUTPUT || FBL_MEM_ENABLE_PASSTHROUGH */
#if defined( FBL_ENABLE_DATA_PROCESSING )
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * procOutJob;
static vuint8 tryCounterDataProc;
#endif /* FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_VERIFY_STREAM )
tFblMemVerifyData verifyData;
#endif /* FBL_MEM_ENABLE_VERIFY_STREAM */
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
tFblAddress prevAddress;
tFblLength prevRemainder;
tFblLength pipeVerificationLength;
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
#if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
tFblResult streamResult;
#endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
#if defined( FBL_MEM_ENABLE_GAP_FILL )
tFblAddress fillAddress;
tFblLength fillLength;
tFblLength tempLength;
#endif /* FBL_MEM_ENABLE_GAP_FILL */
tFblMemOperationMode finalizeStream;
#if defined( V_ENABLE_USE_DUMMY_STATEMENT )
/* Parameters not used: avoid compiler warning */
# if defined( FBL_MEM_ENABLE_SEGMENTATION )
# else
(void)mode;
# endif /* FBL_MEM_ENABLE_SEGMENTATION */
#endif /* V_ENABLE_USE_DUMMY_STATEMENT */
/* Handle watchdog trigger */
FblMemTriggerWatchdog();
#if defined( FBL_MEM_ENABLE_VERIFY_STREAM ) || \
defined( FBL_ENABLE_DATA_PROCESSING ) || \
defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
/* Get pointer to current buffer position */
activeBuffer = FblMemGetBuffer(activeJob);
#endif /* FBL_MEM_ENABLE_VERIFY_STREAM || FBL_ENABLE_DATA_PROCESSING || FBL_MEM_ENABLE_STREAM_OUTPUT || FBL_MEM_ENABLE_PASSTHROUGH */
/* Limit processed length to configured value */
actualLength = FblMemLimitLength(activeJob->used, activeJob->type, mode);
/* Handle buffer types */
switch (activeJob->type)
{
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/* Pipelined verification */
# if defined( FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH )
case kFblMemJobType_VerifyPipeInfo:
{
/* Pass buffer to signature calculation */
verifyData.data = activeBuffer;
verifyData.length = actualLength;
/* Add current data to the signature */
if (kFblMemStatus_Ok != FblMemVerifyInput( &gBlockInfo.verifyRoutinePipe, &verifyData,
FBL_MEM_VERIFY_STATE_COMPUTE, FBL_MEM_VERIFY_STATUS_NULL ))
{
/* Compute operation failed */
FBL_MEM_SET_STATUS(VerifyCompute, gErrorStatus);
}
/* Switch job type to read back programmed data */
activeJob->type = kFblMemJobType_VerifyPipeRead;
/* Prevent modification of position value used by read back operation */
activeJob->used -= actualLength;
actualLength = 0u;
break;
}
# endif /* FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH */
case kFblMemJobType_VerifyPipeRead:
{
# if defined( FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH )
/* Reset to default completion mode, as address information operates in explicit finalization mode */
activeJob->completion = kFblMemOperationMode_Normal;
# endif /* FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH */
/* Verify the pipelined read only in case there is data to be verified */
if (0u != actualLength)
{
/* Read back a chunk of the already programmed data for verification */
# if defined( FBL_MEM_ENABLE_SWITCH_READMEMORY_PARAM )
/* Parameters order changed in comparison to HIS security module specification */
if (gBlockInfo.readFct(activeJob->baseAddress + activeJob->position, actualLength, gVerifyPipeUpdateJob.buffer) != actualLength)
# else
/* Parameters order as defined by HIS security module specification */
if (gBlockInfo.readFct(activeJob->baseAddress + activeJob->position, gVerifyPipeUpdateJob.buffer, actualLength) != actualLength)
# endif /* FBL_MEM_ENABLE_SWITCH_READMEMORY_PARAM */
{
/* Read operation failed */
FBL_MEM_SET_STATUS(VerifyCompute, gErrorStatus);
}
else
{
/* Data successfully read, pass to update routine */
gVerifyPipeUpdateJob.position = 0u;
gVerifyPipeUpdateJob.used = actualLength;
/* Queue updated job */
(void)FblMemQueueDefaultPrioInsert(gProcessingQueue, &gVerifyPipeUpdateJob, activeJob->segmentIndex);
}
}
break;
}
case kFblMemJobType_VerifyPipeUpdate:
{
/* Pass buffer to signature calculation */
verifyData.data = activeBuffer;
verifyData.length = actualLength;
/* Add current data to the signature */
if (kFblMemStatus_Ok != FblMemVerifyInput( &gBlockInfo.verifyRoutinePipe, &verifyData,
FBL_MEM_VERIFY_STATE_COMPUTE, FBL_MEM_VERIFY_STATUS_NULL ))
{
/* Compute operation failed */
FBL_MEM_SET_STATUS(VerifyCompute, gErrorStatus);
}
break;
}
case kFblMemJobType_VerifyPipeFinalize:
{
/* Not used by finalization */
verifyData.data = activeBuffer;
verifyData.length = actualLength;
/* Finish the calculation */
if (kFblMemStatus_Ok != FblMemVerifyInput( &gBlockInfo.verifyRoutinePipe, &verifyData,
FBL_MEM_VERIFY_STATE_FINALIZE, FBL_MEM_VERIFY_STATUS_NULL ))
{
/* Verify finalization failed */
FBL_MEM_SET_STATUS(VerifyFinalize, gErrorStatus);
}
break;
}
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
#if defined( FBL_ENABLE_DATA_PROCESSING )
/* Data processing */
case kFblMemJobType_ProcInput:
case kFblMemJobType_ProcFinalize:
{
# if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
if (kFblOk == __ApplFblMemIsStreamOutputRequired(gSegInfo.input.dataFormat))
{
procOutJob = &gStreamProcJob;
}
else
# endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
{
procOutJob = &gProcWriteJob;
}
/* Update position of output buffer to accommodate for programming remainder */
procOutJob->position = gSegInfo.writeRemainder;
/* Limit input length to 16 bit */
if (actualLength > 0xFFFFu)
{
actualLength = 0xFFFFu;
}
/*
Initialize parameters for data processing.
Note: Other members have been set during initialization
*/
gProcParam.dataBuffer = activeBuffer;
gProcParam.dataLength = (vuint16)actualLength;
/* Align output buffer according to current fill level */
gProcParam.dataOutBuffer = &((FblMemGetBuffer(procOutJob))[procOutJob->used]); /* PRQA S 2822 */ /* MD_FblMem_2822 */
gProcParam.dataOutLength = 0u;
/* Call API function for user specific data processing */
if (kFblOk != ApplFblDataProcessing(&gProcParam))
{
FBL_MEM_SET_STATUS(DataProc, gErrorStatus);
}
else
{
if ((gProcParam.dataOutLength | gProcParam.dataLength) == 0u)
{
if (kFblMemJobType_ProcFinalize == activeJob->type)
{
/* Finalize data processing
gProcWriteJob already has correct values */
if (kFblOk != ApplFblDeinitDataProcessing(&gProcParam))
{
FBL_MEM_SET_STATUS(DataProcDeinit, gErrorStatus);
}
/* Finish job processing */
activeJob->completion = kFblMemOperationMode_Unconditional;
}
else
{
if (tryCounterDataProc >= FBL_MEM_TRY_COUNTER_LIMIT_DATA_PROC)
{
/* Nothing consumed or produced at all, avoid endless loop */
FBL_MEM_SET_STATUS(DataProcConsume, gErrorStatus);
}
else
{
tryCounterDataProc++;
}
}
}
else
{
tryCounterDataProc = 0u;
}
}
if (kFblMemStatus_Ok == gErrorStatus)
{
/* Update consumed length */
actualLength = gProcParam.dataLength;
/* Any data produced? */
if (gProcParam.dataOutLength > 0u)
{
/* Output buffer changed? */
assertFblUser((gProcParam.dataOutBuffer == &((FblMemGetBuffer(procOutJob))[procOutJob->used])), kFblMemAssertUserResultOutOfRange); /* PRQA S 2822 */ /* MD_FblMem_2822 */
/* Output length exceeds configured limit? */
assertFblUser((gProcParam.dataOutLength <= FBL_MEM_INTERNAL_PROC_SEGMENTATION), kFblMemAssertUserResultOutOfRange);
/* Store processed data information */
procOutJob->used += gProcParam.dataOutLength;
/* Total used length exceeds buffer limits? */
assertFblInternal((procOutJob->used <= (procOutJob->totalSize - procOutJob->offset)), kFblMemAssertParameterOutOfRange);
# if defined( FBL_MEM_ENABLE_PROC_SEGMENTATION )
/* Accumulate multiple data processing calls before passing output data to write routine */
/* Buffer was previously empty
Add behind current input data using low prio so that data will definitely be programmed after processing */
if (procOutJob->used == gProcParam.dataOutLength)
{
/* Insert processed data into the queue and remember handle */
gProcHandle = FblMemQueueDefaultPrioInsert(gProcessingQueue, procOutJob, activeJob->segmentIndex);
}
/* Buffer is (almost) filled and has to be programmed before continuing processing */
if ((procOutJob->netSize - procOutJob->used) < FBL_MEM_INTERNAL_PROC_SEGMENTATION)
{
/* Move processed data buffer in front of input data job by updating to higher priority
Handle set during current or previous cycle */
(void)FblMemQueuePrioUpdate(gProcessingQueue, gProcHandle, (tFblMemQueuePrio)kFblMemJobPrio_ProcWriteHigh);
}
# else
/* Insert processed data into the queue */
(void)FblMemQueueDefaultPrioInsert(gProcessingQueue, procOutJob, activeJob->segmentIndex);
# endif /* FBL_MEM_ENABLE_PROC_SEGMENTATION */
}
}
break;
}
case kFblMemJobType_ProcWrite:
#endif /* FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
case kFblMemJobType_WriteFinalize:
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
case kFblMemJobType_InputWrite:
{
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/* Remember current address and remainder length */
prevAddress = gSegInfo.writeAddress;
prevRemainder = gSegInfo.writeRemainder;
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
if (kFblMemJobType_WriteFinalize == activeJob->type)
{
finalizeStream = kFblMemOperationMode_Finalize;
/* Update position of output buffer to accommodate for programming remainder */
activeJob->position = gSegInfo.writeRemainder;
}
# if defined( FBL_ENABLE_UNALIGNED_DATA_TRANSFER )
# else
else if (kFblMemJobType_InputWrite == activeJob->type)
{
/* Force programming of any potential remainder */
finalizeStream = kFblMemOperationMode_Finalize;
}
# endif /* FBL_ENABLE_UNALIGNED_DATA_TRANSFER */
else
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
{
finalizeStream = kFblMemOperationMode_Normal;
}
/* Program data, error code reflects result of operation */
gErrorStatus = FblMemProgramStream(activeJob, &actualLength, finalizeStream);
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/* Update progress information */
if (kFblMemStatus_Ok == gErrorStatus)
{
#if defined( FBL_MEM_ENABLE_GAP_FILL )
FblMemUpdateProgress(gSegInfo.writeLength + gGapFillJob.used);
#else
FblMemUpdateProgress(gSegInfo.writeLength);
#endif
}
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/* Update size of data for pipelined verification */
pipeVerificationLength = ((actualLength + prevRemainder) - gSegInfo.writeRemainder);
#if defined( FBL_VERIFICATION_SEGMENT_INDEX )
/* Check if written data is part of the verification process */
if ((prevAddress + pipeVerificationLength) > (gSegInfo.input.logicalLength + gSegInfo.input.targetAddress))
{
/* Data (signature/CRC) might be written at addresses larger than logical end of data, prevent underflow */
if (prevAddress > (gSegInfo.input.logicalLength + gSegInfo.input.targetAddress))
{
/* No more data to verify, signature/CRC was written */
pipeVerificationLength = 0u;
}
else
{
/* Verify data until signature/CRC starts */
pipeVerificationLength = (gSegInfo.input.logicalLength + gSegInfo.input.targetAddress) - prevAddress;
}
}
#endif /* FBL_VERIFICATION_SEGMENT_INDEX */
/* Update pipelined verification information
Start verification at current write address
Include currently written data without possible remainder */
(void)FblMemUpdateVerifyPipeJob(gSegInfo.ownIndex, prevAddress, pipeVerificationLength);
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
break;
}
#if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
case kFblMemJobType_StreamInput:
case kFblMemJobType_StreamProc:
case kFblMemJobType_StreamFinalize:
{
/*
Initialize parameters for stream output.
Note: Other members have been set during initialization
*/
gStreamParam.inputData = activeBuffer;
gStreamParam.inputLength = actualLength;
gStreamParam.consumedLength = 0u;
gStreamParam.producedLength = 0u;
gStreamParam.address = gSegInfo.writeAddress;
gStreamParam.length = gSegInfo.writeLength;
if (kFblMemJobType_StreamFinalize == activeJob->type)
{
/* Call API function for user specific stream output */
streamResult = ApplFblFinalizeStreamOutput(&gStreamParam);
}
else
{
/* Call API function for user specific stream output */
streamResult = ApplFblStreamOutput(&gStreamParam);
}
if (kFblOk != streamResult)
{
FBL_MEM_SET_STATUS(StreamOutput, gErrorStatus);
}
else
{
if ((gStreamParam.consumedLength | gStreamParam.producedLength) == 0u)
{
if (kFblMemJobType_StreamFinalize == activeJob->type)
{
/* Finalize stream output
Parameter already has correct values */
if (kFblOk != ApplFblDeinitStreamOutput(&gStreamParam))
{
FBL_MEM_SET_STATUS(StreamOutputDeinit, gErrorStatus);
}
/* Finish job processing */
activeJob->completion = kFblMemOperationMode_Unconditional;
}
else
{
/* Nothing consumed or produced at all, avoid endless loop */
FBL_MEM_SET_STATUS(StreamOutputConsume, gErrorStatus);
}
}
}
if (kFblMemStatus_Ok == gErrorStatus)
{
/* Update consumed length */
actualLength = gStreamParam.consumedLength;
/* Any data produced? */
if (gStreamParam.producedLength > 0u)
{
/* Verify complete programming length does not exceed previously requested memory range */
if (gStreamParam.producedLength > gSegInfo.writeLength)
{
FBL_MEM_SET_STATUS(StreamOutputConsume, gErrorStatus);
}
else
{
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/* Update pipelined verification information */
(void)FblMemUpdateVerifyPipeJob(gSegInfo.ownIndex, gSegInfo.writeAddress, gStreamParam.producedLength);
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
/* Update segment information */
gSegInfo.writeAddress += gStreamParam.producedLength;
/* Reduce expected remainder */
gSegInfo.writeLength -= gStreamParam.producedLength;
#if defined( FBL_ENABLE_PROCESSED_DATA_LENGTH )
/* Length actually written */
gSegInfo.writtenLength += gStreamParam.producedLength;
#endif /* FBL_ENABLE_PROCESSED_DATA_LENGTH */
}
}
}
break;
}
#endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
#if defined( FBL_MEM_ENABLE_GAP_FILL )
case kFblMemJobType_GapFill:
{
/* Start address of current fill operation */
fillAddress = activeJob->baseAddress + activeJob->position;
/* Remember total length */
fillLength = actualLength;
/* Reset length actually filled */
actualLength = 0u;
/* Find start segment for current address */
memSegment = FblMemSegmentNrGet(fillAddress);
/* Valid segment addressed? */
if (memSegment < 0)
{
/* Address lies in gap between flash blocks
Range till next block can be skipped */
/* Use last filled address to get follow-up segment */
memSegment = FblMemSegmentNrGet(fillAddress - 1u) + 1;
/* Check segment range */
if ((memSegment >= (vsint16)kNrOfFlashBlock) || (memSegment <= 0))
{
FBL_MEM_SET_STATUS(ProgramOutsideFbt, gErrorStatus);
}
else
{
/* Skip gap */
if (FlashBlock[memSegment].begin > fillAddress)
{
/* Distance to start of next flash block can be skipped */
tempLength = FlashBlock[memSegment].begin - fillAddress;
if (tempLength < fillLength)
{
/* Range partially covers next flash block
Adapt fill range accordingly */
actualLength = tempLength;
fillLength -= actualLength;
fillAddress = FlashBlock[memSegment].begin;
}
else
{
/* Full range lies within gap and can be skipped completely */
actualLength = fillLength;
fillLength = 0u;
}
}
else
{
/* Full range lies within gap and can be skipped completely */
actualLength = fillLength;
fillLength = 0u;
}
}
}
if (kFblMemStatus_Ok == gErrorStatus)
{
/* Limit fill operation to configured segmentation (equals size of prepared buffer) */
if (fillLength > FBL_MEM_GAP_FILL_SEGMENTATION)
{
fillLength = FBL_MEM_GAP_FILL_SEGMENTATION;
}
/* Distance to end of current flash block */
tempLength =(FlashBlock[memSegment].end - fillAddress) + 1u;
/* Limit fill range to current flash block */
if (fillLength > tempLength)
{
fillLength = tempLength;
}
/* Perform actual fill operation */
if (fillLength > 0u)
{
/* Program non-volatile memory */
gErrorStatus = FblMemProgramBufferInternal(fillAddress, &fillLength, gGapFillBuffer.data, kFblMemProgState_Pending);
/* Update length actually processed */
actualLength += fillLength;
}
}
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
if (kFblMemStatus_Ok == gErrorStatus)
{
/* Update programming progress */
FblMemUpdateProgress(gSegInfo.writeLength + (gGapFillJob.used - actualLength));
}
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
break;
}
#endif /* FBL_MEM_ENABLE_GAP_FILL */
case kFblMemJobType_Max:
default:
{
/* Invalid buffer type */
FBL_MEM_SET_STATUS(Failed, gErrorStatus);
break;
}
}
/* Update buffer usage */
activeJob->position += actualLength;
activeJob->used -= actualLength;
#if defined( FBL_ENABLE_SYSTEM_CHECK )
if (kFblMemStatus_Ok == gErrorStatus)
{
if (kFblMemStatus_Ok != FblMemVerifyBufferIntegrity())
{
/* Detected buffer corruption */
FBL_MEM_SET_STATUS(Failed, gErrorStatus);
}
}
#endif /* FBL_ENABLE_SYSTEM_CHECK */
/* Return global error state as function may be called in both service and background context */
return gErrorStatus;
}
/***********************************************************************************************************************
* FblMemProcessQueue
**********************************************************************************************************************/
/*! \brief Process first job in processing queue (directly use single input job if un-queued)
* \details Remove finished job from processing queue (and pipelined queue in case of input job)
* Switch to idle mode if all pending jobs are finished
* \pre FblMemQueueBuffer executed before
* \param[in] mode Operation mode used to trigger finalization
**********************************************************************************************************************/
/* PRQA S 6010, 6030, 6080 1 */ /* MD_MSR_STPTH, MD_MSR_STCYC, MD_MSR_STMIF */
static void FblMemProcessQueue( tFblMemOperationMode mode )
{
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
tFblMemQueueHandle activeHandle;
#endif /* FBL_MEM_ENABLE_PROC_QUEUE */
#if defined( FBL_MEM_ENABLE_PIPELINING )
V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * removeQueue;
#endif /* FBL_MEM_ENABLE_PIPELINING */
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * activeJob;
FblMemTriggerWatchdog();
/*
Critical section secures access to programming state
May also be altered in interrupt context
*/
__ApplFblMemEnterCriticalSection();
/*
Module in idle or error state?
Condition equals ((kFblMemProgState_Idle == fblMemProgState) || (kFblMemProgState_Error == fblMemProgState))
*/
if (fblMemProgState < kFblMemProgState_Suspended)
{
/* Nothing to do */
}
#if defined( FBL_MEM_ENABLE_PIPELINING )
/* Only explicit pending state allowed while programming in background context */
else if ( (kFblMemContext_Background == gProgContext)
&& (kFblMemProgState_Pending != fblMemProgState) )
{
/* Module suspended */
}
#endif /* FBL_MEM_ENABLE_PIPELINING */
else
{
/* Operation potentially paused during previous execution cycle */
fblMemProgState = kFblMemProgState_Pending;
}
/*
No further critical access to programming state
Critical section can be left
*/
__ApplFblMemLeaveCriticalSection();
if (kFblMemProgState_Pending == fblMemProgState)
{
#if defined( FBL_MEM_ENABLE_PIPELINING )
removeQueue = FBL_MEM_QUEUE_NULL;
#endif /* FBL_MEM_ENABLE_PIPELINING */
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
/* Set active job, stored in first entry of processing queue */
activeHandle = FblMemQueueGetFirstUsedHandle(gProcessingQueue);
activeJob = gProcessingQueue[activeHandle].job;
#else
/* Set active job to single input job */
activeJob = &FBL_MEM_INPUT_JOB[0];
#endif /* FBL_MEM_ENABLE_PROC_QUEUE */
/* Perform processing cycle on active job */
if (kFblMemStatus_Ok == FblMemProcessJob(activeJob, mode))
{
/* Buffer completely processed and completion mode allows job to be cleared? */
if ( (kFblMemOperationMode_Unconditional == activeJob->completion)
|| ((kFblMemOperationMode_Normal == activeJob->completion) && (0u == activeJob->used)) )
{
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
/* Remove buffer from processing queue */
(void)FblMemQueueRemove(gProcessingQueue, activeHandle);
if (FblMemQueueIsEmpty(gProcessingQueue))
#endif /* FBL_MEM_ENABLE_PROC_QUEUE */
{
/* Processing queue empty: switch to idle state */
fblMemProgState = kFblMemProgState_Idle;
}
#if defined( FBL_MEM_ENABLE_PIPELINING )
/* Update additional queues */
switch (activeJob->jobClass)
{
# if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
/* Processed buffer is an input buffer */
case tFblMemJobClass_PipeProg:
{
{
/* Remove from input buffer queue */
removeQueue = FBL_MEM_PIPE_PROG_QUEUE;
}
break;
}
# endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
# if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
case tFblMemJobClass_VerifyPipe:
{
/* Remove from verification job queue */
removeQueue = gVerifyPipeQueue;
break;
}
# endif
/* Default class: no special action required */
case tFblMemJobClass_Default:
/* Ignore unknown job class */
default:
{
/* Nothing to do */
break;
}
}
#endif /* FBL_MEM_ENABLE_PIPELINING */
}
}
else
{
/* Error while processing buffer: switch to error state */
fblMemProgState = kFblMemProgState_Error;
#if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
/* Remove from input buffer queue, otherwise all buffer may be in use */
removeQueue = FBL_MEM_PIPE_PROG_QUEUE;
#endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
}
#if defined( FBL_MEM_ENABLE_PIPELINING )
/*
Remove from input buffer queue
Buffer available for new data again
*/
if ( (FBL_MEM_QUEUE_NULL != removeQueue)
&& (!FblMemQueueIsEmpty(removeQueue)) )
{
(void)FblMemQueueRemove(removeQueue, FblMemQueueGetFirstUsedHandle(removeQueue));
}
#endif /* FBL_MEM_ENABLE_PIPELINING */
}
}
/***********************************************************************************************************************
* FblMemFlushQueueByPrio
**********************************************************************************************************************/
/*! \brief Process all jobs in processing queue with priority greater or equal to given value
* \details Loop until processing queue is empty or a job with a priority lower to the given one is reached
* \pre FblMemQueueBuffer executed before
* \param[in] prio Lowest job priority to be processed
**********************************************************************************************************************/
static void FblMemFlushQueueByPrio( tFblMemQueuePrio prio )
{
#if defined( V_ENABLE_USE_DUMMY_STATEMENT )
/* Parameters not used: avoid compiler warning */
# if defined( FBL_MEM_ENABLE_PROC_QUEUE )
# else
(void)prio;
# endif /* FBL_MEM_ENABLE_PROC_QUEUE */
#endif /* V_ENABLE_USE_DUMMY_STATEMENT */
/* Loop while jobs are pending */
while ( (FblMemTaskIsPending())
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
/* Only jobs with priority greater or equal to given one are processed */
&& (FblMemQueueGetFirstUsedEntry(gProcessingQueue).prio >= prio)
#endif
)
{
/* Execute processing cycle */
FblMemProcessQueue(kFblMemOperationMode_Finalize);
}
}
#if defined( FBL_MEM_ENABLE_PIPELINING )
/***********************************************************************************************************************
* FblMemUnblockQueue
**********************************************************************************************************************/
/*! \brief Unblock queue by freeing at least one queue entry
* \details Loop until given queue isn't completely filled any more
* \pre FblMemQueueBuffer executed before
* \param[in] queue Pointer to queue which should be unblocked
**********************************************************************************************************************/
static void FblMemUnblockQueue( const V_MEMRAM1 tFblMemQueueEntry V_MEMRAM2 V_MEMRAM3 * queue )
{
/* Loop while queue is completely filled and jobs are pending */
while ( (FblMemQueueIsFull(queue)) && (FblMemTaskIsPending()) )
{
/* Execute processing cycle */
FblMemProcessQueue(kFblMemOperationMode_Normal);
}
}
#endif /* FBL_MEM_ENABLE_PIPELINING */
/***********************************************************************************************************************
* FblMemQueueBuffer
**********************************************************************************************************************/
/*! \brief Handle new chunk of input data
* \details If any combination of verification, data processing and pipelined programming is configured add jobs to
* processing queue
* Otherwise processing will directly work on single input buffer
* If pipelined programming is configured queue buffer and finish processing of one pending input buffer
* in case no free input buffer is available
* \pre FblMemInitPowerOn executed before, provided buffer and data offset equal the parameters of active
* input buffer
* \param[in] buffer Pointer to input buffer (including preamble)
* \param[in] offset Offset of actual data (after preamble)
* \param[in] length Length of data (without preamble)
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
static tFblMemStatus FblMemQueueBuffer( tFblMemConstRamData buffer, tFblLength offset, tFblLength length )
{
tFblMemStatus retVal;
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * activeJob;
#if defined( V_ENABLE_USE_DUMMY_STATEMENT )
/* Parameters not used: avoid compiler warning */
# if defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER ) || \
defined( FBL_ENABLE_SYSTEM_CHECK ) || \
defined( FBL_ENABLE_ASSERTION )
# else
(void)buffer;
# endif /* FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER || FBL_ENABLE_SYSTEM_CHECK || FBL_ENABLE_ASSERTION */
#endif /* V_ENABLE_USE_DUMMY_STATEMENT */
#if defined( FBL_MEM_ENABLE_VARYING_INPUT_BUFFER )
#else
/* Received data resides in active fill buffer */
activeJob = FblMemGetPendingInputJob();
#endif /* FBL_MEM_ENABLE_VARYING_INPUT_BUFFER */
#if defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER ) || \
defined( FBL_ENABLE_SYSTEM_CHECK )
# if defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER )
# if defined( FBL_MEM_ENABLE_VARYING_INPUT_BUFFER )
if (kFblMemStatus_Ok != FblMemSearchInputBuffer(buffer, offset, length))
# else
if (kFblMemStatus_Ok != FblMemRelocateBufferOffset(activeJob, buffer, offset, length))
# endif /* FBL_MEM_ENABLE_VARYING_INPUT_BUFFER */
# else /* FBL_ENABLE_SYSTEM_CHECK */
/* Limit buffer handling to previously provided buffer */
if ( (buffer != FblMemGetBuffer(activeJob))
|| (offset >= activeJob->netSize)
|| (length > (activeJob->netSize - offset)) )
# endif /* FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER */
{
FBL_MEM_SET_STATUS(DataIndParam, retVal);
}
else
#else
assertFblUser(buffer == FblMemGetBuffer(activeJob), kFblMemAssertParameterOutOfRange);
assertFblUser(offset < activeJob->netSize, kFblMemAssertParameterOutOfRange);
assertFblUser(length <= (activeJob->netSize - offset), kFblMemAssertParameterOutOfRange);
#endif /* FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER || FBL_ENABLE_SYSTEM_CHECK */
{
#if defined( FBL_MEM_ENABLE_VARYING_INPUT_BUFFER )
activeJob = FblMemGetPendingInputJob();
#endif /* FBL_MEM_ENABLE_VARYING_INPUT_BUFFER */
/* Evaluate current error state and directly return if not okay (error in background task) */
if (kFblMemProgState_Error != fblMemProgState)
{
/* Update buffer state with provided information */
activeJob->position = activeJob->offset + offset;
activeJob->used = length;
#if defined( FBL_ENABLE_DATA_PROCESSING ) || \
defined( FBL_MEM_ENABLE_STREAM_OUTPUT ) || \
defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/*
Switch type of input buffer to value set in segment indication:
write through, pass through, data processing or stream output
*/
activeJob->type = gSegInfo.jobType;
#endif /* FBL_ENABLE_DATA_PROCESSING || FBL_MEM_ENABLE_STREAM_OUTPUT || FBL_MEM_ENABLE_PASSTHROUGH || FBL_MEM_ENABLE_REMAINDER_HANDLING */
{
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/* Processing queue full */
if (FblMemQueueIsFull(gVerifyPipeQueue))
{
# if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED_ADDRESS_LENGTH )
/* Last entry is exclusively used for storage of address information
This reserved job has to be free at all times so no blocking operation is necessary during segment start */
# else
/* Verification job for current segment not yet included */
if (FblMemQueueGetLastUsedEntry(gVerifyPipeQueue).job->segmentIndex != gSegInfo.ownIndex)
# endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED_ADDRESS_LENGTH */
{
/* Free at least one queue entry */
FblMemUnblockQueue(gVerifyPipeQueue);
}
}
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
}
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
# if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/* Check for error condition */
if (kFblMemProgState_Error != fblMemProgState)
# endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
{
/* Append buffer to processing queue */
(void)FblMemQueueDefaultPrioInsert(gProcessingQueue, activeJob, gSegInfo.ownIndex);
# if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
/* Append input info to separate queue, managing input buffers exclusively */
(void)FblMemQueueAppend(FBL_MEM_PIPE_PROG_QUEUE);
/* Free at least one queue entry */
FblMemUnblockQueue(FBL_MEM_PIPE_PROG_QUEUE);
# endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
}
#endif /* FBL_MEM_ENABLE_PROC_QUEUE */
#if defined( FBL_MEM_ENABLE_PIPELINING )
/* Check for error condition */
if (kFblMemProgState_Error != fblMemProgState)
#endif /* FBL_MEM_ENABLE_PIPELINING */
{
/* Set pending programming request flag */
fblMemProgState = kFblMemProgState_Pending;
}
}
/* Result depending on queue processing by FblMemUnblockQueue */
retVal = gErrorStatus;
}
return retVal;
}
/***********************************************************************************************************************
* FblMemCheckAllowed
**********************************************************************************************************************/
/*! \brief Check if all required states are set
* \details Clear certain states if successful, completely reset allowed states otherwise
* \pre Allowed state initialized before
* \param[in] check Bitmask of states which have to be active
* \param[in] clear Bitmask of states to be cleared in case check successful
* \return All required states set (kFblOk) or not (kFblFailed)
**********************************************************************************************************************/
static tFblResult FblMemCheckAllowed( tFblMemAllowedInd check, tFblMemAllowedInd clear )
{
tFblResult retVal;
/*
Check executed at beginning of (almost) every exported function
Trigger watchdog here
*/
FblMemTriggerWatchdog();
/* Check for required states */
if (FblMemIsAllowed(check))
{
/* Success: clear requested states */
FblMemClrAllowed(clear);
retVal = kFblOk;
}
else
{
/* Failure: completely reset states */
FblMemResetAllowed();
retVal = kFblFailed;
}
return retVal;
}
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/***********************************************************************************************************************
* FblMemCalculateProgress
**********************************************************************************************************************/
/*! \brief Calculate progress percentage
* \details Percentage is calculated according to the current progress, the expected maximum value and the resulting
* maximum percentage
* \param[in] current Current progress in arbitrary unit (e.g. bytes)
* \param[in] total Expected maximum value in arbitrary unit (e.g. bytes)
* \param[in] percentage Maximum reported percentage
* \return Current progress percentage
**********************************************************************************************************************/
static vuint8 FblMemCalculateProgress( vuint32 current, vuint32 total, vuint8 percentage )
{
vuint8 progress;
/* Init to maximum percentage */
progress = percentage;
/* Prevent division by zero */
if ((0u != total) && (0u != percentage))
{
/* Calculate progress value
Use different calculation paths depending on total value to prevent integer overflow or division by zero
Remark: Unsigned cast of -1 is guaranteed to result in maximum representable value */
if (total < (0xFFFFFFFFuL / (vuint32)percentage))
{
/* Applying multiplication to current value prevents loss of precision,
which otherwise could result in invalid percentages (e.g. 101%) */
progress = (vuint8)(((current * percentage) / total) & 0xFFu);
}
else
{
/* Total value is large enough to provide sufficient precision */
progress = (vuint8)((current / (total / percentage)) & 0xFFu);
}
}
/* Limit reported progress to maximum percentage */
if (progress > percentage)
{
progress = percentage;
}
return progress;
}
/***********************************************************************************************************************
* FblMemInitProgress
**********************************************************************************************************************/
/*! \brief Initialize progress states
* \details Called during startup
**********************************************************************************************************************/
static void FblMemInitProgress( void )
{
/* Initialize progress information */
gProgressInfo.type = kFblMemProgressType_Undefined;
/* Ensure reporting is triggered after change of any value */
gPrevProgressInfo.type = kFblMemProgressType_Undefined;
gPrevProgressInfo.totalProgress = FBL_MEM_PROGRESS_INITIAL;
gPrevProgressInfo.partialProgress = FBL_MEM_PROGRESS_INITIAL;
}
/***********************************************************************************************************************
* FblMemReportProgress
**********************************************************************************************************************/
/*! \brief Report current progress
* \details Report current progress to user callback in case any of the following applies:
* - Type or segment count has changed since previous report
* - Partial progress completed, but not reported yet
* - Total or partial progress increased by at least the configured threshold since previous report
**********************************************************************************************************************/
static void FblMemReportProgress( void )
{
/* Check for relevant changes since previous report */
if ( (FBL_MEM_PROGRESS_COMPLETE == gProgressInfo.partialProgress)
|| (gPrevProgressInfo.type != gProgressInfo.type)
|| (gPrevProgressInfo.segmentCount != gProgressInfo.segmentCount)
|| ((gProgressInfo.totalProgress - gPrevProgressInfo.totalProgress) >= (vuint8)FBL_MEM_PROGRESS_THRESHOLD_PERCENTAGE)
|| ((gProgressInfo.partialProgress - gPrevProgressInfo.partialProgress) >= (vuint8)FBL_MEM_PROGRESS_THRESHOLD_PERCENTAGE) )
{
/* Prevent re-reporting of completed partial progress */
if (FBL_MEM_PROGRESS_COMPLETE != gPrevProgressInfo.partialProgress)
{
/* Inform user callback */
__ApplFblMemReportProgress(&gProgressInfo);
/* Remember reported progress */
gPrevProgressInfo = gProgressInfo;
}
}
}
/***********************************************************************************************************************
* FblMemSetupProgress
**********************************************************************************************************************/
/*! \brief Setup progress for new partial operation
* \details Stores the meta information (type, logical block address and segment count), sets up the percentage
* offset and current contribution of the total operation and stores the target value of the partial operation
* \param[in] type Type of partial operation
* \param[in] logicalAddress Logical start address of processed block
* \param[in] segmentCount Count of segment processed by partial operation, typically zero for erase and verification,
* segment index for programming and index of last segment incremented by one for concluding
* gap fill
* \param[in] totalOffset Percentage offset of total operation for current operation type
* \param[in] totalPercentage Percentage contribution to total operation of current operation type
* \param[in] target Expected maximum value of partial operation in arbitrary unit (e.g. bytes)
**********************************************************************************************************************/
/* PRQA S 6060 1 */ /* MD_FblMem_6060 */
static void FblMemSetupProgress( tFblMemProgressType type, tFblAddress logicalAddress, vuint32 segmentCount,
vuint8 totalOffset, vuint8 totalPercentage, vuint32 target )
{
/* Store meta information */
gProgressInfo.type = type;
gProgressInfo.logicalAddress = logicalAddress;
gProgressInfo.segmentCount = segmentCount;
/* Setup percentage offset and contribution of total operation */
gProgressInfoInternal.totalOffset = totalOffset;
gProgressInfoInternal.totalPercentage = totalPercentage;
/* Store target value of partial operation */
gProgressInfoInternal.target = target;
/* Ensure first update is reported by setting default values for previously reported info */
gProgressPrevRemainder = 0u;
gPrevProgressInfo.totalProgress = FBL_MEM_PROGRESS_INITIAL;
gPrevProgressInfo.partialProgress = FBL_MEM_PROGRESS_INITIAL;
}
/***********************************************************************************************************************
* FblMemOffsetProgress
**********************************************************************************************************************/
/*! \brief Modify total percentages to reflect current progress
* \details During progress setup the total percentages reflect the complete contribution of all operations of the
* current type. This function offsets those values according to the current progress.
* \pre FblMemSetupProgress called before
* \param[in] totalDone Current total progress in arbitrary unit (e.g. bytes)
* \param[in] totalTarget Expected maximum value of total operation in arbitrary unit (e.g. bytes)
**********************************************************************************************************************/
static void FblMemOffsetProgress( vuint32 totalDone, vuint32 totalTarget )
{
/* Update offset according to current progress */
gProgressInfoInternal.totalOffset +=
FblMemCalculateProgress(totalDone, totalTarget, gProgressInfoInternal.totalPercentage);
/* Cut down percentage contribution according to current progress */
gProgressInfoInternal.totalPercentage =
FblMemCalculateProgress(gProgressInfoInternal.target, totalTarget, gProgressInfoInternal.totalPercentage);
}
/***********************************************************************************************************************
* FblMemUpdateProgress
**********************************************************************************************************************/
/*! \brief Update the progress information
* \details Progress information is updated according to the progress of the current partial operation
* Update is performed in case any of the following applies:
* - First update
* - Progress increased by at least the configured threshold since previous update
* \pre FblMemSetupProgress and optionally FblMemOffsetProgress called before
* \param[in] remainderPart Remainder of current partial operation in arbitrary unit (e.g. bytes)
**********************************************************************************************************************/
static void FblMemUpdateProgress( vuint32 remainderPart )
{
vuint32 done;
/* Check for relevant changes since previous update */
if ( (gProgressPrevRemainder < remainderPart)
|| ((gProgressPrevRemainder - remainderPart) >= FBL_MEM_PROGRESS_THRESHOLD_BYTES) )
{
/* Calculate current partial progress in arbitray unit */
done = gProgressInfoInternal.target - remainderPart;
/* Calculate progress percentages */
gProgressInfo.totalProgress =
gProgressInfoInternal.totalOffset
+ FblMemCalculateProgress(done, gProgressInfoInternal.target, gProgressInfoInternal.totalPercentage);
gProgressInfo.partialProgress =
FBL_MEM_PROGRESS_INITIAL + FblMemCalculateProgress(done, gProgressInfoInternal.target, FBL_MEM_PROGRESS_COMPLETE);
/* Report updated progress */
FblMemReportProgress();
/* Remember currently updated remainder value */
gProgressPrevRemainder = remainderPart;
}
}
/***********************************************************************************************************************
* FblMemConcludeProgress
**********************************************************************************************************************/
/*! \brief Conclude the current partial operation
* \details Explicitely set partial progress to maximum percentage and total progress according to the set up values
* \pre FblMemSetupProgress and optionally FblMemOffsetProgress called before
**********************************************************************************************************************/
static void FblMemConcludeProgress( void )
{
/* Set progress percentages to maximum values */
gProgressInfo.totalProgress = gProgressInfoInternal.totalOffset + gProgressInfoInternal.totalPercentage;
gProgressInfo.partialProgress = FBL_MEM_PROGRESS_COMPLETE;
/* Report updated progress */
FblMemReportProgress();
}
/***********************************************************************************************************************
* FblMemProgressRead
**********************************************************************************************************************/
/*! \brief Update verification progress
* \details Read operations of output verification are re-routed through this function to update the progress
* information according the current read address
* \param[in] address Memory address to read out
* \param[out] buffer Target buffer
* \param[in] length Number of bytes to read
* \return Number of actually copied bytes
**********************************************************************************************************************/
# if defined( FBL_MEM_ENABLE_VERIFY_OUTPUT )
# if defined( FBL_MEM_ENABLE_SWITCH_READMEMORY_PARAM )
/* Parameters order changed in comparison to HIS security module specification */
static tFblMemVerifySize FblMemProgressRead( tFblMemVerifyAddr address, tFblMemVerifySize length, tFblMemVerifyDataPtr buffer )
# else
/* Parameters order as defined by HIS security module specification */
static tFblMemVerifySize FblMemProgressRead( tFblMemVerifyAddr address, tFblMemVerifyDataPtr buffer, tFblMemVerifySize length )
# endif /* FBL_MEM_ENABLE_SWITCH_READMEMORY_PARAM */
{
vuint32 position;
/* Calculate position relative to block start address */
position = address - gBlockInfo.targetAddress;
/* Update progress with remainder */
FblMemUpdateProgress(gBlockInfo.targetLength - position);
/* Perform actual read operation */
return gBlockInfo.readFct(address, buffer, length);
}
# endif /* FBL_MEM_ENABLE_VERIFY_OUTPUT */
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
/***********************************************************************************************************************
* GLOBAL FUNCTIONS
**********************************************************************************************************************/
/***********************************************************************************************************************
* FblMemInitPowerOnExt
**********************************************************************************************************************/
/*! \brief Initialize module
* \param[in] preambleLen Length of preamble stored during buffer switch
* \param[in] sourceHandle Handle of input source
* \return Pointer to initial input buffer
**********************************************************************************************************************/
tFblMemRamData FblMemInitPowerOnExt( tFblLength preambleLen, tFblMemInputSource sourceHandle )
{
#if defined( FBL_MEM_ENABLE_GAP_FILL )
vuintx idx;
#endif /* FBL_MEM_ENABLE_GAP_FILL */
#if defined( V_ENABLE_USE_DUMMY_STATEMENT )
/* Parameters not used: avoid compiler warning */
# if defined( FBL_MEM_ENABLE_DYNAMIC_PREAMBLE_LENGTH )
# else
(void)preambleLen;
# endif /* FBL_MEM_ENABLE_DYNAMIC_PREAMBLE_LENGTH */
# if defined( FBL_MEM_ENABLE_MULTI_SOURCE )
# else
(void)sourceHandle;
# endif /* FBL_MEM_ENABLE_MULTI_SOURCE */
#endif /* V_ENABLE_USE_DUMMY_STATEMENT */
#if defined( FBL_MEM_ENABLE_MULTI_SOURCE )
/* Verify source handle lies within range */
assertFblUser(sourceHandle < FBL_MEM_SOURCE_COUNT, kFblMemAssertParameterOutOfRange);
#endif /* FBL_MEM_ENABLE_MULTI_SOURCE */
#if defined( FBL_MEM_ENABLE_PROC_QUEUE )
/* Verify job prio mapping array */
assertFblInternal((vuintx)kFblMemJobType_Max == (FBL_MEM_ARRAY_SIZE(gJobPrio) - 1u), kFblMemAssertParameterOutOfRange); /* PRQA S 2742, 2880 */ /* MD_FblMem_AssertJobMax, MD_MSR_Unreachable */
#endif /* FBL_MEM_ENABLE_PROC_QUEUE */
#if defined( FBL_MEM_ENABLE_SEGMENTATION ) || \
defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/* Verify length limit mapping array */
assertFblInternal((vuintx)kFblMemJobType_Max == (FBL_MEM_ARRAY_SIZE(gLengthLimits) - 1u), kFblMemAssertParameterOutOfRange); /* PRQA S 2742, 2880 */ /* MD_FblMem_AssertJobMax, MD_MSR_Unreachable */
#endif /* FBL_MEM_ENABLE_SEGMENTATION || FBL_MEM_ENABLE_VERIFY_PIPELINED */
#if defined( FBL_ENABLE_DATA_PROCESSING )
/* Initialize data processing info */
FblMemInitJob(&gProcWriteJob, gProcBuffer.data, FBL_MEM_ARRAY_SIZE(gProcBuffer.data), kFblMemJobType_ProcWrite);
/* Overwrite net size, to exclude overhead for remainder and padding */
gProcWriteJob.netSize = FBL_MEM_PROC_BUFFER_SIZE;
/* Initialize info to trigger data processing finalization */
FblMemInitJob(&gProcFinalizeJob, FBL_MEM_BUFFER_NULL, 0, kFblMemJobType_ProcFinalize);
/*
Always provide full configured segmentation size to data processing,
regardless of any remainder
*/
gProcParam.dataOutMaxLength = (vuint16)FBL_MEM_INTERNAL_PROC_SEGMENTATION;
# if defined( FBL_MEM_ENABLE_EXT_TRIGGER_DATA_PROC )
gProcParam.wdTriggerFct = &FblMemTriggerWatchdogExt;
# else
gProcParam.wdTriggerFct = FblMemTriggerWatchdog;
# endif /* FBL_MEM_ENABLE_EXT_TRIGGER_DATA_PROC */
#endif /* FBL_ENABLE_DATA_PROCESSING */
#if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
/* Initialize stream output info */
# if defined( FBL_ENABLE_DATA_PROCESSING )
FblMemInitJob(&gStreamProcJob, gProcBuffer.data, FBL_MEM_ARRAY_SIZE(gProcBuffer.data), kFblMemJobType_StreamProc);
/* Overwrite net size, to exclude overhead for remainder and padding */
gStreamProcJob.netSize = FBL_MEM_PROC_BUFFER_SIZE;
# endif /* FBL_ENABLE_DATA_PROCESSING */
/* Initialize info to trigger stream output finalization */
FblMemInitJob(&gStreamFinalizeJob, FBL_MEM_BUFFER_NULL, 0, kFblMemJobType_StreamFinalize);
gStreamParam.outputData = FBL_MEM_BUFFER_NULL;
gStreamParam.outputSize = 0u;
gStreamParam.watchdog = &FblMemTriggerWatchdog;
#endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
#if defined( FBL_MEM_ENABLE_GAP_FILL )
/* Initialize gap fill job */
FblMemInitJob(&gGapFillJob, FBL_MEM_BUFFER_NULL, FBL_MEM_ARRAY_SIZE(gGapFillBuffer.data), kFblMemJobType_GapFill);
/* Fill gap fill buffer with fill character */
for (idx = 0u; idx < FBL_MEM_ARRAY_SIZE(gGapFillBuffer.data); idx++)
{
gGapFillBuffer.data[idx] = kFillChar;
}
#endif /* FBL_MEM_ENABLE_GAP_FILL */
#if defined( FBL_MEM_ENABLE_MULTI_SOURCE )
gActiveSource = sourceHandle;
#endif /* FBL_MEM_ENABLE_MULTI_SOURCE */
#if defined( FBL_MEM_ENABLE_DYNAMIC_PREAMBLE_LENGTH )
gPreambleLength[FBL_MEM_ACTIVE_SOURCE] = preambleLen; /* PRQA S 2842 */ /* MD_FblMem_2842 */
#endif /* FBL_MEM_ENABLE_DYNAMIC_PREAMBLE_LENGTH */
/* Perform additional initialization and return initial buffer */
return FblMemInitInternal();
}
/***********************************************************************************************************************
* FblMemInitPowerOn
**********************************************************************************************************************/
/*! \brief Initialize module
* \return Pointer to initial input buffer
**********************************************************************************************************************/
tFblMemRamData FblMemInitPowerOn( void )
{
/* Perform initialization and return initial buffer */
return FblMemInitPowerOnExt(FBL_MEM_DEFAULT_PREAMBLE_LENGTH, FBL_MEM_SOURCE_HANDLE_DEFAULT);
}
/***********************************************************************************************************************
* FblMemInit
**********************************************************************************************************************/
/*! \brief (Re)-initialize input buffers
* \details Should be called before performing additional tasks in case a previous operation failed
* \pre FblMemInitPowerOn executed before
* \return Pointer to active input buffer
**********************************************************************************************************************/
tFblMemRamData FblMemInit( void )
{
tFblMemRamData activeBuffer;
#if defined( FBL_MEM_ENABLE_PREAMBLE_HANDLING )
/*
Active fill buffer may change
Store preamble so operation is transparent for caller
*/
FblMemStorePreamble();
#endif /* FBL_MEM_ENABLE_PREAMBLE_HANDLING */
/* Perform re-initialization and get active buffer */
activeBuffer = FblMemInitInternal();
#if defined( FBL_MEM_ENABLE_PREAMBLE_HANDLING )
/*
Active fill buffer may have changed
Restore previously stored preamble so operation is transparent for caller
*/
FblMemRestorePreamble();
#endif /* FBL_MEM_ENABLE_PREAMBLE_HANDLING */
return activeBuffer;
}
/***********************************************************************************************************************
* FblMemDeinit
**********************************************************************************************************************/
/*! \brief De-initialize module
* \details Should be called when memory driver is de-initialized
**********************************************************************************************************************/
void FblMemDeinit( void )
{
/* No further operations allowed, reset states */
FblMemInitStates();
}
#if defined( FBL_MEM_ENABLE_MULTI_SOURCE )
/***********************************************************************************************************************
* FblMemLockInputSource
**********************************************************************************************************************/
/*! \brief Lock input to specific source
* \param[in] sourceHandle Handle of input source
**********************************************************************************************************************/
void FblMemLockInputSource( tFblMemInputSource sourceHandle )
{
/* Verify source handle lies within range */
assertFblInternal(sourceHandle < FBL_MEM_SOURCE_COUNT, kFblMemAssertParameterOutOfRange);
gActiveSource = sourceHandle;
}
#endif /* FBL_MEM_ENABLE_MULTI_SOURCE */
/***********************************************************************************************************************
* FblMemGetActiveBuffer
**********************************************************************************************************************/
/*! \brief Return active input buffer, which can be used for current data reception
* \details Buffer pointer includes previously set offset to accommodate for alignment requirements
* \pre FblMemInitPowerOn executed before
* \return Pointer to active input buffer
**********************************************************************************************************************/
tFblMemRamData FblMemGetActiveBuffer( void )
{
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * inputJob;
/* Get pending job info */
inputJob = FblMemGetPendingInputJob();
/* Reset position to current offset */
inputJob->position = inputJob->offset;
/* Return pointer to active fill buffer */
return FblMemGetBuffer(inputJob);
}
/***********************************************************************************************************************
* FblMemBlockEraseIndication
**********************************************************************************************************************/
/*! \brief Indicate start of a new block (just before first segment)
* \pre FblMemInitPowerOn executed before
* \param[in] block Pointer to block information structure
* Only address and length members have to be initialized
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
tFblMemStatus FblMemBlockEraseIndication( const V_MEMRAM1 tFblMemBlockInfo V_MEMRAM2 V_MEMRAM3 * block )
{
tFblMemStatus retVal;
/* Check allowed states */
if (kFblOk != FblMemCheckAllowed(FBL_MEM_ALLOWED_BLOCK_ERASE, FBL_MEM_ALLOWED_NONE))
{
FBL_MEM_SET_STATUS(BlockEraseSequence, retVal);
}
else
{
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/* Setup erase progress */
FblMemSetupProgress(kFblMemProgressType_Erase, block->logicalAddress, 0u,
FBL_MEM_PROGRESS_INITIAL, FBL_MEM_PROGRESS_ERASE, block->targetLength);
/* Report initial progress */
FblMemUpdateProgress(block->targetLength);
/* Explicitely enable progress information for erase operation */
gProgressState = kFblMemProgressState_Enabled;
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
/* Perform erase */
retVal = FblMemEraseRegionInternal(block->targetAddress, block->targetLength);
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/* Conclude erase progress */
FblMemConcludeProgress();
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
}
if (kFblMemStatus_Ok == retVal)
{
/* Allow block start / erase indication */
FblMemSetAllowed(FBL_MEM_ALLOWED_BLOCK_START | FBL_MEM_ALLOWED_BLOCK_ERASE);
}
return retVal;
}
/***********************************************************************************************************************
* FblMemBlockStartIndication
**********************************************************************************************************************/
/*! \brief Indicate start of a new block (just before first segment)
* \details Block may consist of one or more segments
* \pre FblMemInitPowerOn executed before
* FblMemInit executed before in case previous cycle failed
* \param[in] block Pointer to block information structure
* Required members depending on configuration
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
/* PRQA S 6080 1 */ /* MD_MSR_STMIF */
tFblMemStatus FblMemBlockStartIndication( V_MEMRAM1 tFblMemBlockInfo V_MEMRAM2 V_MEMRAM3 * block ) /* PRQA S 3673 */ /* MD_FblMem_3673 */
{
tFblMemStatus retVal;
#if defined( V_ENABLE_USE_DUMMY_STATEMENT )
/* Parameters not used: avoid compiler warning */
# if defined( FBL_MEM_ENABLE_GLOBAL_BLOCK_INFO )
# else
(void)block;
# endif /* FBL_MEM_ENABLE_GLOBAL_BLOCK_INFO */
#endif /* V_ENABLE_USE_DUMMY_STATEMENT */
retVal = kFblMemStatus_Ok;
/* Check allowed states */
if (kFblOk != FblMemCheckAllowed(FBL_MEM_ALLOWED_BLOCK_START, FBL_MEM_ALLOWED_BLOCK_START))
{
FBL_MEM_SET_STATUS(BlockStartSequence, retVal);
}
else
{
#if defined( FBL_ENABLE_SYSTEM_CHECK ) && \
defined( FBL_MEM_ENABLE_VERIFICATION )
/* At least one of the configured verification variants should be active */
# if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
if (FBL_MEM_VERIFY_FCT_INPUT_NULL != block->verifyRoutinePipe.function)
{
}
else
# endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
# if defined( FBL_MEM_ENABLE_VERIFY_OUTPUT )
if (FBL_MEM_VERIFY_FCT_OUTPUT_NULL != block->verifyRoutineOutput.function)
{
}
else
# endif /* FBL_MEM_ENABLE_VERIFY_OUTPUT */
{
FBL_MEM_SET_STATUS(BlockStartParam, retVal);
}
#endif /* FBL_ENABLE_SYSTEM_CHECK && FBL_MEM_ENABLE_VERIFICATION */
}
#if defined( FBL_ENABLE_FUNC_VIRTUAL_MEMORY )
/* PRQA S 3415 1 */ /* MD_FblMem_3415 */
if ((kFblMemStatus_Ok == retVal) && (IO_E_OK == MemDriver_IsVirtualMem(block->targetAddress)))
{
if (IO_E_OK != MemDriver_RVirtualMemInitSync(block->targetAddress))
{
FBL_MEM_SET_STATUS(BlockStartSequence, retVal);
}
}
#endif /* FBL_ENABLE_FUNC_VIRTUAL_MEMORY */
if (kFblMemStatus_Ok == retVal)
{
/* Store block information */
#if defined( FBL_MEM_ENABLE_GLOBAL_BLOCK_INFO )
gBlockInfo = *block;
#endif /* FBL_MEM_ENABLE_GLOBAL_BLOCK_INFO */
#if defined( FBL_MEM_ENABLE_SEGMENT_HANDLING )
/* Reset segment list */
gBlockInfo.segmentList->nrOfSegments = 0u;
#endif /* FBL_MEM_ENABLE_SEGMENT_HANDLING */
/* Setup index of first segment */
gSegInfo.nextIndex = 0u;
/* Allow segment start indication */
FblMemSetAllowed(FBL_MEM_ALLOWED_SEGMENT_START);
}
return retVal;
}
/***********************************************************************************************************************
* FblMemBlockEndIndication
**********************************************************************************************************************/
/*! \brief Indicate end of current block (after last segment)
* \details Finalize verification if configured
* \pre FblMemSegmentEndIndication executed before
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
/* PRQA S 6050, 6080 1 */ /* MD_MSR_STCAL, MD_MSR_STMIF */
tFblMemStatus FblMemBlockEndIndication( void )
{
tFblMemStatus retVal;
#if defined( FBL_MEM_ENABLE_GAP_FILL )
tFblAddress baseAddress;
tFblLength baseLength;
#endif /* FBL_MEM_ENABLE_GAP_FILL */
retVal = kFblMemStatus_Ok;
/* Check allowed states */
if (kFblOk != FblMemCheckAllowed(FBL_MEM_ALLOWED_BLOCK_END, FBL_MEM_ALLOWED_BLOCK_END))
{
FBL_MEM_SET_STATUS(BlockEndSequence, retVal);
}
else
{
#if defined( FBL_MEM_ENABLE_GAP_FILL )
/* No gap fill required for volatile memory */
if (kFblMemType_RAM != gSegInfo.input.type)
{
/* Address range information of last segment */
baseAddress = gBlockInfo.segmentList->segmentInfo[gSegInfo.ownIndex].targetAddress;
baseLength = gBlockInfo.segmentList->segmentInfo[gSegInfo.ownIndex].length;
/* Include padding previously applied to end of last segment */
baseLength += FblMemPadLength(baseAddress, baseLength);
/* Length till end of block
Subtract offset of segment start and length of segment (including padding) from block length
to prevent range overflow */
gGapFillJob.used = (gBlockInfo.targetLength - (baseAddress - gBlockInfo.targetAddress)) - baseLength;
/* Gap fill not necessary when segment ends at last block address */
if (gGapFillJob.used > 0u)
{
/* Setup concluding gap fill from end of last segment to end of block */
gGapFillJob.position = 0u;
/* Gap fill starts after end of last segment */
gGapFillJob.baseAddress = baseAddress + baseLength;
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/* Setup gap fill progress */
FblMemSetupProgress(kFblMemProgressType_GapFill, gBlockInfo.logicalAddress, gSegInfo.nextIndex,
FBL_MEM_PROGRESS_INITIAL + FBL_MEM_PROGRESS_ERASE, FBL_MEM_PROGRESS_PROGRAM, gGapFillJob.used);
/* Adjust total percentages */
FblMemOffsetProgress((gGapFillJob.baseAddress - gBlockInfo.targetAddress), gBlockInfo.targetLength);
/* Report initial progress */
FblMemUpdateProgress(gGapFillJob.used);
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
/* Trigger concluding gap fill */
(void)FblMemQueueDefaultPrioInsert(gProcessingQueue, &gGapFillJob, gSegInfo.ownIndex);
/* Continue operation */
fblMemProgState = kFblMemProgState_Pending;
/* Finish processing of all pending buffers */
FblMemFlushQueueByPrio((tFblMemQueuePrio)kFblMemJobPrio_Write);
}
}
#endif /* FBL_MEM_ENABLE_GAP_FILL */
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/* Conclude any pending progress (programming or gap fill) */
FblMemConcludeProgress();
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/* Null pointer passed as verification function disables functionality */
if (FBL_MEM_VERIFY_FCT_INPUT_NULL != gBlockInfo.verifyRoutinePipe.function)
{
/* No additional segments for this block, trigger finalization of pipelined verification
Add to processing queue as other jobs may still be pending */
(void)FblMemQueueDefaultPrioInsert(gProcessingQueue, &gVerifyPipeFinalizeJob, gSegInfo.ownIndex);
/* Continue operation */
fblMemProgState = kFblMemProgState_Pending;
}
else
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
{
/* Concluding else */
}
}
if (kFblMemStatus_Ok == retVal)
{
/* Allow block verify */
FblMemSetAllowed(FBL_MEM_ALLOWED_BLOCK_VERIFY);
}
return retVal;
}
/***********************************************************************************************************************
* FblMemBlockVerify
**********************************************************************************************************************/
/*! \brief Finalize block verification by calling extended API with options to abort
* verification in case of failure
* \details Hint: also call function if verification not configured
* \pre FblMemBlockEndIndication executed before
* \param[in] verifyData Pointer to verification structure
* Required members dependent on configuration
* \param[out] verifyResult Pointer to extended verification result
* Null pointer if no extended result required
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
tFblMemStatus FblMemBlockVerify( const V_MEMRAM1 tFblMemBlockVerifyData V_MEMRAM2 V_MEMRAM3 * verifyData,
V_MEMRAM1 tFblMemVerifyStatus V_MEMRAM2 V_MEMRAM3 * verifyResult )
{
return FblMemBlockVerifyExtended(verifyData, verifyResult, 0u);
}
/***********************************************************************************************************************
* FblMemBlockVerifyExtended
**********************************************************************************************************************/
/*! \brief Finalize block verification
* \details Hint: also call function if verification not configured
* \pre FblMemBlockEndIndication executed before
* \param[in] verifyData Pointer to verification structure
* Required members dependent on configuration
* \param[in] option Indication which verification routines shall always be performed,
regardless of previous result.
0 if verification shall be aborted as soon as a step fails (default)
* \param[out] verifyResult Pointer to extended verification result
* Null pointer if no extended result required
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
/* PRQA S 6010, 6030, 6080 1 */ /* MD_MSR_STPTH, MD_MSR_STCYC, MD_MSR_STMIF */
tFblMemStatus FblMemBlockVerifyExtended( const V_MEMRAM1 tFblMemBlockVerifyData V_MEMRAM2 V_MEMRAM3 * verifyData,
V_MEMRAM1 tFblMemVerifyStatus V_MEMRAM2 V_MEMRAM3 * verifyResult, const tFblMemVerifyOption option )
{
tFblMemStatus retVal;
tFblMemVerifyStatus localResult;
#if defined( FBL_MEM_ENABLE_VERIFY_OUTPUT )
tFblMemVerifyStatus verifyOutputResult;
# if defined( FBL_MEM_ENABLE_VERIFY_OUTPUT_FULL_BLOCK_LENGTH )
# else
const V_MEMRAM1 tFblMemSegmentListEntry V_MEMRAM2 V_MEMRAM3 * lastSegment;
# endif /* FBL_MEM_ENABLE_VERIFY_OUTPUT_FULL_BLOCK_LENGTH */
#endif /* FBL_MEM_ENABLE_VERIFY_OUTPUT */
#if defined( V_ENABLE_USE_DUMMY_STATEMENT )
/* Parameters not used: avoid compiler warning */
# if !defined( FBL_MEM_ENABLE_VERIFICATION )
(void)option;
(void)verifyData;
# endif /* !FBL_MEM_ENABLE_VERIFICATION */
#endif /* V_ENABLE_USE_DUMMY_STATEMENT */
retVal = kFblMemStatus_Ok;
localResult = FBL_MEM_VERIFY_OK;
/* Check allowed states */
if (kFblOk != FblMemCheckAllowed(FBL_MEM_ALLOWED_BLOCK_VERIFY, FBL_MEM_ALLOWED_NONE))
{
FBL_MEM_SET_STATUS(BlockVerifySequence, retVal);
}
else
{
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
/* Finish processing of all pending verification jobs */
FblMemFlushQueueByPrio((tFblMemQueuePrio)kFblMemJobPrio_Lowest);
/* Check for error condition */
if (kFblMemProgState_Error == fblMemProgState)
{
/* Report error set by programming routines (previous or current) */
retVal = gErrorStatus;
}
else
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
{
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED )
if ((retVal == kFblMemStatus_Ok) || FBL_MEM_CHK_OPTION(option, FBL_MEM_VERIFY_PIPE_OPT)) /* PRQA S 2991, 2995 */ /* MD_FblMem_2991_2995 */
{
/* Pipelined verification is used
Finalize by comparing with the transferred signature */
if (kFblMemStatus_Ok != FblMemVerifyInput( &gBlockInfo.verifyRoutinePipe, &verifyData->verifyDataPipe,
FBL_MEM_VERIFY_STATE_VERIFY, &localResult ))
{
FBL_MEM_SET_STATUS(BlockVerifyPipeVerify, retVal);
}
}
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED */
#if defined( FBL_MEM_ENABLE_VERIFY_OUTPUT )
if ((retVal == kFblMemStatus_Ok) || FBL_MEM_CHK_OPTION(option, FBL_MEM_VERIFY_OUTPUT_OPT)) /* PRQA S 2991, 2995 */ /* MD_FblMem_2991_2995 */
{
/* Null pointer passed as verification function disables functionality */
if (FBL_MEM_VERIFY_FCT_OUTPUT_NULL != gBlockInfo.verifyRoutineOutput.function)
{
/* Initialize verification parameter structure */
# if defined( FBL_MEM_ENABLE_EXT_TRIGGER_OUTPUT_VERIFY )
gBlockInfo.verifyRoutineOutput.param->wdTriggerFct = (tFblMemVerifyWdFct)&FblMemTriggerWatchdogExt; /* PRQA S 0313 */ /* MD_FblMem_0313 */
# else
gBlockInfo.verifyRoutineOutput.param->wdTriggerFct = (tFblMemVerifyWdFct)&FblMemTriggerWatchdog;
# endif /* FBL_MEM_ENABLE_EXT_TRIGGER_OUTPUT_VERIFY */
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/* Setup erase progress */
FblMemSetupProgress(kFblMemProgressType_Verify, gBlockInfo.logicalAddress, 0u,
FBL_MEM_PROGRESS_INITIAL + FBL_MEM_PROGRESS_ERASE + FBL_MEM_PROGRESS_PROGRAM, FBL_MEM_PROGRESS_VERIFY,
gBlockInfo.targetLength);
/* Report initial progress */
FblMemUpdateProgress(gBlockInfo.targetLength);
/* Overwrite read function to keep track of progress */
gBlockInfo.verifyRoutineOutput.param->readMemory = &FblMemProgressRead;
#else
gBlockInfo.verifyRoutineOutput.param->readMemory = gBlockInfo.readFct;
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
gBlockInfo.verifyRoutineOutput.param->verificationData = verifyData->verifyDataOutput.data;
gBlockInfo.verifyRoutineOutput.param->blockStartAddress = gBlockInfo.targetAddress;
# if defined( FBL_MEM_ENABLE_VERIFY_OUTPUT_FULL_BLOCK_LENGTH )
gBlockInfo.verifyRoutineOutput.param->blockLength = gBlockInfo.targetLength;
# else
/* Total length covered by all programmed segments (from block start) */
lastSegment = &(gBlockInfo.segmentList->segmentInfo[gBlockInfo.segmentList->nrOfSegments - 1u]);
/* Segments have to be stored in increasing address order */
gBlockInfo.verifyRoutineOutput.param->blockLength = (lastSegment->targetAddress - gBlockInfo.targetAddress)
+ lastSegment->length;
# endif /* FBL_MEM_ENABLE_VERIFY_OUTPUT_FULL_BLOCK_LENGTH */
/* Call verification function */
verifyOutputResult = gBlockInfo.verifyRoutineOutput.function(gBlockInfo.verifyRoutineOutput.param);
localResult |= verifyOutputResult; /* PRQA S 2986 */ /* MD_FblMem_2986 */
if (FBL_MEM_VERIFY_OK != verifyOutputResult)
{
FBL_MEM_SET_STATUS(BlockVerifyOutputVerify, retVal);
}
}
}
#else
{
/* Concluding else */
}
#endif /* FBL_MEM_ENABLE_VERIFY_OUTPUT */
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/* Conclude pending verification progress */
FblMemConcludeProgress();
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
}
}
if (kFblMemStatus_Ok == retVal)
{
/* Allow new block start / erase indication */
FblMemAddAllowed(FBL_MEM_ALLOWED_BLOCK_START | FBL_MEM_ALLOWED_BLOCK_ERASE);
}
if (FBL_MEM_VERIFY_STATUS_NULL != verifyResult)
{
/* Pass extended result */
*verifyResult = localResult;
}
return retVal;
}
/***********************************************************************************************************************
* FblMemSegmentStartIndication
**********************************************************************************************************************/
/*! \brief Indicate start of a new segment
* \details Segment may consist of one or more chunks of input data programmed into a contiguous memory range
* Update verification with logical address and length if configured
* Initialize data processing if configured
* \pre FblMemBlockStartIndication executed before
* \param[in] segment Pointer to segment information
* Target address (program operation)
* Target length (program operation)
* either unprocessed data length actually written to memory or processed data length of input
* data
* Logical address (verification operation)
* Logical length (verification operation)
* Type (RAM / ROM)
* Data format identifier (data processing)
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
/* PRQA S 6010, 6030, 6050, 6080 1 */ /* MD_MSR_STMIF, MD_MSR_STPTH, MD_MSR_STCYC, MD_MSR_STCAL */
tFblMemStatus FblMemSegmentStartIndication( const V_MEMRAM1 tFblMemSegmentInfo V_MEMRAM2 V_MEMRAM3 * segment )
{
tFblMemStatus retVal;
#if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED ) && \
defined( FBL_MEM_ENABLE_VERIFY_PIPELINED_ADDRESS_LENGTH )
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * verifyJob;
#endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED && FBL_MEM_ENABLE_VERIFY_PIPELINED_ADDRESS_LENGTH */
#if defined( FBL_MEM_ENABLE_GAP_FILL )
tFblAddress baseAddress;
tFblLength baseLength;
#endif /* FBL_MEM_ENABLE_GAP_FILL */
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
tFblAddress progressAddress;
tFblLength progressRemainder;
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
retVal = kFblMemStatus_Ok;
#if defined( FBL_ENABLE_DATA_PROCESSING ) || \
defined( FBL_MEM_ENABLE_STREAM_OUTPUT ) || \
defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/* Remember input buffer type: write through */
gSegInfo.jobType = kFblMemJobType_InputWrite;
#endif /* FBL_ENABLE_DATA_PROCESSING || FBL_MEM_ENABLE_STREAM_OUTPUT || FBL_MEM_ENABLE_PASSTHROUGH || FBL_MEM_ENABLE_REMAINDER_HANDLING */
/* Check allowed states */
if (kFblOk != FblMemCheckAllowed(FBL_MEM_ALLOWED_SEGMENT_START, FBL_MEM_ALLOWED_SEGMENT_START))
{
FBL_MEM_SET_STATUS(SegmentStartSequence, retVal);
}
#if defined( FBL_MEM_ENABLE_SEGMENT_HANDLING )
else if (gBlockInfo.segmentList->nrOfSegments >= gBlockInfo.maxSegments)
{
/* Maximum number of segment list entries exceeded */
FBL_MEM_SET_STATUS(SegmentStartSegmentCount, retVal);
}
#endif /* FBL_MEM_ENABLE_SEGMENT_HANDLING */
else
{
#if defined( FBL_MEM_ENABLE_VERIFY_STREAM )
/* Initialize verification on first segment */
if (0u == gSegInfo.nextIndex)
{
/* Initialize the calculation */
if (kFblMemStatus_Ok != FblMemInitVerifyInput())
{
/* Overwrites previous error code */
FBL_MEM_SET_STATUS(SegmentStartVerifyInit, retVal);
}
}
if (kFblMemStatus_Ok == retVal)
#endif /* FBL_MEM_ENABLE_VERIFY_STREAM */
#if defined( FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH )
{
{
# if defined( FBL_MEM_ENABLE_VERIFY_PIPELINED ) && \
defined( FBL_MEM_ENABLE_VERIFY_PIPELINED_ADDRESS_LENGTH )
# if defined( __ApplFblMemVerifyPipelinedIsAddressAndLengthIncluded )
/* Skip update of signature information? */
if (kFblOk == __ApplFblMemVerifyPipelinedIsAddressAndLengthIncluded(&gBlockInfo, segment))
# endif
{
/* Get first free job
At least one (additional) job should be free at this point as the previous data indication(s)
unblocked the pipelined verification queue */
verifyJob = FblMemPrepareVerifyPipeJob(gSegInfo.nextIndex, segment->targetAddress);
/* Check for null pointer */
if (FBL_MEM_JOB_NULL != verifyJob)
{
/* Switch job type to include segment information */
verifyJob->type = kFblMemJobType_VerifyPipeInfo;
verifyJob->used = FBL_MEM_VERIFY_ADDRESS_LENGTH_BUFFER_SIZE;
verifyJob->netSize = FBL_MEM_VERIFY_ADDRESS_LENGTH_BUFFER_SIZE;
verifyJob->totalSize = FBL_MEM_VERIFY_ADDRESS_LENGTH_BUFFER_SIZE;
/* Do not remove verification job as it is reused for the read-back */
verifyJob->completion = kFblMemOperationMode_Finalize;
FblMemSetInteger(FBL_MEM_VERIFY_ADDRESS_LENGTH_SIZE, segment->logicalAddress, &verifyJob->buffer[0u]);
FblMemSetInteger(FBL_MEM_VERIFY_ADDRESS_LENGTH_SIZE, segment->logicalLength, &verifyJob->buffer[FBL_MEM_VERIFY_ADDRESS_LENGTH_SIZE]);
}
}
# endif /* FBL_MEM_ENABLE_VERIFY_PIPELINED && FBL_MEM_ENABLE_VERIFY_PIPELINED_ADDRESS_LENGTH */
}
}
if (kFblMemStatus_Ok == retVal)
#endif /* FBL_MEM_ENABLE_VERIFY_ADDRESS_LENGTH */
{
/* Check whether data processing is requested */
if (kFblOk == __ApplFblMemIsDataProcessingRequired(segment->dataFormat))
{
#if defined( FBL_ENABLE_DATA_PROCESSING )
/* Remember input buffer type: data processing */
gSegInfo.jobType = kFblMemJobType_ProcInput;
/* Initialize user specific processing of received data */
gProcParam.mode = segment->dataFormat;
/* Check result */
if (kFblOk != ApplFblInitDataProcessing(&gProcParam))
{
FBL_MEM_SET_STATUS(SegmentStartDataProcInit, retVal);
}
#else
/* Data processing not supported, refuse indication */
FBL_MEM_SET_STATUS(SegmentStartDataProcInit, retVal);
#endif /* FBL_ENABLE_DATA_PROCESSING */
}
}
#if defined( __ApplFblMemIsStreamOutputRequired )
if (kFblMemStatus_Ok == retVal)
{
/* Check whether stream output is requested */
if (kFblOk == __ApplFblMemIsStreamOutputRequired(segment->dataFormat))
{
# if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
# if defined( FBL_ENABLE_DATA_PROCESSING )
if (kFblOk == __ApplFblMemIsDataProcessingRequired(segment->dataFormat))
{
/* Input buffer type (data processing) already set */
}
else
# endif /* FBL_ENABLE_DATA_PROCESSING */
{
/* Remember input buffer type: stream output */
gSegInfo.jobType = kFblMemJobType_StreamInput;
}
/* Initialize user specific stream output of received data */
gStreamParam.mode = segment->dataFormat;
gStreamParam.baseAddress = segment->targetAddress;
gStreamParam.baseLength = gBlockInfo.targetLength - (segment->targetAddress - gBlockInfo.targetAddress);
/* Check result */
if (kFblOk != ApplFblInitStreamOutput(&gStreamParam))
{
FBL_MEM_SET_STATUS(SegmentStartStreamOutInit, retVal);
}
# else
/* Stream output not supported, refuse indication */
FBL_MEM_SET_STATUS(SegmentStartStreamOutInit, retVal);
# endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
}
}
#endif /* __ApplFblMemIsStreamOutputRequired */
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/* Progress information for segment (without gap fill) */
progressAddress = segment->targetAddress;
progressRemainder = segment->targetLength;
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
#if defined( FBL_MEM_ENABLE_GAP_FILL )
if (kFblMemStatus_Ok == retVal)
{
/* No gap fill required for volatile memory */
if (kFblMemType_RAM != segment->type)
{
/* First segment of block? */
if (0u == gSegInfo.nextIndex)
{
/* Fill from beginning of block */
baseAddress = gBlockInfo.targetAddress;
baseLength = 0u;
}
else
{
/* Fill from end of previous segment */
baseAddress = gBlockInfo.segmentList->segmentInfo[gSegInfo.ownIndex].targetAddress;
baseLength = gBlockInfo.segmentList->segmentInfo[gSegInfo.ownIndex].length;
}
/* Include padding applied to end of previous segment */
baseLength += FblMemPadLength(baseAddress, baseLength);
/* Segments in ascending order? */
assertFblInternal((segment->targetAddress >= baseAddress), kFblMemAssertParameterOutOfRange);
assertFblInternal(((segment->targetAddress - baseAddress) >= baseLength), kFblMemAssertParameterOutOfRange);
/* Length till start of current segment
Subtract length of previous segment (including padding) from offset between segments
to prevent range overflow */
gGapFillJob.used = (segment->targetAddress - baseAddress) - baseLength;
/* Gap fill not necessary when segment starts directly after previous segment */
if (gGapFillJob.used > 0u)
{
/* Setup gap fill */
gGapFillJob.position = 0u;
/* Gap fill starts after end of previous segment */
gGapFillJob.baseAddress = baseAddress + baseLength;
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/* Include gap fill range in progress information */
progressAddress = gGapFillJob.baseAddress;
progressRemainder += gGapFillJob.used;
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
/* Trigger gap fill */
(void)FblMemQueueDefaultPrioInsert(gProcessingQueue, &gGapFillJob, gSegInfo.nextIndex);
# if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
/* Process operation in task */
fblMemProgState = kFblMemProgState_Pending;
# endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
}
}
}
#endif /* FBL_MEM_ENABLE_GAP_FILL */
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/* Setup programming progress (may include gap fill) */
FblMemSetupProgress(kFblMemProgressType_Program, gBlockInfo.logicalAddress, gSegInfo.nextIndex,
FBL_MEM_PROGRESS_INITIAL + FBL_MEM_PROGRESS_ERASE, FBL_MEM_PROGRESS_PROGRAM, progressRemainder);
/* Adjust total percentages */
FblMemOffsetProgress((progressAddress - gBlockInfo.targetAddress), gBlockInfo.targetLength);
/* Report initial progress */
FblMemUpdateProgress(progressRemainder);
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
}
if (kFblMemStatus_Ok == retVal)
{
/* Copy input parameters to local variable */
gSegInfo.input = *segment;
/* Initialize internal attributes */
gSegInfo.writeAddress = segment->targetAddress;
gSegInfo.writeRemainder = 0u;
#if defined( FBL_ENABLE_PROCESSED_DATA_LENGTH )
/* Available length from start of segment to end of block */
gSegInfo.writeLength = gBlockInfo.targetLength - (segment->targetAddress - gBlockInfo.targetAddress);
gSegInfo.writtenLength = 0u;
#else
gSegInfo.writeLength = segment->targetLength;
#endif /* FBL_ENABLE_PROCESSED_DATA_LENGTH */
#if defined( FBL_ENABLE_UNALIGNED_DATA_TRANSFER )
/* Sliding input address required for remainder calculation */
gSegInfo.inputAddress = segment->targetAddress;
#endif /* FBL_ENABLE_UNALIGNED_DATA_TRANSFER */
#if defined( FBL_MEM_ENABLE_INPUT_LENGTH )
/* Running input length required for early overflow detection */
gSegInfo.inputLength = segment->targetLength;
#endif /* FBL_MEM_ENABLE_INPUT_LENGTH */
gSegInfo.ownIndex = gSegInfo.nextIndex;
gSegInfo.nextIndex++;
/* Allow data indication */
FblMemSetAllowed(FBL_MEM_ALLOWED_DATA_IND);
}
return retVal;
}
/***********************************************************************************************************************
* FblMemSegmentEndIndication
**********************************************************************************************************************/
/*! \brief Indicate end of current segment
* \details Finish processing of all pending buffers, finalize data
* processing and program any remainder
* \pre FblMemDataIndication executed before
* \param[out] writeLength Pointer to length actually written
* \return Result of operation (potentially remapped to OEM specific NRC)
* May return error generated by background task operation
**********************************************************************************************************************/
/* PRQA S 6010, 6030, 6050 1 */ /* MD_MSR_STMIF, MD_MSR_STPTH, MD_MSR_STCAL */
tFblMemStatus FblMemSegmentEndIndication( V_MEMRAM1 tFblLength V_MEMRAM2 V_MEMRAM3 * writeLength )
{
tFblMemStatus retVal;
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING_SINGLE_JOB ) || \
defined( FBL_ENABLE_UNALIGNED_DATA_TRANSFER ) || \
defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER )
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * activeJob;
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING_SINGLE_JOB || FBL_ENABLE_UNALIGNED_DATA_TRANSFER || FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER */
#if defined( FBL_MEM_ENABLE_SEGMENT_HANDLING )
V_MEMRAM1 tFblMemSegmentListEntry V_MEMRAM2 V_MEMRAM3 * activeSegment;
#endif /* FBL_MEM_ENABLE_SEGMENT_HANDLING */
retVal = kFblMemStatus_Ok;
/* Check allowed states */
if (kFblOk != FblMemCheckAllowed(FBL_MEM_ALLOWED_SEGMENT_END, FBL_MEM_ALLOWED_DATA_IND | FBL_MEM_ALLOWED_SEGMENT_END))
{
FBL_MEM_SET_STATUS(SegmentEndSequence, retVal);
}
else
{
#if defined( FBL_MEM_ENABLE_PREAMBLE_HANDLING )
/* Active fill buffer may change
Store preamble so operation is transparent for caller */
FblMemStorePreamble();
#endif /* FBL_MEM_ENABLE_PREAMBLE_HANDLING */
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING_SINGLE_JOB )
/* Active input job may be used as finalization trigger (flush remainder) */
activeJob = FblMemGetPendingInputJob();
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING_SINGLE_JOB */
{
#if defined( FBL_ENABLE_DATA_PROCESSING ) || \
defined( FBL_MEM_ENABLE_STREAM_OUTPUT ) || \
defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
if (kFblMemProgState_Error != fblMemProgState)
{
# if defined( FBL_ENABLE_DATA_PROCESSING )
/* Data processing active? */
if (kFblOk == __ApplFblMemIsDataProcessingRequired(gSegInfo.input.dataFormat))
{
/* Do not remove finalize job until no data is produced anymore */
gProcFinalizeJob.completion = kFblMemOperationMode_Finalize;
/* No additional data in this segment, trigger finalization of data processing
Add to queue as some jobs may still be pending */
(void)FblMemQueueDefaultPrioInsert(gProcessingQueue, &gProcFinalizeJob, gSegInfo.ownIndex);
}
# endif /* FBL_ENABLE_DATA_PROCESSING */
# if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
/* Stream output active? */
if (kFblOk == __ApplFblMemIsStreamOutputRequired(gSegInfo.input.dataFormat))
{
/* Do not remove finalize job until no data is produced anymore */
gStreamFinalizeJob.completion = kFblMemOperationMode_Finalize;
/* No additional data in this segment, trigger finalization of stream output
Add to queue as some jobs may still be pending */
(void)FblMemQueueDefaultPrioInsert(gProcessingQueue, &gStreamFinalizeJob, gSegInfo.ownIndex);
}
/* Skip write finalization */
else
# endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
{
# if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
# if defined( FBL_MEM_ENABLE_PROC_QUEUE )
/* Insert dummy info into queue to trigger finalization of write */
(void)FblMemQueueDefaultPrioInsert(gProcessingQueue, &gWriteFinalizeJob, gSegInfo.ownIndex);
# else
/* Enable switch combination equals FBL_MEM_ENABLE_REMAINDER_HANDLING_SINGLE_JOB */
/* Re-use active input job as finalization trigger (flush remainder)
Change job type, all other members already have a consistent value */
activeJob->type = kFblMemJobType_WriteFinalize;
# endif /* FBL_MEM_ENABLE_PROC_QUEUE */
# endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING */
}
# if defined( FBL_MEM_ENABLE_PROC_QUEUE )
if (!FblMemQueueIsEmpty(gProcessingQueue))
# endif /* FBL_MEM_ENABLE_PROC_QUEUE */
{
/* Continue operation */
fblMemProgState = kFblMemProgState_Pending;
}
}
#endif /* FBL_ENABLE_DATA_PROCESSING || FBL_MEM_ENABLE_STREAM_OUTPUT || FBL_MEM_ENABLE_REMAINDER_HANDLING */
}
#if defined( FBL_MEM_ENABLE_PROC_QUEUE ) || \
defined( FBL_MEM_ENABLE_REMAINDER_HANDLING )
/* Finish processing of all pending buffers */
FblMemFlushQueueByPrio((tFblMemQueuePrio)kFblMemJobPrio_Write);
/* Check for error condition */
if (kFblMemProgState_Error == fblMemProgState)
{
/* Report error set by programming routines (previous or current) */
retVal = gErrorStatus;
}
else
#endif /* FBL_MEM_ENABLE_PROC_QUEUE || FBL_MEM_ENABLE_REMAINDER_HANDLING */
{
#if defined( FBL_MEM_ENABLE_REMAINDER_HANDLING_SINGLE_JOB )
/* Remainder handling finished, restore original job type */
activeJob->type = kFblMemJobType_InputWrite;
#endif /* FBL_MEM_ENABLE_REMAINDER_HANDLING_SINGLE_JOB */
#if defined( FBL_ENABLE_PROCESSED_DATA_LENGTH )
/* Requested data should be completely provided at this point */
if (0u != gSegInfo.inputLength)
#else
/* Requested data should be completely programmed at this point */
if (0u != gSegInfo.writeLength)
#endif /* FBL_ENABLE_PROCESSED_DATA_LENGTH */
{
FBL_MEM_SET_STATUS(SegmentEndInsufficientData, retVal);
}
#if defined( __ApplFblMemPostSegmentEnd )
else
{
/* Perform actions directly after segment end */
if (kFblOk != __ApplFblMemPostSegmentEnd())
{
FBL_MEM_SET_STATUS(SegmentEndPost, retVal);
}
}
#endif /* __ApplFblMemPostSegmentEnd */
/* Return written length */
#if defined( FBL_ENABLE_PROCESSED_DATA_LENGTH )
*writeLength = gSegInfo.writtenLength;
#else
*writeLength = gSegInfo.input.targetLength - gSegInfo.writeLength;
#endif /* FBL_ENABLE_PROCESSED_DATA_LENGTH */
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
FblMemConcludeProgress();
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
}
#if defined( FBL_MEM_ENABLE_SEGMENT_HANDLING )
/* Add segment to list */
if (kFblMemStatus_Ok == retVal)
{
activeSegment = &(gBlockInfo.segmentList->segmentInfo[gBlockInfo.segmentList->nrOfSegments]);
activeSegment->targetAddress = gSegInfo.input.targetAddress;
activeSegment->transferredAddress = gSegInfo.input.logicalAddress;
activeSegment->length = *writeLength;
gBlockInfo.segmentList->nrOfSegments++;
}
#endif /* FBL_MEM_ENABLE_SEGMENT_HANDLING */
#if defined( FBL_ENABLE_UNALIGNED_DATA_TRANSFER ) || \
defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER )
/* Restore default offset to align actual data to platform requirements */
activeJob = FblMemGetPendingInputJob();
activeJob->offset = FBL_MEM_PREAMBLE_OFFSET(FBL_MEM_PREAMBLE_LENGTH);
#endif /* FBL_ENABLE_UNALIGNED_DATA_TRANSFER || FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER */
#if defined( FBL_MEM_ENABLE_PREAMBLE_HANDLING )
/*
Active fill buffer may have changed
Restore previously stored preamble so operation is transparent for caller
*/
FblMemRestorePreamble();
#endif /* FBL_MEM_ENABLE_PREAMBLE_HANDLING */
}
/* Check result of final programming */
if (kFblMemStatus_Ok == retVal)
{
/* Allow additional segment start or block end indication */
FblMemSetAllowed(FBL_MEM_ALLOWED_SEGMENT_START | FBL_MEM_ALLOWED_BLOCK_END);
}
return retVal;
}
/***********************************************************************************************************************
* FblMemDataIndication
**********************************************************************************************************************/
/*! \brief Indicate new chunk of input data
* \details If pipelined programming is configured queue buffer and immediately return in case another input buffer
* is available
* Otherwise finish processing of one pending input buffer
* In non-pipelined mode indicated input buffer is directly processed
* \pre FblMemSegmentStartIndication executed before, provided buffer
* and data offset equal the parameters of active input buffer
* \param[in] buffer Pointer to input buffer (including preamble)
* \param[in] offset Offset of actual data (after preamble)
* \param[in] length Length of data (without preamble)
* \return Result of operation (potentially remapped to OEM specific NRC)
* May return error generated by background task operation
**********************************************************************************************************************/
/* PRQA S 6010, 6030, 6050, 6080 1 */ /* MD_MSR_STPTH, MD_MSR_STCYC, MD_MSR_STCAL, MD_MSR_STMIF */
tFblMemStatus FblMemDataIndication( tFblMemConstRamData buffer, tFblLength offset, tFblLength length )
{
tFblMemStatus retVal;
#if defined( FBL_ENABLE_UNALIGNED_DATA_TRANSFER ) || \
defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER )
V_MEMRAM1 tFblMemJob V_MEMRAM2 V_MEMRAM3 * activeJob;
#endif /* FBL_ENABLE_UNALIGNED_DATA_TRANSFER || FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER */
#if defined( FBL_MEM_ENABLE_INPUT_LENGTH )
tFblLength inputLength;
/* Do not update running input length unless data is unprocessed */
inputLength = 0u; /* PRQA S 2982 */ /* MD_FblMem_2982 */
#endif /* FBL_MEM_ENABLE_INPUT_LENGTH */
retVal = kFblMemStatus_Ok;
/* Check allowed states */
if (kFblOk != FblMemCheckAllowed(FBL_MEM_ALLOWED_DATA_IND, FBL_MEM_ALLOWED_DATA_IND | FBL_MEM_ALLOWED_SEGMENT_END))
{
FBL_MEM_SET_STATUS(DataIndSequence, retVal);
}
else
{
#if defined( FBL_ENABLE_SYSTEM_CHECK ) || \
defined( FBL_MEM_ENABLE_INPUT_LENGTH )
# if defined( FBL_ENABLE_PROCESSED_DATA_LENGTH )
/* Unconditionally check input length */
{
# else
# if defined( FBL_ENABLE_DATA_PROCESSING ) || \
defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
{
/*
Data processed in any way?
Unprocessed data directly written through
*/
# if defined( FBL_ENABLE_DATA_PROCESSING )
if (kFblOk == __ApplFblMemIsDataProcessingRequired(gSegInfo.input.dataFormat))
{
}
else
# endif /* FBL_ENABLE_DATA_PROCESSING */
# if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
if (kFblOk == __ApplFblMemIsStreamOutputRequired(gSegInfo.input.dataFormat))
{
}
else
# endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
# else
{
# endif /* FBL_ENABLE_DATA_PROCESSING ||FBL_MEM_ENABLE_STREAM_OUTPUT */
# endif /* FBL_ENABLE_PROCESSED_DATA_LENGTH */
{
/*
Verify newly provided data does not exceed length requested
in segment info
*/
# if defined( FBL_MEM_ENABLE_INPUT_LENGTH )
/* Unprocessed data: Update running input data length */
inputLength = length;
if (gSegInfo.inputLength < length)
# else
if (gSegInfo.writeLength < length)
# endif /* FBL_MEM_ENABLE_INPUT_LENGTH */
{
FBL_MEM_SET_STATUS(DataIndOverflow, retVal);
}
}
}
#endif /* FBL_ENABLE_SYSTEM_CHECK || FBL_ENABLE_PROCESSED_DATA_LENGTH */
}
if (kFblMemStatus_Ok == retVal)
{
#if defined( FBL_MEM_ENABLE_PREAMBLE_HANDLING )
/*
Active fill buffer may change
Store preamble so operation is transparent for caller
*/
FblMemStorePreamble();
#endif /* FBL_MEM_ENABLE_PREAMBLE_HANDLING */
/* Queue the active buffer for further processing */
retVal = FblMemQueueBuffer(buffer, offset, length);
if (kFblMemStatus_Ok == retVal)
{
#if defined( FBL_MEM_ENABLE_INPUT_LENGTH )
/* Keep track of expected input data length */
gSegInfo.inputLength -= inputLength;
#endif /* FBL_MEM_ENABLE_INPUT_LENGTH */
#if defined( FBL_MEM_ENABLE_INPUT_DATA_FLUSH )
# if defined( FBL_ENABLE_PIPELINED_PROGRAMMING )
if (kFblOk == __ApplFblMemIsPipelinedProgrammingDisabled(&gBlockInfo, &(gSegInfo.input)))
# endif /* FBL_ENABLE_PIPELINED_PROGRAMMING */
{
/* Directly process all input data */
FblMemFlushQueueByPrio((tFblMemQueuePrio)kFblMemJobPrio_Write);
}
/* Check for programming error */
if (kFblMemProgState_Error == fblMemProgState)
{
retVal = gErrorStatus;
}
else
#endif /* FBL_MEM_ENABLE_INPUT_DATA_FLUSH */
{
#if defined( FBL_ENABLE_UNALIGNED_DATA_TRANSFER ) || \
defined( FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER )
/* Restore default offset to align actual data to platform requirements */
activeJob = FblMemGetPendingInputJob();
activeJob->offset = FBL_MEM_PREAMBLE_OFFSET(FBL_MEM_PREAMBLE_LENGTH);
# if defined( FBL_ENABLE_UNALIGNED_DATA_TRANSFER )
/* Remainder handling neither required for volatile memory nor for processed data */
if (kFblMemType_RAM == gSegInfo.input.type)
{
}
else
# if defined( FBL_ENABLE_DATA_PROCESSING )
if (kFblOk == __ApplFblMemIsDataProcessingRequired(gSegInfo.input.dataFormat))
{
}
else
# endif /* FBL_ENABLE_DATA_PROCESSING */
# if defined( FBL_MEM_ENABLE_STREAM_OUTPUT )
if (kFblOk == __ApplFblMemIsStreamOutputRequired(gSegInfo.input.dataFormat))
{
}
else
# endif /* FBL_MEM_ENABLE_STREAM_OUTPUT */
{
/* Align active input buffer to current remainder */
activeJob->offset += FblMemGetWriteRemainder(gSegInfo.inputAddress, length);
/* Update sliding input address */
gSegInfo.inputAddress += length;
}
# endif /* FBL_ENABLE_UNALIGNED_DATA_TRANSFER */
#endif /* FBL_ENABLE_UNALIGNED_DATA_TRANSFER || FBL_MEM_ENABLE_SEGMENTED_INPUT_BUFFER */
}
}
#if defined( FBL_MEM_ENABLE_PREAMBLE_HANDLING )
/*
Active fill buffer may have changed
Restore previously stored preamble so operation is transparent for caller
*/
FblMemRestorePreamble();
#endif /* FBL_MEM_ENABLE_PREAMBLE_HANDLING */
}
if (kFblMemStatus_Ok == retVal)
{
#if defined( FBL_MEM_ENABLE_INPUT_LENGTH )
/* No more data is expected, all data has been processed within the last chunk of data */
if (gSegInfo.inputLength == 0u)
{
FblMemSetAllowed(FBL_MEM_ALLOWED_SEGMENT_END);
}
else
#endif /* FBL_MEM_ENABLE_INPUT_LENGTH */
{
/* Allow additional data or segment end indication */
FblMemSetAllowed(FBL_MEM_ALLOWED_DATA_IND | FBL_MEM_ALLOWED_SEGMENT_END);
}
}
return retVal;
}
/***********************************************************************************************************************
* FblMemTask
**********************************************************************************************************************/
/*! \brief Perform background tasks
* \details If pipelined programming is configured pending buffers are processed until the operation is suspended by
* a Rx notification
* \pre FblMemInitPowerOn executed before
**********************************************************************************************************************/
void FblMemTask( void )
{
#if defined( FBL_MEM_ENABLE_PIPELINING )
switch (fblMemProgState)
{
/* Operation pending */
case kFblMemProgState_Pending:
{
/* Switch to background context
* Affects watchdog and suspend handling */
gProgContext = kFblMemContext_Background;
/*
Loop while processing isn't paused by external event (e.g. data reception)
and buffers are pending
*/
while (kFblMemProgState_Pending == fblMemProgState)
{
/* Execute processing cycle */
FblMemProcessQueue(kFblMemOperationMode_Normal);
}
/* Suspend request received during processing */
if (kFblMemProgState_SuspendPending == fblMemProgState)
{
fblMemProgState = kFblMemProgState_Suspended;
}
/* Switch back (foreground) service context
* Affects watchdog and suspend handling */
gProgContext = kFblMemContext_Service;
break;
}
/* No operation pending */
case kFblMemProgState_Idle:
case kFblMemProgState_Error:
/* Operation suspended by external event */
case kFblMemProgState_Suspended:
case kFblMemProgState_SuspendPending:
/* Operation suspended by internal checkpoint event */
case kFblMemProgState_Checkpoint:
/* Unknown state value */
default:
{
/* Nothing to do */
break;
}
}
#endif /* FBL_MEM_ENABLE_PIPELINING */
}
/***********************************************************************************************************************
* FblMemFlushInputData
**********************************************************************************************************************/
/*! \brief Flush any pending input data
* \details Process and program any pending input data passed via DataIndication. Pipelined verification tasks
* may still be pending afterwards.
* \pre FblMemInitPowerOn executed before
**********************************************************************************************************************/
void FblMemFlushInputData( void )
{
/* Process all pending input data */
FblMemFlushQueueByPrio((tFblMemQueuePrio)kFblMemJobPrio_Write);
}
#define FBLLIB_MEM_STOP_SEC_CODE
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
# define FBLLIB_MEM_RAMCODE_START_SEC_CODE
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
/***********************************************************************************************************************
* FblMemResumeIndication
**********************************************************************************************************************/
/*! \brief Resume suspended operation
* \details If pipelined programming is configured a pending operation which was suspended earlier through a RX
* notification will be resumed
* \pre FblMemInitPowerOn executed before
**********************************************************************************************************************/
void FblMemResumeIndication( void )
{
#if defined( FBL_MEM_ENABLE_PIPELINING )
/* Resume any pending operations */
if (FblMemTaskIsPending())
{
/* Continue operation on next call cycle of task
Note: fblMemProgState may already be pending and isn't necessarily suspended */
fblMemProgState = kFblMemProgState_Pending;
}
#endif /* FBL_MEM_ENABLE_PIPELINING */
}
#if defined( FBL_MEM_ENABLE_PIPELINING )
/***********************************************************************************************************************
* FblMemRxNotification
**********************************************************************************************************************/
/*! \brief Suspend any pending programming operation
* \details If memory driver supports pausing of active operation call respective user callback too
* \pre FblMemInitPowerOn executed before
**********************************************************************************************************************/
void FblMemRxNotification( void )
{
/*
Notification may be executed in interrupt context
Critical section secures access to programming state
*/
__ApplFblMemEnterCriticalSection();
/*
Actions only necessary if programming is pending
Condition equals ((fblMemProgState == kFblMemProgState_Pending) || (fblMemProgState == kFblMemProgState_Checkpoint))
*/
if (fblMemProgState >= kFblMemProgState_Checkpoint)
{
/* Suspend programming to handle received request */
fblMemProgState = kFblMemProgState_SuspendPending;
}
/* Leave critical section */
__ApplFblMemLeaveCriticalSection();
}
#endif /* FBL_MEM_ENABLE_PIPELINING */
# define FBLLIB_MEM_RAMCODE_STOP_SEC_CODE
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
#define FBLLIB_MEM_START_SEC_CODE
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
/***********************************************************************************************************************
* FblMemEraseRegion
**********************************************************************************************************************/
/*! \brief Performs erase operation in non-volatile memory
* \details All memory segments fully or partially covered by given region are affected. Gaps in the memory segment
* definition are skipped.
* \pre Memory driver initialized
* \param[in] eraseAddress Start address of erase region
* \param[in] eraseLength Length of erase region
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
tFblMemStatus FblMemEraseRegion( tFblAddress eraseAddress, tFblLength eraseLength )
{
tFblMemStatus retVal;
#if defined( FBL_MEM_ENABLE_PROGRESS_INFO )
/* Disable progress information */
gProgressState = kFblMemProgressState_Disabled;
#endif /* FBL_MEM_ENABLE_PROGRESS_INFO */
retVal = FblMemEraseRegionInternal(eraseAddress, eraseLength);
return retVal;
}
/***********************************************************************************************************************
* FblMemProgramBuffer
**********************************************************************************************************************/
/*! \brief Performs program operation to non-volatile memory
* \details If the length is not aligned to the segment size the odd bytes are padded with the configured fill
* character.
* Programming may be suspended by an external event. In this case parameter programLength will be
* updated to reflect the length actually programmed
* \pre Memory driver initialized, address aligned to memory segment size
* \param[in] programAddress Program address
* \param[in,out] programLength Length of data (output: length actually programmed)
* \param[in,out] programData Pointer to program data (contents are padded in case length is not aligned to memory
* segment size!)
* \return Result of operation (potentially remapped to OEM specific NRC)
**********************************************************************************************************************/
tFblMemStatus FblMemProgramBuffer( tFblAddress programAddress,
V_MEMRAM1 tFblLength V_MEMRAM2 V_MEMRAM3 * programLength, tFblMemRamData programData )
{
tFblMemProgState activeProgState;
tFblMemStatus retVal;
{
/* Remember active programming state */
activeProgState = fblMemProgState;
/* Operation potentially paused during previous execution cycle */
fblMemProgState = kFblMemProgState_Pending;
/* Perform actual programming */
retVal = FblMemProgramBufferInternal(programAddress, programLength, programData, kFblMemProgState_Pending);
/* Restore previous programming state */
fblMemProgState = activeProgState;
}
return retVal;
}
/***********************************************************************************************************************
* FblMemSetInteger
**********************************************************************************************************************/
/*! \brief Convert given integer value to big-endian byte array
* \param[in] count Number of relevant bytes
* \param[in] input Input value
* \param[out] buffer Pointer to output buffer
**********************************************************************************************************************/
void FblMemSetInteger( vuintx count, vuint32 input, tFblMemRamData buffer )
{
vuint32 localInput = input;
vuintx localCount = count;
/* Integer to array conversion only supported for up to 32-bit values */
assertFblInternal(count <= sizeof(vuint32), kFblMemAssertParameterOutOfRange);
/* Loop relevant bytes */
while (localCount > 0u)
{
localCount--;
/* Store most significant byte first */
buffer[localCount] = (vuint8)(localInput & 0xFFu);
/* Shift in next byte */
localInput >>= 8u;
}
}
/***********************************************************************************************************************
* FblMemGetInteger
**********************************************************************************************************************/
/*! \brief Convert given big-endian byte array to integer value
* \param[in] count Number of relevant bytes
* \param[in] buffer Pointer to input buffer
* \return Integer value
**********************************************************************************************************************/
vuint32 FblMemGetInteger( vuintx count, tFblMemConstRamData buffer )
{
vuint32 output = 0u;
vuintx idx = 0u;
vuintx localCount = count;
/* Array to integer conversion only supported for up to 32-bit values */
assertFblInternal(count <= sizeof(vuint32), kFblMemAssertParameterOutOfRange);
/* Loop relevant bytes */
while (localCount > 0u)
{
/* Most significant byte first */
output <<= 8u;
/* Add current byte */
output |= (vuint32)buffer[idx];
idx++;
localCount--;
}
return output;
}
#define FBLLIB_MEM_STOP_SEC_CODE
#include "MemMap.h" /* PRQA S 5087 */ /* MD_MSR_MemMap */
/***********************************************************************************************************************
* MISRA DEVIATIONS
**********************************************************************************************************************/
/* Justification for module-specific MISRA deviations:
MD_FblMem_0306:
Reason: Address conversion between integer values and pointers is required to allow for hardware independent
configuration and address range checks.
Risk: The size of integer required to hold the result of a pointer cast is implementation defined.
Prevention: The size of the respective integer data type which holds the address value is adapted on a hardware
specific basis.
MD_FblMem_0310_3305:
Reason: Snapshot stored in byte buffer. This way caller doesn't need to know specific structure type.
Risk: Byte buffer size or alignment not suitable for direct access through structure pointer.
Prevention: Offset calculated to align pointer to platform requirements.
Effective size after alignment checked against actual structure requirements.
MD_FblMem_0313:
Reason: Functions pointers may be cast to different types if function parameters or function return values
are not used by callers.
Risk: Calling a function with erroneus call context or without a return value while expecting a return value
Prevention: Check if new type is compatible with target type:
- in case of function parameter change: Are paremeters of new type compatible with parameters of
target type or are function parameters of target type unused in active configuration?
- in case of return type change: is return value of target type compatible with return value of new
type or is a return value from target type never expected if target type returns void?
MD_FblMem_0314_0326_MemCpy:
Reason: The copy function have a void pointer as a function parameter and an integer is casted to pointer void.
Risk: No risk, because the underlying pointer type is known and the cast is safe.
Prevention: No prevention necessary.
MD_FblMem_0488:
Reason: Calling instance passes an arbitrary buffer pointer. Relative offset to internal buffer has to be
calculated.
Risk: 1) Range overflow caused by subtraction.
2) Accessing memory outside of allocated buffer.
Prevention: Input and internal pointer have exact same type
1) Input pointer compared against internal buffer before subtraction.
2) Pointer arithmetic not used to access data directly. Instead calculated offset and input length are verified
against constraints given by internal buffer. Actual data accesses use array indexing.
MD_FblMem_0602_0603:
Reason: Usage of reserved identifiers with leading underscores is accepted for compatibility reasons.
Risk: Name conflicts.
Prevention: Compile and link of the different variants in the component and integration test.
MD_FblMem_0724_EnumValNotUnique:
Reason: The same numerical value is associated to different enum entries, to allows each entry value to be used in
the appropriate context, keeping a better code structure and understanding.
Risk: Enum values can potentially be assigned with the same value unintentionally.
Prevention: Correct design.
MD_FblMem_2822:
Reason: Apparent NULL pointer which are actually correctly assigned at runtime.
Risk: The pointer is not initialized.
Prevention: A correct design prevent missing or uncorrect initialization.
MD_FblMem_2842:
Reason: Checks over out of boundary for arrays are usually done at compile time through Assertions, therefore there
is no need to perform further checks at runtime.
Risk: An out of boundary access is not detected through assertions and happens at runtime.
Prevention: The code shall be correctly designed in order to avoid any kind of out of boundary access.
MD_FblMem_2889:
Reason: Multiple return paths are used to reduce code complexity, increase readability and reducing nesting level.
Risk: Some operations intended to conclude the function (e.g. states cleaning) can be unintentionally jumped.
Prevention: Code inspection and runtime tests.
MD_FblMem_2982:
Reason: Depending on configuration different paths may be executed. Initialization makes sure no invalid variable
contents are used accidentally.
Risk: No identifiable risk.
Prevention: No prevention required.
MD_FblMem_2986:
Reason: Code is only redundant in certain configurations.
Risk: No identifiable risk
Prevention: No prevention required.
MD_FblMem_2991_2995:
Reason: The result of this logical operation might not always be true depending on configuration. Check is kept
to avoid excessive encapsulation which might decrease readability.
Risk: No identifiable risk.
Prevention: No prevention required.
MD_FblMem_3205_IdentifierNotUsed:
Reason: Some enumerations are not used in all the configurations or just define the extreme limits of the enumeration (MIN, MAX).
Risk: No identifiable risk.
Prevention: No prevention required.
MD_FblMem_3218:
Reason: The local data buffers of this module are kept at a central location for a better overview and maintenance.
Risk: Scope is larger than required (whole file instead of one function). Some other function could access
the variable.
Prevention: Restrict the functionality in this module to the intended purpose. Don't add functions which shall not
be able to access the local data buffers.
MD_FblMem_3415:
Reason: If condition relies on lazy evaluation, second argument only executed if ROM download is active.
Risk: No identifiable risk.
Prevention: No prevention required.
MD_FblMem_3673:
Reason: Depending on configuration the parameter is actually modified inside the calling hierarchy.
Risk: No identifiable risk.
Prevention: No prevention required.
MD_FblMem_6060:
Reason: Reducing the number of parameters can only be achieved by using a data grouping structure (i.e. structs)
or by splitting the function in different sub-functions, but both will affect the readability and the
maintenability of the function.
Risk: Stack usage and runtime too high for target uC.
Prevention: Test of resulting code on target uC. User must check stack usage in project context.
MD_FblMem_AssertJobMax:
Reason: For certain configurations this check is always false. However, this check can only be done at runtime.
Risk: No risk.
Prevention: No prevention required. (Note: assertions are disabled in production software).
MD_FblMem_FalseFinding
Reason: Manual evaluation shows that QAC throws a false finding. This was confirmed by using a different
QAC version with the same MISRA ruleset.
Risk: No risk.
Prevention: No prevention required.
*/
/***********************************************************************************************************************
* END OF FILE: FBL_MEM.C
**********************************************************************************************************************/