Refs #792. Reintegrated branch with the trunk after completing the template for new microcontroller ports.

git-svn-id: https://svn.code.sf.net/p/openblt/code/trunk@677 5dc33758-31d5-4daf-9ae8-b24bf3d40d73
This commit is contained in:
Frank Voorburg 2019-04-17 20:10:12 +00:00
parent bfccabb4e6
commit 6b20667a02
11 changed files with 2735 additions and 0 deletions

View File

@ -0,0 +1,71 @@
/************************************************************************************//**
* \file Source/_template/GCC/cpu_comp.c
* \brief Bootloader cpu module source file.
* \ingroup Target__template_cpu_comp
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
/************************************************************************************//**
* \defgroup Target__template_cpu_comp Compiler specifics of a port
* \brief This module implements the compiler specific parts of a microcontroller
* port.
* \ingroup Target__template
****************************************************************************************/
/****************************************************************************************
* Include files
****************************************************************************************/
#include "boot.h" /* bootloader generic header */
/************************************************************************************//**
** \brief Disable global interrupts.
** \return none.
**
****************************************************************************************/
void CpuIrqDisable(void)
{
/* TODO ##Port Disable the global interrupts. For safety and stability reasons, the
* bootloader does not use interrupts. This function is called to disable the
* generation of interrupt. It is called upon bootloader initialization by CpuInit().
*/
} /*** end of CpuIrqDisable ***/
/************************************************************************************//**
** \brief Enable global interrupts.
** \return none.
**
****************************************************************************************/
void CpuIrqEnable(void)
{
/* TODO ##Port Enable the global interrupts. Not all, but some microcontrollers have
* global interrupts enabled right after reset. In this case the bootloader should
* enable the global interrupts again, right before the user program is started in
* function CpuStartUserProgram().
*/
} /*** end of CpuIrqEnable ***/
/*********************************** end of cpu_comp.c *********************************/

View File

@ -0,0 +1,273 @@
/************************************************************************************//**
* \file Source/_template/can.c
* \brief Bootloader CAN communication interface source file.
* \ingroup Target__template_can
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
/************************************************************************************//**
* \defgroup Target__template_can CAN driver of a port
* \brief This module implements the CAN driver of a microcontroller port.
* \details For the most parts, this driver is already implemented. The only parts that
* need porting are the UART initialization, byte reception and byte
* transmission.
* \ingroup Target__template
****************************************************************************************/
/****************************************************************************************
* Include files
****************************************************************************************/
#include "boot.h" /* bootloader generic header */
#if (BOOT_COM_CAN_ENABLE > 0)
/* TODO ##Port Include microcontroller peripheral driver header files here. */
/****************************************************************************************
* Macro definitions
****************************************************************************************/
/** \brief Timeout for transmitting a CAN message in milliseconds. */
#define CAN_MSG_TX_TIMEOUT_MS (50u)
/****************************************************************************************
* Type definitions
****************************************************************************************/
/** \brief Structure type for grouping CAN bus timing related information. */
typedef struct t_can_bus_timing
{
blt_int8u tseg1; /**< CAN time segment 1 */
blt_int8u tseg2; /**< CAN time segment 2 */
} tCanBusTiming;
/****************************************************************************************
* Local constant declarations
****************************************************************************************/
/** \brief CAN bittiming table for dynamically calculating the bittiming settings.
* \details According to the CAN protocol 1 bit-time can be made up of between 8..25
* time quanta (TQ). The total TQ in a bit is SYNC + TSEG1 + TSEG2 with SYNC
* always being 1. The sample point is (SYNC + TSEG1) / (SYNC + TSEG1 + SEG2) *
* 100%. This array contains possible and valid time quanta configurations with
* a sample point between 68..78%.
*/
static const tCanBusTiming canTiming[] =
{
/* TQ | TSEG1 | TSEG2 | SP */
/* ------------------------- */
{ 5, 2 }, /* 8 | 5 | 2 | 75% */
{ 6, 2 }, /* 9 | 6 | 2 | 78% */
{ 6, 3 }, /* 10 | 6 | 3 | 70% */
{ 7, 3 }, /* 11 | 7 | 3 | 73% */
{ 8, 3 }, /* 12 | 8 | 3 | 75% */
{ 9, 3 }, /* 13 | 9 | 3 | 77% */
{ 9, 4 }, /* 14 | 9 | 4 | 71% */
{ 10, 4 }, /* 15 | 10 | 4 | 73% */
{ 11, 4 }, /* 16 | 11 | 4 | 75% */
{ 12, 4 }, /* 17 | 12 | 4 | 76% */
{ 12, 5 }, /* 18 | 12 | 5 | 72% */
{ 13, 5 }, /* 19 | 13 | 5 | 74% */
{ 14, 5 }, /* 20 | 14 | 5 | 75% */
{ 15, 5 }, /* 21 | 15 | 5 | 76% */
{ 15, 6 }, /* 22 | 15 | 6 | 73% */
{ 16, 6 }, /* 23 | 16 | 6 | 74% */
{ 16, 7 }, /* 24 | 16 | 7 | 71% */
{ 16, 8 } /* 25 | 16 | 8 | 68% */
};
/************************************************************************************//**
** \brief Search algorithm to match the desired baudrate to a possible bus
** timing configuration.
** \param baud The desired baudrate in kbps. Valid values are 10..1000.
** \param prescaler Pointer to where the value for the prescaler will be stored.
** \param tseg1 Pointer to where the value for TSEG2 will be stored.
** \param tseg2 Pointer to where the value for TSEG2 will be stored.
** \return BLT_TRUE if the CAN bustiming register values were found, BLT_FALSE
** otherwise.
**
****************************************************************************************/
static blt_bool CanGetSpeedConfig(blt_int16u baud, blt_int16u *prescaler,
blt_int8u *tseg1, blt_int8u *tseg2)
{
blt_int8u cnt;
blt_int32u canClockFreqkHz;
/* TODO ##Port This helper function assists with getting a compatible bittiming
* configuration, based on the specified 'baud' communication speed on the CAN bus in
* kbps. This function needs two microcontroller specific values: (1) the speed of
* the clock that sources the CAN peripheral and (2) the supported range of the
* prescaler that for scaling down the CAN peripheral clock speed.
*/
/* TODO ##Port Set the clock speed of the CAN peripheral in kHz. You can used the
* macros BOOT_CPU_XTAL_SPEED_KHZ and BOOT_CPU_SYSTEM_SPEED_KHZ if applicable.
*/
canClockFreqkHz = BOOT_CPU_XTAL_SPEED_KHZ;
/* loop through all possible time quanta configurations to find a match */
for (cnt=0; cnt < sizeof(canTiming)/sizeof(canTiming[0]); cnt++)
{
if ((canClockFreqkHz % (baud*(canTiming[cnt].tseg1+canTiming[cnt].tseg2+1))) == 0)
{
/* compute the prescaler that goes with this TQ configuration */
*prescaler = canClockFreqkHz/(baud*(canTiming[cnt].tseg1+canTiming[cnt].tseg2+1));
/* TODO ##Port Update the prescaler range that is supported by the CAN peripheral
* on the microcontroller. The example implementation is for a prescaler that can
* be in the 1 - 1024 range.
*/
/* make sure the prescaler is valid */
if ((*prescaler > 0) && (*prescaler <= 1024))
{
/* store the bustiming configuration */
*tseg1 = canTiming[cnt].tseg1;
*tseg2 = canTiming[cnt].tseg2;
/* found a good bus timing configuration */
return BLT_TRUE;
}
}
}
/* could not find a good bus timing configuration */
return BLT_FALSE;
} /*** end of CanGetSpeedConfig ***/
/************************************************************************************//**
** \brief Initializes the CAN controller and synchronizes it to the CAN bus.
** \return none.
**
****************************************************************************************/
void CanInit(void)
{
blt_int16u prescaler = 0;
blt_int8u tseg1 = 0, tseg2 = 0;
/* TODO ##Port Perform compile time assertion to check that the configured CAN channel
* is actually supported by this driver. The example is for a driver where CAN
* channels 0 - 1 are supported.
*/
ASSERT_CT((BOOT_COM_CAN_CHANNEL_INDEX == 0 || BOOT_COM_CAN_CHANNEL_INDEX == 1));
/* obtain bittiming configuration information. */
if (CanGetSpeedConfig(BOOT_COM_CAN_BAUDRATE/1000, &prescaler, &tseg1, &tseg2) == BLT_FALSE)
{
/* Incorrect configuration. The specified baudrate is not supported for the given
* clock configuration. Verify the following settings in blt_conf.h:
* - BOOT_COM_CAN_BAUDRATE
* - BOOT_CPU_XTAL_SPEED_KHZ
* - BOOT_CPU_SYSTEM_SPEED_KHZ
*/
ASSERT_RT(BLT_FALSE);
}
/* TODO ##Vg Perform the configuration and initialization of the CAN controller. Note
* that the bittiming related values are already stored in 'prescaler, 'tseg1', and
* 'tseg2'. There values are ready to be used. Typically, the following tasks need
* to be performed:
* (1) Place the CAN controller in initialization mode.
* (2) Disable all CAN related interrupts as the bootloader runs in polling mode.
* (3) Configure the bittiming based on: 'prescaler', 'tseg1' and 'tseg2'. It is okay
* to configure 1 time quanta for the synchronization jump width (SWJ).
* (4) Configure one transmit message object. It will be used in CanTransmitPacket()
* to transmit a CAN message with identifier BOOT_COM_CAN_TX_MSG_ID. Note that if
* the 0x80000000 bit is set in this identifier, it means that it is a 29-bit CAN
* identifier instead of an 11-bit.
* (5) Configure at least one reception message object and configure its reception
* acceptance filter such that only the CAN identifier BOOT_COM_CAN_RX_MSG_ID is
* received. Note that if the 0x80000000 bit is set in this identifier, it means
* that it is a 29-bit CAN identifier instead of an 11-bit.
* (6) Leave the initialization mode and place the CAN controller in operational mode.
*/
} /*** end of CanInit ***/
/************************************************************************************//**
** \brief Transmits a packet formatted for the communication interface.
** \param data Pointer to byte array with data that it to be transmitted.
** \param len Number of bytes that are to be transmitted.
** \return none.
**
****************************************************************************************/
void CanTransmitPacket(blt_int8u *data, blt_int8u len)
{
blt_int32u timeout;
/* TODO ##Port Configure the transmit message object for transmitting a CAN message
* with CAN identifier BOOT_COM_CAN_TX_MSG_ID. Note that if the 0x80000000 bit is set
* in this identifier, it means that it is a 29-bit CAN identifier instead of an
* 11-bit. Next, copy the message data to the transmit message object. The number
* of data bytes is in 'len' and the actual data byte values are in array 'data'.
* Once done, start the transmission of the message that was just stored in the
* transmit message object.
*/
/* TODO ##Port Wait for the message transmission to complete, with timeout though to
* make sure this function doesn't hang in case of an error. This is typically achieved
* by evaluating a transmit complete flag in a register of the transmit message object.
*/
/* determine timeout time for the transmit completion. */
timeout = TimerGet() + CAN_MSG_TX_TIMEOUT_MS;
/* poll for completion of the transmit operation. */
while (1 == 0)
{
/* service the watchdog. */
CopService();
/* break loop upon timeout. this would indicate a hardware failure or no other
* nodes connected to the bus.
*/
if (TimerGet() > timeout)
{
break;
}
}
} /*** end of CanTransmitPacket ***/
/************************************************************************************//**
** \brief Receives a communication interface packet if one is present.
** \param data Pointer to byte array where the data is to be stored.
** \param len Pointer where the length of the packet is to be stored.
** \return BLT_TRUE is a packet was received, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool CanReceivePacket(blt_int8u *data, blt_int8u *len)
{
blt_bool result = BLT_FALSE;
/* TODO ##Port Check for the reception of a new CAN message with identifier
* BOOT_COM_CAN_RX_MSG_ID. Note that if the 0x80000000 bit is set in this identifier,
* it means that it is a 29-bit CAN identifier instead of an 11-bit.
* If a new message with this CAN identifier was received, store the data byte values
* in array 'data' and store the number of data bytes in 'len'. Finally, set 'result'
* to BLT_TRUE to indicate to the caller of this function that a new CAN message was
* received and stored.
*/
/* give the result back to the caller */
return result;
} /*** end of CanReceivePacket ***/
#endif /* BOOT_COM_CAN_ENABLE > 0 */
/*********************************** end of can.c **************************************/

View File

@ -0,0 +1,232 @@
/************************************************************************************//**
* \file Source/_template/cpu.c
* \brief Bootloader cpu module source file.
* \ingroup Target__template_cpu
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
/************************************************************************************//**
* \defgroup Target__template_cpu CPU driver of a port
* \brief This module implements the CPU driver of a microcontroller port.
* \ingroup Target__template
****************************************************************************************/
/****************************************************************************************
* Include files
****************************************************************************************/
#include "boot.h" /* bootloader generic header */
/****************************************************************************************
* Macro definitions
****************************************************************************************/
/** \brief Pointer to the user program's vector table. */
#define CPU_USER_PROGRAM_VECTABLE_OFFSET ((blt_addr)NvmGetUserProgBaseAddress())
/****************************************************************************************
* Hook functions
****************************************************************************************/
#if (BOOT_CPU_USER_PROGRAM_START_HOOK > 0)
extern blt_bool CpuUserProgramStartHook(void);
#endif
/************************************************************************************//**
** \brief Initializes the CPU module.
** \return none.
**
****************************************************************************************/
void CpuInit(void)
{
/* bootloader runs in polling mode so disable the global interrupts. this is done for
* safety reasons. if the bootloader was started from a running user program, it could
* be that the user program did not properly disable the interrupt generation of
* peripherals. */
CpuIrqDisable();
} /*** end of CpuInit ***/
/************************************************************************************//**
** \brief Starts the user program, if one is present. In this case this function
** does not return.
** \return none.
**
****************************************************************************************/
void CpuStartUserProgram(void)
{
void (*pProgResetHandler)(void);
/* check if a user program is present by verifying the checksum */
if (NvmVerifyChecksum() == BLT_FALSE)
{
#if (BOOT_COM_DEFERRED_INIT_ENABLE > 0) && (BOOT_COM_ENABLE > 0)
/* bootloader will stay active so perform deferred initialization to make sure
* the communication interface that were not yet initialized are now initialized.
* this is needed to make sure firmware updates via these communication interfaces
* will be possible.
*/
ComDeferredInit();
#endif
/* not a valid user program so it cannot be started */
return;
}
#if (BOOT_CPU_USER_PROGRAM_START_HOOK > 0)
/* invoke callback */
if (CpuUserProgramStartHook() == BLT_FALSE)
{
#if (BOOT_COM_DEFERRED_INIT_ENABLE > 0) && (BOOT_COM_ENABLE > 0)
/* bootloader will stay active so perform deferred initialization to make sure
* the communication interface that were not yet initialized are now initialized.
* this is needed to make sure firmware updates via these communication interfaces
* will be possible.
*/
ComDeferredInit();
#endif
/* callback requests the user program to not be started */
return;
}
#endif
#if (BOOT_COM_ENABLE > 0)
/* release the communication interface */
ComFree();
#endif
/* reset the timer */
TimerReset();
/* TODO ##Port Prepare to start the user program. This typically consists of remapping
* the base address of the vector table, since the user program is typically moved
* forward to make space for the bootloader itself.
* Some microcontrollers to not support changing the base address of the vector
* table. In this the bootloader would need to reroute all interrupt vectors, except
* the reset vector, to the location in memory where the user program has its vector
* table. This was done in the HCS12 port.
* If the microcontroller does not support remapping the vector table base address in
* flash, it might support remapping it to RAM. In this case you would not only need
* to do the remapping, but also copy the user program's vector table to this area
* in RAM. This was done in the STM32F0 port.
*/
/* TODO ##Port Enable the global interrupts by calling function CpuIrqEnable(). Note
* that this should only be done if the microcontroller normally has global interrupts
* enabled after a reset event. Otherwise, you can skip this part.
*/
CpuIrqEnable();
/* TODO ##Port Start the user program. This is achieved by reading out the address
* of the user program's reset handler from its vector table and jumping to it.
* The following example implementation shows how this is done in case the reset
* handler is located in the first entry of the interrupt vector table and the
* interrupt vector table is at the start of the user program.
* Note that for a lot of ARM Cortex CPUs, the first entry is the stackpointer and the
* second entry is the reset handler. In this case an extra 4 bytes need to be added
* to get to the address of where the reset handler pointer is located. In this case
* the user program should also explicitly initialize the stackpointer as the first
* thing in the reset handler.
*/
/* set the address where the bootloader needs to jump to. this is the address of
* the 1st entry in the user program's vector table. this address points to the
* user program's reset handler.
*/
pProgResetHandler = (void(*)(void))(*((blt_addr *)NvmGetUserProgBaseAddress()));
/* start the user program by calling its reset interrupt service routine */
pProgResetHandler();
#if (BOOT_COM_DEFERRED_INIT_ENABLE > 0) && (BOOT_COM_ENABLE > 0)
/* theoretically, the code never gets here because the user program should now be
* running and the previous function call should not return. In case it did return
* for whatever reason, make sure all communication interfaces are initialized so that
* firmware updates can be started.
*/
ComDeferredInit();
#endif
} /*** end of CpuStartUserProgram ***/
/************************************************************************************//**
** \brief Copies data from the source to the destination address.
** \param dest Destination address for the data.
** \param src Source address of the data.
** \param len length of the data in bytes.
** \return none.
**
****************************************************************************************/
void CpuMemCopy(blt_addr dest, blt_addr src, blt_int16u len)
{
blt_int8u *from, *to;
/* TODO ##Port Implements similar functionality as the C library's memcpy() function.
* For most ports you can simply leave this function as is. If desired you can optimize
* the implementation, for example by copying 32-bits at a time for 32-bit CPU
* architectures. Alternativly, you could just use memcpy().
*/
/* set casted pointers */
from = (blt_int8u *)src;
to = (blt_int8u *)dest;
/* copy all bytes from source address to destination address */
while (len-- > 0)
{
/* store byte value from source to destination */
*to++ = *from++;
/* keep the watchdog happy */
CopService();
}
} /*** end of CpuMemCopy ***/
/************************************************************************************//**
** \brief Sets the bytes at the destination address to the specified value.
** \param dest Destination address for the data.
** \param value Value to write.
** \param len Number of bytes to write.
** \return none.
**
****************************************************************************************/
void CpuMemSet(blt_addr dest, blt_int8u value, blt_int16u len)
{
blt_int8u *to;
/* TODO ##Port Implements similar functionality as the C library's memset() function.
* For most ports you can simply leave this function as is. If desired you can optimize
* the implementation, for example by setting 32-bits at a time for 32-bit CPU
* architectures. Alternativly, you could just use memset().
*/
/* set casted pointer */
to = (blt_int8u *)dest;
/* set all bytes at the destination address to the specified value */
while (len-- > 0)
{
/* set byte value */
*to++ = value;
/* keep the watchdog happy */
CopService();
}
} /*** end of CpuMemSet ***/
/*********************************** end of cpu.c **************************************/

View File

@ -0,0 +1,867 @@
/************************************************************************************//**
* \file Source/_template/flash.c
* \brief Bootloader flash driver source file.
* \ingroup Target__template_flash
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
/************************************************************************************//**
* \defgroup Target__template_flash Flash driver of a port
* \brief This module implements the flash EEPROM memory driver of a microcontroller
* port.
* \details The flash driver manages the actual erase and program operations on the
* flash EEPROM and the signature checksum.
* The signature checksum is a 32-bit value in the user program. It is used as
* a flag to determine if a user program is present or not.
* Newly programmed data is always first buffered in RAM buffers with a size
* of FLASH_WRITE_BLOCK_SIZE.
* This driver manages a second RAM buffer of the same size, called the
* bootBlock. The bootBlock buffers program data that includes the interrupt
* vector table and the 32-bit signature checksum. The signature checksum
* value is written as the last step during a firmware update, hence the need
* for the bootBlock.
* Note that the majority of this flash driver can be used as is. The only
* parts that need to be updated / implemented are:
* * Macros FLASH_WRITE_BLOCK_SIZE and BOOT_FLASH_VECTOR_TABLE_CS_OFFSET.
* * The flashLayout[]-array contents.
* * The functions FlashEraseSectors() and FlashWriteBlock().
* * The functions FlashWriteChecksum() and FlashVerifyChecksum().
* \ingroup Target__template
****************************************************************************************/
/****************************************************************************************
* Include files
****************************************************************************************/
#include "boot.h" /* bootloader generic header */
/****************************************************************************************
* Macro definitions
****************************************************************************************/
/** \brief Value for an invalid flash sector. */
#define FLASH_INVALID_SECTOR (0xff)
/** \brief Value for an invalid flash address. */
#define FLASH_INVALID_ADDRESS (0xffffffff)
/** \brief Standard size of a flash block for writing. */
/* TODO ##Port The FLASH_WRITE_BLOCK_SIZE should be at least 512. If for some reason this
* is not large enough, double the size so: 512 -> 1024 -> 2048 -> 4096 etc.
*/
#define FLASH_WRITE_BLOCK_SIZE (512)
/** \brief Total numbers of sectors in array flashLayout[]. */
#define FLASH_TOTAL_SECTORS (sizeof(flashLayout)/sizeof(flashLayout[0]))
/** \brief End address of the bootloader programmable flash. */
#define FLASH_END_ADDRESS (flashLayout[FLASH_TOTAL_SECTORS-1].sector_start + \
flashLayout[FLASH_TOTAL_SECTORS-1].sector_size - 1)
/** \brief Offset into the user program's vector table where the checksum is located.
* For this target it is set to the end of the vector table. Note that the
* value can be overriden in blt_conf.h, because the size of the vector table
* could vary. When changing this value, don't forget to update the location
* of the checksum in the user program accordingly. Otherwise the checksum
* verification will always fail.
*/
#ifndef BOOT_FLASH_VECTOR_TABLE_CS_OFFSET
/* TODO ##Port The bootloader uses a 32-bit checksum signature value to determine if a
* a valid user program is present or not. This checksum value is written by the
* bootloader at the end of a firmware update with function FlashWriteChecksum(). Right
* before a user program is about to be started, function FlashVerifyChecksum() is called
* to verify the presence of a user program. Space must be reserved in the user program
* for the checksum signature value and the bootloader needs to know where this space
* is reserved. It is recommended to place the signature checksum right after the
* user program's vector table. Using this approach it is easy to reserved space for the
* checksum signature in the user program by simply adding one more dummy entry into the
* vector table. This macro should be set to the size of the vector table, which can then
* be used to determine the memory address of the signature checksum.
*/
#define BOOT_FLASH_VECTOR_TABLE_CS_OFFSET (0x188)
#endif
/****************************************************************************************
* Plausibility checks
****************************************************************************************/
#if (BOOT_FLASH_VECTOR_TABLE_CS_OFFSET >= FLASH_WRITE_BLOCK_SIZE)
#error "BOOT_FLASH_VECTOR_TABLE_CS_OFFSET is set too high. It must be located in the first writable block."
#endif
#ifndef BOOT_FLASH_CUSTOM_LAYOUT_ENABLE
#define BOOT_FLASH_CUSTOM_LAYOUT_ENABLE (0u)
#endif
/****************************************************************************************
* Type definitions
****************************************************************************************/
/** \brief Flash sector descriptor type. */
typedef struct
{
blt_addr sector_start; /**< sector start address */
blt_int32u sector_size; /**< sector size in bytes */
blt_int8u sector_num; /**< sector number */
} tFlashSector;
/** \brief Structure type for grouping flash block information.
* \details Programming is done per block of max FLASH_WRITE_BLOCK_SIZE. for this a
* flash block manager is implemented in this driver. this flash block manager
* depends on this flash block info structure. It holds the base address of
* the flash block and the data that should be programmed into the flash
* block. The .base_addr must be a multiple of FLASH_WRITE_BLOCK_SIZE.
*/
typedef struct
{
blt_addr base_addr;
blt_int8u data[FLASH_WRITE_BLOCK_SIZE];
} tFlashBlockInfo;
/****************************************************************************************
* Hook functions
****************************************************************************************/
#if (BOOT_FLASH_CRYPTO_HOOKS_ENABLE > 0)
extern blt_bool FlashCryptoDecryptDataHook(blt_int8u * data, blt_int32u size);
#endif
/****************************************************************************************
* Function prototypes
****************************************************************************************/
static blt_bool FlashInitBlock(tFlashBlockInfo *block, blt_addr address);
static tFlashBlockInfo *FlashSwitchBlock(tFlashBlockInfo *block, blt_addr base_addr);
static blt_bool FlashAddToBlock(tFlashBlockInfo *block, blt_addr address,
blt_int8u *data, blt_int32u len);
static blt_bool FlashWriteBlock(tFlashBlockInfo *block);
static blt_bool FlashEraseSectors(blt_int8u first_sector, blt_int8u last_sector);
static blt_int8u FlashGetSector(blt_addr address);
static blt_addr FlashGetSectorBaseAddr(blt_int8u sector);
static blt_addr FlashGetSectorSize(blt_int8u sector);
/****************************************************************************************
* Local constant declarations
****************************************************************************************/
/** \brief If desired, it is possible to set BOOT_FLASH_CUSTOM_LAYOUT_ENABLE to > 0
* in blt_conf.h and then implement your own version of the flashLayout[] table
* in a source-file with the name flash_layout.c. This way you customize the
* flash memory size reserved for the bootloader, without having to modify
* the flashLayout[] table in this file directly. This file will then include
* flash_layout.c so there is no need to compile it additionally with your
* project.
*/
#if (BOOT_FLASH_CUSTOM_LAYOUT_ENABLE == 0)
/** \brief Array wit the layout of the flash memory.
* \details Also controls what part of the flash memory is reserved for the bootloader.
* If the bootloader size changes, the reserved sectors for the bootloader
* might need adjustment to make sure the bootloader doesn't get overwritten.
*/
static const tFlashSector flashLayout[] =
{
/* TODO ##Port Update the contents of this array with the erase sector sizes as defined
* in the microcontroller's reference manual. The flash sector erase sizes are
* hardware specific and must therefore match, otherwise erase operations cannot be
* performed properly.
* Besides controlling the flash erase size, this array also controls which sectors
* are reserved for the bootloader and will therefore never be erased. The current
* fictive implementation is for a microcontroller that can only erase flash memory
* in chunks of 16 KB and the first 32 KB are reserved for the bootloader. Its flash
* memory starts at 0x08000000 in the memory map.
*/
/* { 0x08000000, 0x04000, 0}, flash sector 0 - reserved for bootloader */
/* { 0x08004000, 0x04000, 1}, flash sector 1 - reserved for bootloader */
{ 0x08008000, 0x04000, 2}, /* flash sector 2 - 16kb */
{ 0x0800C000, 0x04000, 3}, /* flash sector 3 - 16kb */
#if (BOOT_NVM_SIZE_KB > 64)
{ 0x08010000, 0x4000, 4}, /* flash sector 4 - 16kb */
{ 0x08014000, 0x4000, 5}, /* flash sector 5 - 16kb */
{ 0x08018000, 0x4000, 6}, /* flash sector 6 - 16kb */
{ 0x0801C000, 0x4000, 7}, /* flash sector 7 - 16kb */
#endif
#if (BOOT_NVM_SIZE_KB > 128)
{ 0x08020000, 0x4000, 8}, /* flash sector 8 - 16kb */
{ 0x08024000, 0x4000, 9}, /* flash sector 9 - 16kb */
{ 0x08028000, 0x4000, 10}, /* flash sector 10 - 16kb */
{ 0x0802C000, 0x4000, 11}, /* flash sector 11 - 16kb */
{ 0x08030000, 0x4000, 12}, /* flash sector 12 - 16kb */
{ 0x08034000, 0x4000, 13}, /* flash sector 13 - 16kb */
{ 0x08038000, 0x4000, 14}, /* flash sector 14 - 16kb */
{ 0x0803C000, 0x4000, 15}, /* flash sector 15 - 16kb */
#endif
#if (BOOT_NVM_SIZE_KB > 256)
#error "BOOT_NVM_SIZE_KB > 256 is currently not supported."
#endif
};
#else
#include "flash_layout.c"
#endif /* BOOT_FLASH_CUSTOM_LAYOUT_ENABLE == 0 */
/****************************************************************************************
* Local data declarations
****************************************************************************************/
/** \brief Local variable with information about the flash block that is currently
* being operated on.
* \details The smallest amount of flash that can be programmed is
* FLASH_WRITE_BLOCK_SIZE. A flash block manager is implemented in this driver
* and stores info in this variable. Whenever new data should be flashed, it
* is first added to a RAM buffer, which is part of this variable. Whenever
* the RAM buffer, which has the size of a flash block, is full or data needs
* to be written to a different block, the contents of the RAM buffer are
* programmed to flash. The flash block manager requires some software
* overhead, yet results is faster flash programming because data is first
* harvested, ideally until there is enough to program an entire flash block,
* before the flash device is actually operated on.
*/
static tFlashBlockInfo blockInfo;
/** \brief Local variable with information about the flash boot block.
* \details The first block of the user program holds the vector table, which on the
* STM32 is also the where the checksum is written to. Is it likely that
* the vector table is first flashed and then, at the end of the programming
* sequence, the checksum. This means that this flash block need to be written
* to twice. Normally this is not a problem with flash memory, as long as you
* write the same values to those bytes that are not supposed to be changed
* and the locations where you do write to are still in the erased 0xFF state.
* Unfortunately, writing twice to flash this way, does not work reliably on
* all micros. This is why we need to have an extra block, the bootblock,
* placed under the management of the block manager. This way is it possible
* to implement functionality so that the bootblock is only written to once
* at the end of the programming sequence.
*/
static tFlashBlockInfo bootBlockInfo;
/************************************************************************************//**
** \brief Initializes the flash driver.
** \return none.
**
****************************************************************************************/
void FlashInit(void)
{
/* init the flash block info structs by setting the address to an invalid address */
blockInfo.base_addr = FLASH_INVALID_ADDRESS;
bootBlockInfo.base_addr = FLASH_INVALID_ADDRESS;
} /*** end of FlashInit ***/
/************************************************************************************//**
** \brief Reinitializes the flash driver.
** \return none.
**
****************************************************************************************/
void FlashReinit(void)
{
/* init the flash block info structs by setting the address to an invalid address */
blockInfo.base_addr = FLASH_INVALID_ADDRESS;
bootBlockInfo.base_addr = FLASH_INVALID_ADDRESS;
} /*** end of FlashReinit ***/
/************************************************************************************//**
** \brief Writes the data to flash through a flash block manager. Note that this
** function also checks that no data is programmed outside the flash
** memory region, so the bootloader can never be overwritten.
** \param addr Start address.
** \param len Length in bytes.
** \param data Pointer to the data buffer.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool FlashWrite(blt_addr addr, blt_int32u len, blt_int8u *data)
{
blt_addr base_addr;
/* validate the len parameter */
if ((len - 1) > (FLASH_END_ADDRESS - addr))
{
return BLT_FALSE;
}
/* make sure the addresses are within the flash device */
if ((FlashGetSector(addr) == FLASH_INVALID_SECTOR) || \
(FlashGetSector(addr+len-1) == FLASH_INVALID_SECTOR))
{
return BLT_FALSE;
}
/* if this is the bootblock, then let the boot block manager handle it */
base_addr = (addr/FLASH_WRITE_BLOCK_SIZE)*FLASH_WRITE_BLOCK_SIZE;
if (base_addr == flashLayout[0].sector_start)
{
/* let the boot block manager handle it */
return FlashAddToBlock(&bootBlockInfo, addr, data, len);
}
/* let the block manager handle it */
return FlashAddToBlock(&blockInfo, addr, data, len);
} /*** end of FlashWrite ***/
/************************************************************************************//**
** \brief Erases the flash memory. Note that this function also checks that no
** data is erased outside the flash memory region, so the bootloader can
** never be erased.
** \param addr Start address.
** \param len Length in bytes.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool FlashErase(blt_addr addr, blt_int32u len)
{
blt_int8u first_sector;
blt_int8u last_sector;
/* validate the len parameter */
if ((len - 1) > (FLASH_END_ADDRESS - addr))
{
return BLT_FALSE;
}
/* obtain the first and last sector number */
first_sector = FlashGetSector(addr);
last_sector = FlashGetSector(addr+len-1);
/* check them */
if ((first_sector == FLASH_INVALID_SECTOR) || (last_sector == FLASH_INVALID_SECTOR))
{
return BLT_FALSE;
}
/* erase the sectors */
return FlashEraseSectors(first_sector, last_sector);
} /*** end of FlashErase ***/
/************************************************************************************//**
** \brief Writes a checksum of the user program to non-volatile memory. This is
** performed once the entire user program has been programmed. Through
** the checksum, the bootloader can check if the programming session
** was completed, which indicates that a valid user programming is
** present and can be started.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool FlashWriteChecksum(void)
{
blt_int32u signature_checksum = 0;
/* TODO ##Port Calculate and write the signature checksum such that it appears at the
* address configured with macro BOOT_FLASH_VECTOR_TABLE_CS_OFFSET. Use the
* FlashWrite() function for the actual write operation. For a typical microcontroller,
* the bootBlock holds the program code that includes the user program's interrupt
* vector table and after which the 32-bit for the signature checksum is reserved.
*
* Note that this means one extra dummy entry must be added at the end of the user
* program's vector table to reserve storage space for the signature checksum value,
* which is then overwritten by this function.
*
* The example here calculates a signature checksum by summing up the first 32-bit
* values in the bootBlock (so the first 7 interrupt vectors) and then taking the
* Two's complement of this sum. You can modify this to anything you like as long as
* the signature checksum is based on program code present in the bootBlock.
*/
/* first check that the bootblock contains valid data. if not, this means the
* bootblock is not part of the reprogramming this time and therefore no
* new checksum needs to be written
*/
if (bootBlockInfo.base_addr == FLASH_INVALID_ADDRESS)
{
return BLT_TRUE;
}
#if (BOOT_FLASH_CRYPTO_HOOKS_ENABLE > 0)
/* perform decryption of the bootblock, before calculating the checksum and writing it
* to flash memory.
*/
if (FlashCryptoDecryptDataHook(bootBlockInfo.data, FLASH_WRITE_BLOCK_SIZE) == BLT_FALSE)
{
return BLT_FALSE;
}
#endif
/* compute the checksum. note that the user program's vectors are not yet written
* to flash but are present in the bootblock data structure at this point.
*/
signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x00]));
signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x04]));
signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x08]));
signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x0C]));
signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x10]));
signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x14]));
signature_checksum += *((blt_int32u *)(&bootBlockInfo.data[0+0x18]));
signature_checksum = ~signature_checksum; /* one's complement */
signature_checksum += 1; /* two's complement */
/* write the checksum */
return FlashWrite(flashLayout[0].sector_start+BOOT_FLASH_VECTOR_TABLE_CS_OFFSET,
sizeof(blt_addr), (blt_int8u *)&signature_checksum);
} /*** end of FlashWriteChecksum ***/
/************************************************************************************//**
** \brief Verifies the checksum, which indicates that a valid user program is
** present and can be started.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool FlashVerifyChecksum(void)
{
blt_int32u signature_checksum = 0;
/* TODO ##Port Implement code here that basically does the reverse of
* FlashWriteChecksum(). Just make sure to read the values directory from flash memory
* and NOT from the bootBlock.
* The example implementation reads the first 7 32-bit from the user program flash
* memory and sums them up. The signature checksum written by FlashWriteChecksum() was
* the Two complement's value. This means that if you add the previously written
* signature checksum value to the sum of the first 7 32-bit values, the result is
* a value of 0 in case the signature checksum is valid.
*/
/* verify the checksum based on how it was written by FlashWriteChecksum(). */
signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start));
signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+0x04));
signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+0x08));
signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+0x0C));
signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+0x10));
signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+0x14));
signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+0x18));
/* add the checksum value that was written by FlashWriteChecksum(). Since this was a
* Two complement's value, the resulting value should equal 0.
*/
signature_checksum += *((blt_int32u *)(flashLayout[0].sector_start+BOOT_FLASH_VECTOR_TABLE_CS_OFFSET));
/* sum should add up to an unsigned 32-bit value of 0 */
if (signature_checksum == 0)
{
/* checksum okay */
return BLT_TRUE;
}
/* checksum incorrect */
return BLT_FALSE;
} /*** end of FlashVerifyChecksum ***/
/************************************************************************************//**
** \brief Finalizes the flash driver operations. There could still be data in
** the currently active block that needs to be flashed.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool FlashDone(void)
{
/* check if there is still data waiting to be programmed in the boot block */
if (bootBlockInfo.base_addr != FLASH_INVALID_ADDRESS)
{
if (FlashWriteBlock(&bootBlockInfo) == BLT_FALSE)
{
return BLT_FALSE;
}
}
/* check if there is still data waiting to be programmed */
if (blockInfo.base_addr != FLASH_INVALID_ADDRESS)
{
if (FlashWriteBlock(&blockInfo) == BLT_FALSE)
{
return BLT_FALSE;
}
}
/* still here so all is okay */
return BLT_TRUE;
} /*** end of FlashDone ***/
/************************************************************************************//**
** \brief Obtains the base address of the flash memory available to the user program.
** This is basically the first address in the flashLayout table.
** \return Base address.
**
****************************************************************************************/
blt_addr FlashGetUserProgBaseAddress(void)
{
return flashLayout[0].sector_start;
} /*** end of FlashGetUserProgBaseAddress ***/
/************************************************************************************//**
** \brief Copies data currently in flash to the block->data and sets the
** base address.
** \param block Pointer to flash block info structure to operate on.
** \param address Base address of the block data.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
static blt_bool FlashInitBlock(tFlashBlockInfo *block, blt_addr address)
{
/* check address alignment */
if ((address % FLASH_WRITE_BLOCK_SIZE) != 0)
{
return BLT_FALSE;
}
/* make sure that we are initializing a new block and not the same one */
if (block->base_addr == address)
{
/* block already initialized, so nothing to do */
return BLT_TRUE;
}
/* set the base address and copies the current data from flash */
block->base_addr = address;
CpuMemCopy((blt_addr)block->data, address, FLASH_WRITE_BLOCK_SIZE);
return BLT_TRUE;
} /*** end of FlashInitBlock ***/
/************************************************************************************//**
** \brief Switches blocks by programming the current one and initializing the
** next.
** \param block Pointer to flash block info structure to operate on.
** \param base_addr Base address of the next block.
** \return The pointer of the block info struct that is no being used, or a NULL
** pointer in case of error.
**
****************************************************************************************/
static tFlashBlockInfo *FlashSwitchBlock(tFlashBlockInfo *block, blt_addr base_addr)
{
/* check if a switch needs to be made away from the boot block. in this case the boot
* block shouldn't be written yet, because this is done at the end of the programming
* session by FlashDone(), this is right after the checksum was written.
*/
if (block == &bootBlockInfo)
{
/* switch from the boot block to the generic block info structure */
block = &blockInfo;
}
/* check if a switch back into the bootblock is needed. in this case the generic block
* doesn't need to be written here yet.
*/
else if (base_addr == flashLayout[0].sector_start)
{
/* switch from the generic block to the boot block info structure */
block = &bootBlockInfo;
base_addr = flashLayout[0].sector_start;
}
else
{
/* need to switch to a new block, so program the current one and init the next */
if (FlashWriteBlock(block) == BLT_FALSE)
{
return BLT_NULL;
}
}
/* initialize tne new block when necessary */
if (FlashInitBlock(block, base_addr) == BLT_FALSE)
{
return BLT_NULL;
}
/* still here to all is okay */
return block;
} /*** end of FlashSwitchBlock ***/
/************************************************************************************//**
** \brief Programming is done per block. This function adds data to the block
** that is currently collecting data to be written to flash. If the
** address is outside of the current block, the current block is written
** to flash an a new block is initialized.
** \param block Pointer to flash block info structure to operate on.
** \param address Flash destination address.
** \param data Pointer to the byte array with data.
** \param len Number of bytes to add to the block.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
static blt_bool FlashAddToBlock(tFlashBlockInfo *block, blt_addr address,
blt_int8u *data, blt_int32u len)
{
blt_addr current_base_addr;
blt_int8u *dst;
blt_int8u *src;
/* determine the current base address */
current_base_addr = (address/FLASH_WRITE_BLOCK_SIZE)*FLASH_WRITE_BLOCK_SIZE;
/* make sure the blockInfo is not uninitialized */
if (block->base_addr == FLASH_INVALID_ADDRESS)
{
/* initialize the blockInfo struct for the current block */
if (FlashInitBlock(block, current_base_addr) == BLT_FALSE)
{
return BLT_FALSE;
}
}
/* check if the new data fits in the current block */
if (block->base_addr != current_base_addr)
{
/* need to switch to a new block, so program the current one and init the next */
block = FlashSwitchBlock(block, current_base_addr);
if (block == BLT_NULL)
{
return BLT_FALSE;
}
}
/* add the data to the current block, but check for block overflow */
dst = &(block->data[address - block->base_addr]);
src = data;
do
{
/* keep the watchdog happy */
CopService();
/* buffer overflow? */
if ((blt_addr)(dst-&(block->data[0])) >= FLASH_WRITE_BLOCK_SIZE)
{
/* need to switch to a new block, so program the current one and init the next */
block = FlashSwitchBlock(block, current_base_addr+FLASH_WRITE_BLOCK_SIZE);
if (block == BLT_NULL)
{
return BLT_FALSE;
}
/* reset destination pointer */
dst = &(block->data[0]);
}
/* write the data to the buffer */
*dst = *src;
/* update pointers */
dst++;
src++;
/* decrement byte counter */
len--;
}
while (len > 0);
/* still here so all is good */
return BLT_TRUE;
} /*** end of FlashAddToBlock ***/
/************************************************************************************//**
** \brief Programs FLASH_WRITE_BLOCK_SIZE bytes to flash from the block->data
** array.
** \param block Pointer to flash block info structure to operate on.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
static blt_bool FlashWriteBlock(tFlashBlockInfo *block)
{
blt_addr prog_addr;
blt_int32u prog_data;
blt_int32u word_cnt;
blt_bool result = BLT_TRUE;
#if (BOOT_FLASH_CRYPTO_HOOKS_ENABLE > 0)
#if (BOOT_NVM_CHECKSUM_HOOKS_ENABLE == 0)
/* note that the bootblock is already decrypted in FlashWriteChecksum(), if the
* internal checksum mechanism is used. Therefore don't decrypt it again.
*/
if (block != &bootBlockInfo)
#endif
{
/* perform decryption of the program data before writing it to flash memory. */
if (FlashCryptoDecryptDataHook(block->data, FLASH_WRITE_BLOCK_SIZE) == BLT_FALSE)
{
return BLT_FALSE;
}
}
#endif
/* TODO ##Port Program the data contents in 'block' to flash memory here and read the
* programmed data values back directory from flash memory to verify that the flash
* program operation was successful. The example implementation assumes that flash
* data can be written 32-bits at a time.
*/
/* program all words in the block one by one */
for (word_cnt=0; word_cnt<(FLASH_WRITE_BLOCK_SIZE/sizeof(blt_int32u)); word_cnt++)
{
prog_addr = block->base_addr + (word_cnt * sizeof(blt_int32u));
prog_data = *(volatile blt_int32u *)(&block->data[word_cnt * sizeof(blt_int32u)]);
/* keep the watchdog happy */
CopService();
/* TODO ##Port Program 32-bit 'prog_data' data value to memory address 'prog_addr'.
* In case an error occured, set result to BLT_FALSE and break the loop.
*/
if (1 == 0)
{
result = BLT_FALSE;
break;
}
/* verify that the written data is actually there */
if (*(volatile blt_int32u *)prog_addr != prog_data)
{
/* TODO ##Port Uncomment the following two lines again. It was commented out so
* that a dry run with the flash driver is possible without it reporting errors.
*/
/*result = BLT_FALSE;*/
/*break;*/
}
}
/* Give the result back to the caller. */
return result;
} /*** end of FlashWriteBlock ***/
/************************************************************************************//**
** \brief Erases the flash sectors from first_sector up until last_sector.
** \param first_sector First flash sector number.
** \param last_sector Last flash sector number.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
static blt_bool FlashEraseSectors(blt_int8u first_sector, blt_int8u last_sector)
{
blt_bool result = BLT_TRUE;
blt_int8u sectorIdx;
blt_addr sectorBaseAddr;
blt_int32u sectorSize;
/* validate the sector numbers */
if (first_sector > last_sector)
{
result = BLT_FALSE;
}
if ((first_sector < flashLayout[0].sector_num) || \
(last_sector > flashLayout[FLASH_TOTAL_SECTORS-1].sector_num))
{
result = BLT_FALSE;
}
/* only move forward with the erase operation if all is okay so far */
if (result == BLT_TRUE)
{
/* erase all sectors one by one */
for (sectorIdx=first_sector; sectorIdx<= last_sector; sectorIdx++)
{
/* keep the watchdog happy */
CopService();
/* get information about the sector */
sectorBaseAddr = FlashGetSectorBaseAddr(sectorIdx);
sectorSize = FlashGetSectorSize(sectorIdx);
/* validate the sector information */
if ( (sectorBaseAddr == FLASH_INVALID_ADDRESS) || (sectorSize == 0) )
{
/* invalid sector information. flag error and abort erase operation */
result = BLT_FALSE;
break;
}
/* TODO ##Port Perform the flash erase operation of a sector that starts at
* 'sectorBaseAddr' and has a length of 'sectorSize' bytes. In case an error
* occured, set result to BLT_FALSE and break the loop.
*/
if(1 == 0)
{
/* could not perform erase operation */
result = BLT_FALSE;
/* error detected so don't bother continuing with the loop */
break;
}
}
}
/* give the result back to the caller */
return result;
} /*** end of FlashEraseSectors ***/
/************************************************************************************//**
** \brief Determines the flash sector the address is in.
** \param address Address in the flash sector.
** \return Flash sector number or FLASH_INVALID_SECTOR.
**
****************************************************************************************/
static blt_int8u FlashGetSector(blt_addr address)
{
blt_int8u result = FLASH_INVALID_SECTOR;
blt_int8u sectorIdx;
/* search through the sectors to find the right one */
for (sectorIdx = 0; sectorIdx < FLASH_TOTAL_SECTORS; sectorIdx++)
{
/* keep the watchdog happy */
CopService();
/* is the address in this sector? */
if ((address >= flashLayout[sectorIdx].sector_start) && \
(address < (flashLayout[sectorIdx].sector_start + \
flashLayout[sectorIdx].sector_size)))
{
/* found the sector we are looking for so store it */
result = flashLayout[sectorIdx].sector_num;
/* all done so no need to continue looping */
break;
}
}
/* give the result back to the caller */
return result;
} /*** end of FlashGetSector ***/
/************************************************************************************//**
** \brief Determines the flash sector base address.
** \param sector Sector to get the base address of.
** \return Flash sector base address or FLASH_INVALID_ADDRESS.
**
****************************************************************************************/
static blt_addr FlashGetSectorBaseAddr(blt_int8u sector)
{
blt_int8u sectorIdx;
/* search through the sectors to find the right one */
for (sectorIdx = 0; sectorIdx < FLASH_TOTAL_SECTORS; sectorIdx++)
{
/* keep the watchdog happy */
CopService();
if (flashLayout[sectorIdx].sector_num == sector)
{
return flashLayout[sectorIdx].sector_start;
}
}
/* still here so no valid sector found */
return FLASH_INVALID_ADDRESS;
} /*** end of FlashGetSectorBaseAddr ***/
/************************************************************************************//**
** \brief Determines the flash sector size.
** \param sector Sector to get the size of.
** \return Flash sector size or 0.
**
****************************************************************************************/
static blt_addr FlashGetSectorSize(blt_int8u sector)
{
blt_int8u sectorIdx;
/* search through the sectors to find the right one */
for (sectorIdx = 0; sectorIdx < FLASH_TOTAL_SECTORS; sectorIdx++)
{
/* keep the watchdog happy */
CopService();
if (flashLayout[sectorIdx].sector_num == sector)
{
return flashLayout[sectorIdx].sector_size;
}
}
/* still here so no valid sector found */
return 0;
} /*** end of FlashGetSectorSize ***/
/*********************************** end of flash.c ************************************/

View File

@ -0,0 +1,45 @@
/************************************************************************************//**
* \file Source/_template/flash.h
* \brief Bootloader flash driver header file.
* \ingroup Target__template_flash
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
#ifndef FLASH_H
#define FLASH_H
/****************************************************************************************
* Function prototypes
****************************************************************************************/
void FlashInit(void);
void FlashReinit(void);
blt_bool FlashWrite(blt_addr addr, blt_int32u len, blt_int8u *data);
blt_bool FlashErase(blt_addr addr, blt_int32u len);
blt_bool FlashWriteChecksum(void);
blt_bool FlashVerifyChecksum(void);
blt_bool FlashDone(void);
blt_addr FlashGetUserProgBaseAddress(void);
#endif /* FLASH_H */
/*********************************** end of flash.h ************************************/

View File

@ -0,0 +1,257 @@
/************************************************************************************//**
* \file Source/_template/nvm.c
* \brief Bootloader non-volatile memory driver source file.
* \ingroup Target__template_nvm
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
/************************************************************************************//**
* \defgroup Target__template_nvm Non-volatile memory driver of a port
* \brief This module implements the non-volatile memory driver of a microcontroller
* port. Note that the default implementation if for a microcontroller that
* has internal flash memory. At the time of this writing pretty much all
* microcontrollers use flash EEPROM as non-volatile memory to store the
* program code. Assuming that this is also the case for the microcontroller
* for which the port is developed, nothing needs to be modified in this
* source file.
* \ingroup Target__template
****************************************************************************************/
/****************************************************************************************
* Include files
****************************************************************************************/
#include "boot.h" /* bootloader generic header */
#include "flash.h"
/****************************************************************************************
* Hook functions
****************************************************************************************/
#if (BOOT_NVM_HOOKS_ENABLE > 0)
extern void NvmInitHook(void);
extern void NvmReinitHook(void);
extern blt_int8u NvmWriteHook(blt_addr addr, blt_int32u len, blt_int8u *data);
extern blt_int8u NvmEraseHook(blt_addr addr, blt_int32u len);
extern blt_bool NvmDoneHook(void);
#endif
#if (BOOT_NVM_CHECKSUM_HOOKS_ENABLE > 0)
extern blt_bool NvmWriteChecksumHook(void);
extern blt_bool NvmVerifyChecksumHook(void);
#endif
/************************************************************************************//**
** \brief Initializes the NVM driver.
** \return none.
**
****************************************************************************************/
void NvmInit(void)
{
#if (BOOT_NVM_HOOKS_ENABLE > 0)
/* give the application a chance to initialize a driver for operating on NVM
* that is not by default supported by this driver.
*/
NvmInitHook();
#endif
/* init the internal driver */
FlashInit();
} /*** end of NvmInit ***/
/************************************************************************************//**
** \brief Reinitializes the NVM driver. This function is called at the start of each
** firmware update as opposed to NvmInit, which is only called once during
** power on.
** \return none.
**
****************************************************************************************/
void NvmReinit(void)
{
#if (BOOT_NVM_HOOKS_ENABLE > 0)
/* give the application a chance to re-initialize a driver for operating on NVM
* that is not by default supported by this driver.
*/
NvmReinitHook();
#endif
/* reinitialize the internal driver */
FlashReinit();
} /*** end of NvmReinit ***/
/************************************************************************************//**
** \brief Programs the non-volatile memory.
** \param addr Start address.
** \param len Length in bytes.
** \param data Pointer to the data buffer.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool NvmWrite(blt_addr addr, blt_int32u len, blt_int8u *data)
{
#if (BOOT_NVM_HOOKS_ENABLE > 0)
blt_int8u result = BLT_NVM_NOT_IN_RANGE;
#endif
#if (BOOT_NVM_HOOKS_ENABLE > 0)
/* give the application a chance to operate on memory that is not by default supported
* by this driver.
*/
result = NvmWriteHook(addr, len, data);
/* process the return code */
if (result == BLT_NVM_OKAY)
{
/* data was within range of the additionally supported memory and succesfully
* programmed, so we are all done.
*/
return BLT_TRUE;
}
else if (result == BLT_NVM_ERROR)
{
/* data was within range of the additionally supported memory and attempted to be
* programmed, but an error occurred, so we can't continue.
*/
return BLT_FALSE;
}
#endif
/* still here so the internal driver should try and perform the program operation */
return FlashWrite(addr, len, data);
} /*** end of NvmWrite ***/
/************************************************************************************//**
** \brief Erases the non-volatile memory.
** \param addr Start address.
** \param len Length in bytes.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool NvmErase(blt_addr addr, blt_int32u len)
{
#if (BOOT_NVM_HOOKS_ENABLE > 0)
blt_int8u result = BLT_NVM_NOT_IN_RANGE;
#endif
#if (BOOT_NVM_HOOKS_ENABLE > 0)
/* give the application a chance to operate on memory that is not by default supported
* by this driver.
*/
result = NvmEraseHook(addr, len);
/* process the return code */
if (result == BLT_NVM_OKAY)
{
/* address was within range of the additionally supported memory and succesfully
* erased, so we are all done.
*/
return BLT_TRUE;
}
else if (result == BLT_NVM_ERROR)
{
/* address was within range of the additionally supported memory and attempted to be
* erased, but an error occurred, so we can't continue.
*/
return BLT_FALSE;
}
#endif
/* still here so the internal driver should try and perform the erase operation */
return FlashErase(addr, len);
} /*** end of NvmErase ***/
/************************************************************************************//**
** \brief Verifies the checksum, which indicates that a valid user program is
** present and can be started.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool NvmVerifyChecksum(void)
{
#if (BOOT_NVM_CHECKSUM_HOOKS_ENABLE > 0)
/* check checksum using the application specific method. */
return NvmVerifyChecksumHook();
#else
/* check checksum using the interally supported method. */
return FlashVerifyChecksum();
#endif
} /*** end of NvmVerifyChecksum ***/
/************************************************************************************//**
** \brief Obtains the base address of the non-volatile memory available to the user
** program. This is typically that start of the vector table.
** \return Base address.
**
****************************************************************************************/
blt_addr NvmGetUserProgBaseAddress(void)
{
return FlashGetUserProgBaseAddress();
} /*** end of NvmGetUserProgBaseAddress ***/
/************************************************************************************//**
** \brief Once all erase and programming operations are completed, this
** function is called, so at the end of the programming session and
** right before a software reset is performed. It is used to calculate
** a checksum and program this into flash. This checksum is later used
** to determine if a valid user program is present in flash.
** \return BLT_TRUE if successful, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool NvmDone(void)
{
#if (BOOT_NVM_HOOKS_ENABLE > 0)
/* give the application's NVM driver a chance to finish up */
if (NvmDoneHook() == BLT_FALSE)
{
/* error so no need to continue */
return BLT_FALSE;
}
#endif
#if (BOOT_NVM_CHECKSUM_HOOKS_ENABLE > 0)
/* compute and write checksum, using the application specific method. */
if (NvmWriteChecksumHook() == BLT_FALSE)
{
return BLT_FALSE;
}
#else
/* compute and write checksum, which is programmed by the internal driver. */
if (FlashWriteChecksum() == BLT_FALSE)
{
return BLT_FALSE;
}
#endif
/* finish up internal driver operations */
return FlashDone();
} /*** end of NvmDone ***/
/*********************************** end of nvm.c **************************************/

View File

@ -0,0 +1,12 @@
/**
\defgroup Target__template Target Port Template
\ingroup Ports
\brief Target dependent code as a template for new microcontroller ports.
\details This module serves as a template to implement the bootloader's target
dependent part for a specific microcontroller family. It can be copied and
used as a foundation when developing new microcontroller ports. Note that
the parts of a port that need to be implemented are described as a source
code comment that starts with "TODO ##Port".
*/

View File

@ -0,0 +1,125 @@
/************************************************************************************//**
* \file Source/_template/timer.c
* \brief Bootloader timer driver source file.
* \ingroup Target__template_timer
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
/************************************************************************************//**
* \defgroup Target__template_timer Timer driver of a port
* \brief This module implements the timer memory driver of a microcontroller port.
* \details The timer driver implements a polling based 1 millisecond timer. It
* provides the time base for all timing related parts of the bootloader. The
* bootloader calls the function TimerUpdate() continuously to check if the
* next millisecond period passed.
* \ingroup Target__template
****************************************************************************************/
/****************************************************************************************
* Include files
****************************************************************************************/
#include "boot.h" /* bootloader generic header */
/****************************************************************************************
* Local data declarations
****************************************************************************************/
/** \brief Local variable for storing the number of milliseconds that have elapsed since
* startup.
*/
static blt_int32u millisecond_counter;
/************************************************************************************//**
** \brief Initializes the polling based millisecond timer driver.
** \return none.
**
****************************************************************************************/
void TimerInit(void)
{
/* Reset the timer configuration. */
TimerReset();
/* TODO ##Port Configure a timer peripheral such that 1 millisecond events can be
* detected. Note that the bootloader does not use interrupts, so this driver should
* also not generate timer related interrupts.
*/
/* Reset the millisecond counter value. */
millisecond_counter = 0;
} /*** end of TimerInit ***/
/************************************************************************************//**
** \brief Reset the timer by placing the timer back into it's default reset
** configuration.
** \return none.
**
****************************************************************************************/
void TimerReset(void)
{
/* TODO ##Port Set the timer peripheral back into the default reset value. */
} /* end of TimerReset */
/************************************************************************************//**
** \brief Updates the millisecond timer.
** \return none.
**
****************************************************************************************/
void TimerUpdate(void)
{
/* TODO ##Port Check with the timer peripheral if the 1 millisecond event occured. This
* is typically done by looking at a flag bit this is set by the timer peripheral. An
* alternative solution would use the timer peripheral's free running counter. Just
* keep in mind that with the latter case, you would have to store the free running
* counter value of the last time the millisecond event occured. This you can compare
* it with the current value of the free running counter to determine if a millisecond
* passed.
*/
if (1 == 0)
{
/* Increment the millisecond counter. */
millisecond_counter++;
}
} /*** end of TimerUpdate ***/
/************************************************************************************//**
** \brief Obtains the counter value of the millisecond timer.
** \return Current value of the millisecond timer.
**
****************************************************************************************/
blt_int32u TimerGet(void)
{
/* Updating timer here allows this function to be called in a loop with timeout
* detection.
*/
TimerUpdate();
/* Read and return the amount of milliseconds that passed since initialization. */
return millisecond_counter;
} /*** end of TimerGet ***/
/*********************************** end of timer.c ************************************/

View File

@ -0,0 +1,68 @@
/************************************************************************************//**
* \file Source/_template/types.h
* \brief Bootloader types header file.
* \ingroup Target__template_types
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
#ifndef TYPES_H
#define TYPES_H
/************************************************************************************//**
* \defgroup Target__template_types Type definitions of a port
* \brief This module implements the variable type definitions of a microcontroller
* port.
* \ingroup Target__template
****************************************************************************************/
/****************************************************************************************
* Macro definitions
****************************************************************************************/
/** \brief Boolean true value. */
#define BLT_TRUE (1)
/** \brief Boolean false value. */
#define BLT_FALSE (0)
/** \brief NULL pointer value. */
#define BLT_NULL ((void *)0)
/****************************************************************************************
* Type definitions
****************************************************************************************/
/* TODO ##Port Adjust the type definitions such that they match the microcontroller
* architecture for which this port is being developed. These type definitions are quite
* generic and can typically be left as is.
*/
typedef unsigned char blt_bool; /**< boolean type */
typedef char blt_char; /**< character type */
typedef unsigned long blt_addr; /**< memory address type */
typedef unsigned char blt_int8u; /**< 8-bit unsigned integer */
typedef signed char blt_int8s; /**< 8-bit signed integer */
typedef unsigned short blt_int16u; /**< 16-bit unsigned integer */
typedef signed short blt_int16s; /**< 16-bit signed integer */
typedef unsigned int blt_int32u; /**< 32-bit unsigned integer */
typedef signed int blt_int32s; /**< 32-bit signed integer */
#endif /* TYPES_H */
/*********************************** end of types.h ************************************/

View File

@ -0,0 +1,253 @@
/************************************************************************************//**
* \file Source/_template/uart.c
* \brief Bootloader UART communication interface source file.
* \ingroup Target__template_uart
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
/************************************************************************************//**
* \defgroup Target__template_uart RS232 UART driver of a port
* \brief This module implements the RS232 UART driver of a microcontroller port.
* \details For the most parts, this driver is already implemented. The only parts that
* need porting are the UART initialization, byte reception and byte
* transmission.
* \ingroup Target__template
****************************************************************************************/
/****************************************************************************************
* Include files
****************************************************************************************/
#include "boot.h" /* bootloader generic header */
#if (BOOT_COM_UART_ENABLE > 0)
/* TODO ##Port Include microcontroller peripheral driver header files here. */
/****************************************************************************************
* Macro definitions
****************************************************************************************/
/** \brief Timeout time for the reception of a CTO packet. The timer is started upon
* reception of the first packet byte.
*/
#define UART_CTO_RX_PACKET_TIMEOUT_MS (100u)
/** \brief Timeout for transmitting a byte in milliseconds. */
#define UART_BYTE_TX_TIMEOUT_MS (10u)
/****************************************************************************************
* Function prototypes
****************************************************************************************/
static blt_bool UartReceiveByte(blt_int8u *data);
static void UartTransmitByte(blt_int8u data);
/************************************************************************************//**
** \brief Initializes the UART communication interface.
** \return none.
**
****************************************************************************************/
void UartInit(void)
{
/* TODO ##Port Perform compile time assertion to check that the configured UART channel
* is actually supported by this driver. The example is for a driver where UART
* channels 0 - 2 are supported.
*/
ASSERT_CT((BOOT_COM_UART_CHANNEL_INDEX == 0) ||
(BOOT_COM_UART_CHANNEL_INDEX == 1) ||
(BOOT_COM_UART_CHANNEL_INDEX == 2));
/* TODO ##Port Configure and initialize the UART peripheral for the configured UART
* channel. The communication speed should be set to the value configured with
* BOOT_COM_UART_BAUDRATE. Further communication settings are: 8 databits, no parity,
* and 1 stopbit. Keep in mind that the bootloader runs in polling mode so without
* interrupts. For this reason make sure not to configure the UART peripheral for
* interrupt driven operation.
*/
} /*** end of UartInit ***/
/************************************************************************************//**
** \brief Transmits a packet formatted for the communication interface.
** \param data Pointer to byte array with data that it to be transmitted.
** \param len Number of bytes that are to be transmitted.
** \return none.
**
****************************************************************************************/
void UartTransmitPacket(blt_int8u *data, blt_int8u len)
{
blt_int16u data_index;
/* verify validity of the len-paramenter */
ASSERT_RT(len <= BOOT_COM_UART_TX_MAX_DATA);
/* first transmit the length of the packet */
UartTransmitByte(len);
/* transmit all the packet bytes one-by-one */
for (data_index = 0; data_index < len; data_index++)
{
/* keep the watchdog happy */
CopService();
/* write byte */
UartTransmitByte(data[data_index]);
}
} /*** end of UartTransmitPacket ***/
/************************************************************************************//**
** \brief Receives a communication interface packet if one is present.
** \param data Pointer to byte array where the data is to be stored.
** \param len Pointer where the length of the packet is to be stored.
** \return BLT_TRUE if a packet was received, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool UartReceivePacket(blt_int8u *data, blt_int8u *len)
{
static blt_int8u xcpCtoReqPacket[BOOT_COM_UART_RX_MAX_DATA+1]; /* one extra for length */
static blt_int8u xcpCtoRxLength;
static blt_bool xcpCtoRxInProgress = BLT_FALSE;
static blt_int32u xcpCtoRxStartTime = 0;
/* start of cto packet received? */
if (xcpCtoRxInProgress == BLT_FALSE)
{
/* store the message length when received */
if (UartReceiveByte(&xcpCtoReqPacket[0]) == BLT_TRUE)
{
if ( (xcpCtoReqPacket[0] > 0) &&
(xcpCtoReqPacket[0] <= BOOT_COM_UART_RX_MAX_DATA) )
{
/* store the start time */
xcpCtoRxStartTime = TimerGet();
/* reset packet data count */
xcpCtoRxLength = 0;
/* indicate that a cto packet is being received */
xcpCtoRxInProgress = BLT_TRUE;
}
}
}
else
{
/* store the next packet byte */
if (UartReceiveByte(&xcpCtoReqPacket[xcpCtoRxLength+1]) == BLT_TRUE)
{
/* increment the packet data count */
xcpCtoRxLength++;
/* check to see if the entire packet was received */
if (xcpCtoRxLength == xcpCtoReqPacket[0])
{
/* copy the packet data */
CpuMemCopy((blt_int32u)data, (blt_int32u)&xcpCtoReqPacket[1], xcpCtoRxLength);
/* done with cto packet reception */
xcpCtoRxInProgress = BLT_FALSE;
/* set the packet length */
*len = xcpCtoRxLength;
/* packet reception complete */
return BLT_TRUE;
}
}
else
{
/* check packet reception timeout */
if (TimerGet() > (xcpCtoRxStartTime + UART_CTO_RX_PACKET_TIMEOUT_MS))
{
/* cancel cto packet reception due to timeout. note that that automaticaly
* discards the already received packet bytes, allowing the host to retry.
*/
xcpCtoRxInProgress = BLT_FALSE;
}
}
}
/* packet reception not yet complete */
return BLT_FALSE;
} /*** end of UartReceivePacket ***/
/************************************************************************************//**
** \brief Receives a communication interface byte if one is present.
** \param data Pointer to byte where the data is to be stored.
** \return BLT_TRUE if a byte was received, BLT_FALSE otherwise.
**
****************************************************************************************/
static blt_bool UartReceiveByte(blt_int8u *data)
{
blt_bool result = BLT_FALSE;
/* TODO ##Port Check if a new byte was received on the configured channel. This is
* typically done by checking the reception register not empty flag. If a new byte
* was received, read it out and store it in '*data'. Next, clear the reception flag
* such that a new byte can be received again. Finally, set 'result' to BLT_TRUE to
* indicate to the caller of this function that a new byte was received and stored.
*/
if (1 == 0)
{
/* retrieve and store the newly received byte */
*data = 0;
/* update the result */
result = BLT_TRUE;
}
/* give the result back to the caller */
return result;
} /*** end of UartReceiveByte ***/
/************************************************************************************//**
** \brief Transmits a communication interface byte.
** \param data Value of byte that is to be transmitted.
** \return none.
**
****************************************************************************************/
static void UartTransmitByte(blt_int8u data)
{
blt_int32u timeout;
/* TODO ##Port Write the byte value in 'data' to the transmit register of the UART
* peripheral such that the transmission of the byte value is started.
*/
/* set timeout time to wait for transmit completion. */
timeout = TimerGet() + UART_BYTE_TX_TIMEOUT_MS;
/* TODO ##Port Wait in a loop, with timeout, until the UART peripheral reports that the
* data was successfully completed. This is typically done by reading out a transmit
* register empty flag.
*/
/* wait for tx holding register to be empty */
while (1 == 0)
{
/* keep the watchdog happy */
CopService();
/* break loop upon timeout. this would indicate a hardware failure. */
if (TimerGet() > timeout)
{
break;
}
}
} /*** end of UartTransmitByte ***/
#endif /* BOOT_COM_UART_ENABLE > 0 */
/*********************************** end of uart.c *************************************/

View File

@ -0,0 +1,532 @@
/************************************************************************************//**
* \file Source/_template/usb.c
* \brief Bootloader USB communication interface source file.
* \ingroup Target__template_usb
* \internal
*----------------------------------------------------------------------------------------
* C O P Y R I G H T
*----------------------------------------------------------------------------------------
* Copyright (c) 2019 by Feaser http://www.feaser.com All rights reserved
*
*----------------------------------------------------------------------------------------
* L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/
/************************************************************************************//**
* \defgroup Target__template_usb USB driver of a port
* \brief This module implements the USB driver of a microcontroller port.
* \details The USB driver makes user of bulk communications only, by means of two
* endpoints. This driver already implements FIFO buffers for the endpoint
* data including utility functions for managing these FIFOs. Note that the
* USB descriptor configuration and other enumeration related configuration
* is typically stored with the bootloader demo application and is not
* implemented in this driver directly.
* Basically, whenever the USB communication stack is available to transmit
* new data on the IN-endpoint, function UsbTransmitPipeBulkIN() can be
* called, which checks if there is data to transmit in the FIFO buffer and
* then needs to copy it to the IN-endpoint buffer. Whenever the USB
* communication stack signals that new data was received on the OUT-endpoint,
* UsbReceivePipeBulkOUT() can be called which then copies the data from the
* OUT-endpoint buffer into the FIFO buffer.
* \ingroup Target__template
****************************************************************************************/
/****************************************************************************************
* Include files
****************************************************************************************/
#include "boot.h" /* bootloader generic header */
#if (BOOT_COM_USB_ENABLE > 0)
#include "usb.h" /* USB bootloader driver */
/* TODO ##Port Include microcontroller peripheral driver header files here. */
/****************************************************************************************
* Configuration macros
****************************************************************************************/
/* For a USB bootloader, the backdoor needs to stay open long enough for the USB device
* to enumerate on the host PC. Therefore the default backdoor open time needs to be
* extended. Note that this won't be long enough for a first time USB driver install
* on the host PC. In this case the bootloader should be started with the backup backdoor
* that uses, for example, a digital input to force the bootloader to stay active. This
* can be implemented in CpuUserProgramStartHook(). Feel free to shorten/lengthen this
* time for finetuning. Note that adding this configuration macro to blt_conf.h overrides
* the value here.
*/
#ifndef BOOT_COM_USB_BACKDOOR_EXTENSION_MS
#define BOOT_COM_USB_BACKDOOR_EXTENSION_MS (2000)
#endif
/****************************************************************************************
* Macro definitions
****************************************************************************************/
/** \brief Total number of fifo buffers. */
#define FIFO_MAX_BUFFERS (2)
/** \brief Invalid value for a fifo buffer handle. */
#define FIFO_ERR_INVALID_HANDLE (255)
/** \brief Number of bytes that fit in the fifo pipe. */
#define FIFO_PIPE_SIZE (64)
/** \brief Endpoint IN & OUT Packet size. */
#define BULK_DATA_MAX_PACKET_SIZE (64)
/****************************************************************************************
* Type definitions
****************************************************************************************/
/** \brief Structure type for fifo control. */
typedef struct t_fifo_ctrl
{
blt_int8u *startptr; /**< pointer to start of buffer */
blt_int8u *endptr; /**< pointer to end of buffer */
blt_int8u *readptr; /**< pointer to next read location */
blt_int8u *writeptr; /**< pointer to next free location */
blt_int8u length; /**< number of buffer elements */
blt_int8u entries; /**< # of full buffer elements */
blt_int8u handle; /**< handle of the buffer */
struct t_fifo_ctrl *fifoctrlptr; /**< pointer to free buffer control */
} tFifoCtrl;
/** \brief Structure type for a fifo pipe. */
typedef struct
{
blt_int8u handle; /**< fifo handle */
blt_int8u data[FIFO_PIPE_SIZE]; /**< fifo data buffer */
} tFifoPipe; /**< USB pipe fifo type */
/****************************************************************************************
* Function prototypes
****************************************************************************************/
static blt_bool UsbReceiveByte(blt_int8u *data);
static blt_bool UsbTransmitByte(blt_int8u data);
static void UsbFifoMgrInit(void);
static blt_int8u UsbFifoMgrCreate(blt_int8u *buffer, blt_int8u length);
static blt_bool UsbFifoMgrWrite(blt_int8u handle, blt_int8u data);
static blt_bool UsbFifoMgrRead(blt_int8u handle, blt_int8u *data);
static blt_int8u UsbFifoMgrScan(blt_int8u handle);
/****************************************************************************************
* Local data declarations
****************************************************************************************/
/** \brief Local variable that holds the fifo control structures. */
static tFifoCtrl fifoCtrl[FIFO_MAX_BUFFERS];
/** \brief Local pointer that points to the next free fifo control structure. */
static tFifoCtrl *fifoCtrlFree;
/** \brief Fifo pipe used for the bulk in endpoint. */
static tFifoPipe fifoPipeBulkIN;
/** \brief Fifo pipe used for the bulk out endpoint. */
static tFifoPipe fifoPipeBulkOUT;
/************************************************************************************//**
** \brief Initializes the USB communication interface.
** \return none.
**
****************************************************************************************/
void UsbInit(void)
{
/* initialize the FIFO manager */
UsbFifoMgrInit();
/* place 2 buffers under FIFO management */
fifoPipeBulkIN.handle = UsbFifoMgrCreate(fifoPipeBulkIN.data, FIFO_PIPE_SIZE);
fifoPipeBulkOUT.handle = UsbFifoMgrCreate(fifoPipeBulkOUT.data, FIFO_PIPE_SIZE);
/* validate fifo handles */
ASSERT_RT((fifoPipeBulkIN.handle != FIFO_ERR_INVALID_HANDLE) && \
(fifoPipeBulkOUT.handle != FIFO_ERR_INVALID_HANDLE));
/* TODO ##Port Initialize the USB communication stack library. */
/* inform application about the connect event */
UsbConnectHook(BLT_TRUE);
/* extend the time that the backdoor is open in case the default timed backdoor
* mechanism is used.
*/
#if (BOOT_BACKDOOR_HOOKS_ENABLE == 0)
if (BackDoorGetExtension() < BOOT_COM_USB_BACKDOOR_EXTENSION_MS)
{
BackDoorSetExtension(BOOT_COM_USB_BACKDOOR_EXTENSION_MS);
}
#endif /* BOOT_BACKDOOR_HOOKS_ENABLE == 0 */
} /*** end of UsbInit ***/
/************************************************************************************//**
** \brief Releases the USB communication interface.
** \return none.
**
****************************************************************************************/
void UsbFree(void)
{
/* TODO ##Port Unitialize the USB communication stack library. */
/* inform application about the disconnect event */
UsbConnectHook(BLT_FALSE);
} /*** end of UsbFree ***/
/************************************************************************************//**
** \brief Transmits a packet formatted for the communication interface.
** \param data Pointer to byte array with data that it to be transmitted.
** \param len Number of bytes that are to be transmitted.
** \return none.
**
****************************************************************************************/
void UsbTransmitPacket(blt_int8u *data, blt_int8u len)
{
blt_int16u data_index;
blt_bool result;
/* verify validity of the len-paramenter */
ASSERT_RT(len <= BOOT_COM_USB_TX_MAX_DATA);
/* first transmit the length of the packet */
result = UsbTransmitByte(len);
ASSERT_RT(result == BLT_TRUE);
/* transmit all the packet bytes one-by-one */
for (data_index = 0; data_index < len; data_index++)
{
/* keep the watchdog happy */
CopService();
/* write byte */
result = UsbTransmitByte(data[data_index]);
ASSERT_RT(result == BLT_TRUE);
}
} /*** end of UsbTransmitPacket ***/
/************************************************************************************//**
** \brief Receives a communication interface packet if one is present.
** \param data Pointer to byte array where the data is to be stored.
** \param len Pointer where the length of the packet is to be stored.
** \return BLT_TRUE if a packet was received, BLT_FALSE otherwise.
**
****************************************************************************************/
blt_bool UsbReceivePacket(blt_int8u *data, blt_int8u *len)
{
static blt_int8u xcpCtoReqPacket[BOOT_COM_USB_RX_MAX_DATA+1]; /* one extra for length */
static blt_int8u xcpCtoRxLength;
static blt_bool xcpCtoRxInProgress = BLT_FALSE;
/* TODO ##Port Poll USB interrupt flags to process USB related events. This is a good
* location for this, since this function is called continuously by the bootloader.
*/
/* start of cto packet received? */
if (xcpCtoRxInProgress == BLT_FALSE)
{
/* store the message length when received */
if (UsbReceiveByte(&xcpCtoReqPacket[0]) == BLT_TRUE)
{
if ( (xcpCtoReqPacket[0] > 0) &&
(xcpCtoReqPacket[0] <= BOOT_COM_USB_RX_MAX_DATA) )
{
/* indicate that a cto packet is being received */
xcpCtoRxInProgress = BLT_TRUE;
/* reset packet data count */
xcpCtoRxLength = 0;
}
}
}
else
{
/* store the next packet byte */
if (UsbReceiveByte(&xcpCtoReqPacket[xcpCtoRxLength+1]) == BLT_TRUE)
{
/* increment the packet data count */
xcpCtoRxLength++;
/* check to see if the entire packet was received */
if (xcpCtoRxLength == xcpCtoReqPacket[0])
{
/* copy the packet data */
CpuMemCopy((blt_int32u)data, (blt_int32u)&xcpCtoReqPacket[1], xcpCtoRxLength);
/* done with cto packet reception */
xcpCtoRxInProgress = BLT_FALSE;
/* set the packet length */
*len = xcpCtoRxLength;
/* packet reception complete */
return BLT_TRUE;
}
}
}
/* packet reception not yet complete */
return BLT_FALSE;
} /*** end of UsbReceivePacket ***/
/************************************************************************************//**
** \brief Receives a communication interface byte if one is present.
** \param data Pointer to byte where the data is to be stored.
** \return BLT_TRUE if a byte was received, BLT_FALSE otherwise.
**
****************************************************************************************/
static blt_bool UsbReceiveByte(blt_int8u *data)
{
blt_bool result;
/* obtain data from the fifo */
result = UsbFifoMgrRead(fifoPipeBulkOUT.handle, data);
return result;
} /*** end of UsbReceiveByte ***/
/************************************************************************************//**
** \brief Transmits a communication interface byte.
** \param data Value of byte that is to be transmitted.
** \return BLT_TRUE if the byte was transmitted, BLT_FALSE otherwise.
**
****************************************************************************************/
static blt_bool UsbTransmitByte(blt_int8u data)
{
blt_bool result;
/* write data from to fifo */
result = UsbFifoMgrWrite(fifoPipeBulkIN.handle, data);
return result;
} /*** end of UsbTransmitByte ***/
/************************************************************************************//**
** \brief Checks if there is still data left to transmit and if so submits it
** for transmission with the USB endpoint.
** \return none.
**
****************************************************************************************/
void UsbTransmitPipeBulkIN(void)
{
/* USB_Tx_Buffer is static for run-time optimalization */
static blt_int8u USB_Tx_Buffer[BULK_DATA_MAX_PACKET_SIZE];
blt_int8u nr_of_bytes_for_tx_endpoint;
blt_int8u byte_counter;
blt_int8u byte_value;
blt_bool result;
/* read how many bytes should be transmitted */
nr_of_bytes_for_tx_endpoint = UsbFifoMgrScan(fifoPipeBulkIN.handle);
/* only continue if there is actually data left to transmit */
if (nr_of_bytes_for_tx_endpoint == 0)
{
return;
}
/* make sure to not transmit more than the USB endpoint can handle */
if (nr_of_bytes_for_tx_endpoint > BULK_DATA_MAX_PACKET_SIZE)
{
nr_of_bytes_for_tx_endpoint = BULK_DATA_MAX_PACKET_SIZE;
}
/* copy the transmit data to the transmit buffer */
for (byte_counter=0; byte_counter < nr_of_bytes_for_tx_endpoint; byte_counter++)
{
/* obtain data from the fifo */
result = UsbFifoMgrRead(fifoPipeBulkIN.handle, &byte_value);
ASSERT_RT(result == BLT_TRUE);
/* store it in the endpoint's RAM */
USB_Tx_Buffer[byte_counter] = byte_value;
}
/* TODO ##Port Copy data to IN-endpoint's RAM and start the transmission. The data is
* located in local array 'USB_Tx_Buffer' and the number of bytes is stored in
* 'nr_of_bytes_for_tx_endpoint'
*/
} /*** end of UsbTransmitPipeBulkIN ***/
/************************************************************************************//**
** \brief Stores data that was received on the Bulk OUT pipe in the fifo.
** \return none.
**
****************************************************************************************/
void UsbReceivePipeBulkOUT(void)
{
blt_int16u USB_Rx_Cnt=0;
blt_int8u *usbRxBufferPtr;
blt_int16u byte_counter;
blt_bool result;
/* TODO ##Port Get the number of received bytes and set the pointer to where the
* received bytes are currently located.
*/
usbRxBufferPtr = BLT_NULL;
USB_Rx_Cnt = 0;
/* USB data will be immediately processed, this allows next USB traffic being
* NAKed till the end of the USART Xfer
*/
for (byte_counter=0; byte_counter<USB_Rx_Cnt; byte_counter++)
{
/* add the data to the fifo */
result = UsbFifoMgrWrite(fifoPipeBulkOUT.handle, usbRxBufferPtr[byte_counter]);
/* verify that the fifo wasn't full */
ASSERT_RT(result == BLT_TRUE);
}
/* TODO ##Port Prepare the OUT endpoint to receive next packet. */
} /*** end of UsbReceivePipeBulkOUT ***/
/************************************************************************************//**
** \brief Initializes the fifo manager. Each controlled fifo is assigned a
** unique handle, which is the same as its index into fifoCtrl[]. Each
** controlled fifo holds a pointer to the next free fifo control.
** For the last fifo in fifoCtrl[] this one is set to a null-pointer as
** an out of fifo control indicator. Function should be called once
** before any of the other fifo management functions are called.
** \return none.
**
****************************************************************************************/
static void UsbFifoMgrInit(void)
{
blt_int8u i;
tFifoCtrl *pbc1, *pbc2;
pbc1 = &fifoCtrl[0];
pbc2 = &fifoCtrl[1];
/* assign fifo handles and pointer to next free fifo */
for (i = 0; i < (FIFO_MAX_BUFFERS - 1); i++)
{
pbc1->handle = i;
pbc1->fifoctrlptr = pbc2;
pbc1++;
pbc2++;
}
/* initialize handle for the last one and use null-pointer for the next free fifo */
pbc1->handle = i;
pbc1->fifoctrlptr = (tFifoCtrl *)0;
fifoCtrlFree = &fifoCtrl[0];
} /*** end of UsbFifoMgrInit ***/
/************************************************************************************//**
** \brief Places a data storage array under fifo management control. A handle
** for identifying the fifo in subsequent fifo management function
** calls is returned, if successful.
** \param buffer Pointer to the first element in the data storage fifo.
** \param length Maximum number of data elements that can be stored in the fifo.
** \return Fifo handle if successfull, or FIFO_ERR_INVALID_HANDLE.
**
****************************************************************************************/
static blt_int8u UsbFifoMgrCreate(blt_int8u *buffer, blt_int8u length)
{
tFifoCtrl *pbc;
/* first determine if these is still a free fifo control available */
if (fifoCtrlFree == (tFifoCtrl *)0)
{
return FIFO_ERR_INVALID_HANDLE;
}
/* store pointer to free fifo and update pointer to next free one */
pbc = fifoCtrlFree;
fifoCtrlFree = pbc->fifoctrlptr;
/* initialize the buffer control */
pbc->length = length;
pbc->readptr = buffer;
pbc->writeptr = buffer;
pbc->entries = 0;
pbc->startptr = buffer;
pbc->endptr = (blt_int8u *)(buffer + length - 1);
/* return the handle to the successfully created fifo control */
return pbc->handle;
} /*** end of UsbFifoMgrCreate ***/
/************************************************************************************//**
** \brief Stores data in the fifo.
** \param handle Identifies the fifo to write data to.
** \param data Pointer to the data that is to be written to the fifo.
** \return BLT_TRUE if the data was successfully stored in the fifo, BLT_FALSE
** otherwise.
**
****************************************************************************************/
static blt_bool UsbFifoMgrWrite(blt_int8u handle, blt_int8u data)
{
/* check the validity of the handle parameter */
ASSERT_RT(handle < FIFO_MAX_BUFFERS);
/* check if fifo is full */
if (fifoCtrl[handle].entries == fifoCtrl[handle].length)
{
return BLT_FALSE;
}
/* copy data to fifo */
*fifoCtrl[handle].writeptr = data;
/* data written so update number of entries */
fifoCtrl[handle].entries++;
/* update write pointer */
fifoCtrl[handle].writeptr++;
/* check end of fifo */
if (fifoCtrl[handle].writeptr > fifoCtrl[handle].endptr)
{
/* set write pointer to start of the cyclic fifo */
fifoCtrl[handle].writeptr = fifoCtrl[handle].startptr;
}
/* still here so all is okay */
return BLT_TRUE;
} /*** end of UsbFifoMgrWrite ***/
/************************************************************************************//**
** \brief Retrieves data from the fifo.
** \param handle Identifies the fifo to read data from.
** \param data Pointer to where the read data is to be stored.
** \return BLT_TRUE if the data was successfully read from the fifo, BLT_FALSE
** otherwise.
**
****************************************************************************************/
static blt_bool UsbFifoMgrRead(blt_int8u handle, blt_int8u *data)
{
/* check the validity of the handle parameter */
ASSERT_RT(handle < FIFO_MAX_BUFFERS);
/* check if fifo is empty */
if (fifoCtrl[handle].entries == 0)
{
return BLT_FALSE;
}
/* read the data */
*data = *fifoCtrl[handle].readptr;
/* data read so update number of entries */
fifoCtrl[handle].entries--;
/* update read pointer */
fifoCtrl[handle].readptr++;
/* check end of fifo */
if (fifoCtrl[handle].readptr > fifoCtrl[handle].endptr)
{
/* set read pointer to start of the cyclic fifo */
fifoCtrl[handle].readptr = fifoCtrl[handle].startptr;
}
/* still here so all is good */
return BLT_TRUE;
} /*** end of UsbFifoMgrRead ***/
/************************************************************************************//**
** \brief Returns the number of data entries currently present in the fifo.
** \param handle Identifies the fifo that is to be scanned.
** \return Number of data entries in the fifo if successful, otherwise 0.
**
****************************************************************************************/
static blt_int8u UsbFifoMgrScan(blt_int8u handle)
{
/* check the validity of the handle parameter */
ASSERT_RT(handle < FIFO_MAX_BUFFERS);
/* read and return the number of data entries */
return fifoCtrl[handle].entries;
} /*** end of UsbFifoMgrScan ***/
#endif /* BOOT_COM_USB_ENABLE > 0 */
/*********************************** end of usb.c **************************************/