6013 lines
278 KiB
C
6013 lines
278 KiB
C
|
|
/***********************************************************************************************************************
|
||
|
|
* 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
|
||
|
|
**********************************************************************************************************************/
|