Need help with ST 32F4 SPI code (ex HAL) - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: July 27, 2021, 06:15:03 pm
content: In the Cube IDE library there is this functionCode:/*** @brief Transmit and Receive an amount of data in blocking mode.* @param hspi pointer to a SPI_HandleTypeDef structure that contains* the configuration information for SPI module.* @param pTxData pointer to transmission data buffer* @param pRxData pointer to reception data buffer* @param Size amount of data to be sent and received* @param Timeout Timeout duration* @retval HAL status*/HAL_StatusTypeDef HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,uint32_t Timeout){uint16_t initial_TxXferCount;uint32_t tmp_mode;HAL_SPI_StateTypeDef tmp_state;uint32_t tickstart;/* Variable used to alternate Rx and Tx during transfer */uint32_t txallowed = 1U;HAL_StatusTypeDef errorcode = HAL_OK;/* Check Direction parameter */assert_param(IS_SPI_DIRECTION_2LINES(hspi->Init.Direction));/* Process Locked */__HAL_LOCK(hspi);/* Init tickstart for timeout management*/tickstart = HAL_GetTick();/* Init temporary variables */tmp_state = hspi->State;tmp_mode = hspi->Init.Mode;initial_TxXferCount = Size;if (!((tmp_state == HAL_SPI_STATE_READY) || \((tmp_mode == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES) && (tmp_state == HAL_SPI_STATE_BUSY_RX)))){errorcode = HAL_BUSY;goto error;}if ((pTxData == NULL) || (pRxData == NULL) || (Size == 0U)){errorcode = HAL_ERROR;goto error;}/* Don't overwrite in case of HAL_SPI_STATE_BUSY_RX */if (hspi->State != HAL_SPI_STATE_BUSY_RX){hspi->State = HAL_SPI_STATE_BUSY_TX_RX;}/* Set the transaction information */hspi->ErrorCode = HAL_SPI_ERROR_NONE;hspi->pRxBuffPtr = (uint8_t *)pRxData;hspi->RxXferCount = Size;hspi->RxXferSize = Size;hspi->pTxBuffPtr = (uint8_t *)pTxData;hspi->TxXferCount = Size;hspi->TxXferSize = Size;/*Init field not used in handle to zero */hspi->RxISR = NULL;hspi->TxISR = NULL;#if (USE_SPI_CRC != 0U)/* Reset CRC Calculation */if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE){SPI_RESET_CRC(hspi);}#endif /* USE_SPI_CRC *//* Check if the SPI is already enabled */if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE){/* Enable SPI peripheral */__HAL_SPI_ENABLE(hspi);}/* Transmit and Receive data in 16 Bit mode */if (hspi->Init.DataSize == SPI_DATASIZE_16BIT){if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U)){hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);hspi->pTxBuffPtr += sizeof(uint16_t);hspi->TxXferCount--;}while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U)){/* Check TXE flag */if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)) && (hspi->TxXferCount > 0U) && (txallowed == 1U)){hspi->Instance->DR = *((uint16_t *)hspi->pTxBuffPtr);hspi->pTxBuffPtr += sizeof(uint16_t);hspi->TxXferCount--;/* Next Data is a reception (Rx). Tx not allowed */txallowed = 0U;#if (USE_SPI_CRC != 0U)/* Enable CRC Transmission */if ((hspi->TxXferCount == 0U) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)){SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);}#endif /* USE_SPI_CRC */}/* Check RXNE flag */if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) && (hspi->RxXferCount > 0U)){*((uint16_t *)hspi->pRxBuffPtr) = (uint16_t)hspi->Instance->DR;hspi->pRxBuffPtr += sizeof(uint16_t);hspi->RxXferCount--;/* Next Data is a Transmission (Tx). Tx is allowed */txallowed = 1U;}if (((HAL_GetTick() - tickstart) >= Timeout) && (Timeout != HAL_MAX_DELAY)){errorcode = HAL_TIMEOUT;goto error;}}}/* Transmit and Receive data in 8 Bit mode */else{if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U)){*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);hspi->pTxBuffPtr += sizeof(uint8_t);hspi->TxXferCount--;}while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U)){/* Check TXE flag */if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)) && (hspi->TxXferCount > 0U) && (txallowed == 1U)){*(__IO uint8_t *)&hspi->Instance->DR = (*hspi->pTxBuffPtr);hspi->pTxBuffPtr++;hspi->TxXferCount--;/* Next Data is a reception (Rx). Tx not allowed */txallowed = 0U;#if (USE_SPI_CRC != 0U)/* Enable CRC Transmission */if ((hspi->TxXferCount == 0U) && (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)){SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);}#endif /* USE_SPI_CRC */}/* Wait until RXNE flag is reset */if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) && (hspi->RxXferCount > 0U)){(*(uint8_t *)hspi->pRxBuffPtr) = hspi->Instance->DR;hspi->pRxBuffPtr++;hspi->RxXferCount--;/* Next Data is a Transmission (Tx). Tx is allowed */txallowed = 1U;}if ((((HAL_GetTick() - tickstart) >= Timeout) && ((Timeout != HAL_MAX_DELAY))) || (Timeout == 0U)){errorcode = HAL_TIMEOUT;goto error;}}}#if (USE_SPI_CRC != 0U)/* Read CRC from DR to close CRC calculation process */if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE){/* Wait until TXE flag */if (SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_RXNE, SET, Timeout, tickstart) != HAL_OK){/* Error on the CRC reception */SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_CRC);errorcode = HAL_TIMEOUT;goto error;}/* Read CRC */READ_REG(hspi->Instance->DR);}/* Check if CRC error occurred */if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_CRCERR)){SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_CRC);/* Clear CRC Flag */__HAL_SPI_CLEAR_CRCERRFLAG(hspi);errorcode = HAL_ERROR;}#endif /* USE_SPI_CRC *//* Check the end of the transaction */if (SPI_EndRxTxTransaction(hspi, Timeout, tickstart) != HAL_OK){errorcode = HAL_ERROR;hspi->ErrorCode = HAL_SPI_ERROR_FLAG;goto error;}/* Clear overrun flag in 2 Lines communication mode because received is not read */if (hspi->Init.Direction == SPI_DIRECTION_2LINES){__HAL_SPI_CLEAR_OVRFLAG(hspi);}error :hspi->State = HAL_SPI_STATE_READY;__HAL_UNLOCK(hspi);return errorcode;}It makes a provision for every possible operating mode, which I don't need, but more to the point it seems to have a timeout on things which cannot possibly fail unless a) the SPI is not configured or b) the silicon is defective.It also needs a timer tick i.e. interrupts running.I am doing a boot loader which doesn't have interrupts and just needs a simple byte mode, so I cut it down as follows:Code:HAL_StatusTypeDef B_HAL_SPI_TransmitReceive(SPI_HandleTypeDef *hspi, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size,uint32_t Timeout){uint16_t initial_TxXferCount;uint32_t tmp_mode;HAL_SPI_StateTypeDef tmp_state;// uint32_t tickstart;/* Variable used to alternate Rx and Tx during transfer */uint32_t txallowed = 1U;HAL_StatusTypeDef errorcode = HAL_OK;/* Init temporary variables */tmp_state = hspi->State;tmp_mode = hspi->Init.Mode;initial_TxXferCount = Size;if (!((tmp_state == HAL_SPI_STATE_READY) || \((tmp_mode == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES) && (tmp_state == HAL_SPI_STATE_BUSY_RX)))){errorcode = HAL_BUSY;goto error;}if ((pTxData == NULL) || (pRxData == NULL) || (Size == 0U)){errorcode = HAL_ERROR;goto error;}/* Don't overwrite in case of HAL_SPI_STATE_BUSY_RX */if (hspi->State != HAL_SPI_STATE_BUSY_RX){hspi->State = HAL_SPI_STATE_BUSY_TX_RX;}/* Set the transaction information */hspi->ErrorCode = HAL_SPI_ERROR_NONE;hspi->pRxBuffPtr = (uint8_t *)pRxData;hspi->RxXferCount = Size;hspi->RxXferSize = Size;hspi->pTxBuffPtr = (uint8_t *)pTxData;hspi->TxXferCount = Size;hspi->TxXferSize = Size;/*Init field not used in handle to zero */hspi->RxISR = NULL;hspi->TxISR = NULL;/* Check if the SPI is already enabled */if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE){/* Enable SPI peripheral */__HAL_SPI_ENABLE(hspi);}/* Transmit and Receive data in 8 Bit mode */if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U)){*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);hspi->pTxBuffPtr += sizeof(uint8_t);hspi->TxXferCount--;}while ((hspi->TxXferCount > 0U) || (hspi->RxXferCount > 0U)){/* Check TXE flag */if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)) && (hspi->TxXferCount > 0U) && (txallowed == 1U)){*(__IO uint8_t *)&hspi->Instance->DR = (*hspi->pTxBuffPtr);hspi->pTxBuffPtr++;hspi->TxXferCount--;/* Next Data is a reception (Rx). Tx not allowed */txallowed = 0U;}/* Wait until RXNE flag is reset */if ((__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)) && (hspi->RxXferCount > 0U)){(*(uint8_t *)hspi->pRxBuffPtr) = hspi->Instance->DR;hspi->pRxBuffPtr++;hspi->RxXferCount--;/* Next Data is a Transmission (Tx). Tx is allowed */txallowed = 1U;}}/* Clear overrun flag in 2 Lines communication mode because received is not read */if (hspi->Init.Direction == SPI_DIRECTION_2LINES){__HAL_SPI_CLEAR_OVRFLAG(hspi);}error :hspi->State = HAL_SPI_STATE_READY;return errorcode;}Is this correct? It does work!There is no need for a timeout because there is nothing useful to do if this function hangs. I wonder if some other error conditions are similarly superfluous. In fact I am pretty confused about a lot of the ST code... SPI is supposed to be quite simple - like a UART. TX empty = put a byte in. RX not empty = take a byte out. One difference is that SPI always transmits and receives concurrently, using the common clock.The function needs to be blocking, in my code, because while I could usefully do something else, it gets too complicated. AFAICT these functions wait for the operation to finish.Many thanks for looking at this bloated code!Similarly I "simplified" these two functions:(timeout is no longer used)Code:HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout){// uint32_t tickstart;HAL_StatusTypeDef errorcode = HAL_OK;uint16_t initial_TxXferCount;initial_TxXferCount = Size;if (hspi->State != HAL_SPI_STATE_READY){errorcode = HAL_BUSY;goto error;}if ((pData == NULL) || (Size == 0U)){errorcode = HAL_ERROR;goto error;}/* Set the transaction information */hspi->State = HAL_SPI_STATE_BUSY_TX;hspi->ErrorCode = HAL_SPI_ERROR_NONE;hspi->pTxBuffPtr = (uint8_t *)pData;hspi->TxXferSize = Size;hspi->TxXferCount = Size;/*Init field not used in handle to zero */hspi->pRxBuffPtr = (uint8_t *)NULL;hspi->RxXferSize = 0U;hspi->RxXferCount = 0U;hspi->TxISR = NULL;hspi->RxISR = NULL;/* Configure communication direction : 1Line */if (hspi->Init.Direction == SPI_DIRECTION_1LINE){SPI_1LINE_TX(hspi);}/* Check if the SPI is already enabled */if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE){/* Enable SPI peripheral */__HAL_SPI_ENABLE(hspi);}/* Transmit data in 8 Bit mode */if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (initial_TxXferCount == 0x01U)){*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);hspi->pTxBuffPtr += sizeof(uint8_t);hspi->TxXferCount--;}while (hspi->TxXferCount > 0U){/* Wait until TXE flag is set to send data */if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE)){*((__IO uint8_t *)&hspi->Instance->DR) = (*hspi->pTxBuffPtr);hspi->pTxBuffPtr += sizeof(uint8_t);hspi->TxXferCount--;}}/* Clear overrun flag in 2 Lines communication mode because received is not read */if (hspi->Init.Direction == SPI_DIRECTION_2LINES){__HAL_SPI_CLEAR_OVRFLAG(hspi);}if (hspi->ErrorCode != HAL_SPI_ERROR_NONE){errorcode = HAL_ERROR;}error:hspi->State = HAL_SPI_STATE_READY;return errorcode;}HAL_StatusTypeDef HAL_SPI_Receive(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout){// uint32_t tickstart;HAL_StatusTypeDef errorcode = HAL_OK;if ((hspi->Init.Mode == SPI_MODE_MASTER) && (hspi->Init.Direction == SPI_DIRECTION_2LINES)){hspi->State = HAL_SPI_STATE_BUSY_RX;/* Call transmit-receive function to send Dummy data on Tx line and generate clock on CLK line */return B_HAL_SPI_TransmitReceive(hspi, pData, pData, Size, Timeout);}if (hspi->State != HAL_SPI_STATE_READY){errorcode = HAL_BUSY;goto error;}if ((pData == NULL) || (Size == 0U)){errorcode = HAL_ERROR;goto error;}/* Set the transaction information */hspi->State = HAL_SPI_STATE_BUSY_RX;hspi->ErrorCode = HAL_SPI_ERROR_NONE;hspi->pRxBuffPtr = (uint8_t *)pData;hspi->RxXferSize = Size;hspi->RxXferCount = Size;/*Init field not used in handle to zero */hspi->pTxBuffPtr = (uint8_t *)NULL;hspi->TxXferSize = 0U;hspi->TxXferCount = 0U;hspi->RxISR = NULL;hspi->TxISR = NULL;/* Configure communication direction: 1Line */if (hspi->Init.Direction == SPI_DIRECTION_1LINE){SPI_1LINE_RX(hspi);}/* Check if the SPI is already enabled */if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE){/* Enable SPI peripheral */__HAL_SPI_ENABLE(hspi);}/* Receive data in 8 Bit mode *//* Transfer loop */while (hspi->RxXferCount > 0U){/* Check the RXNE flag */if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_RXNE)){/* read the received data */(* (uint8_t *)hspi->pRxBuffPtr) = *(__IO uint8_t *)&hspi->Instance->DR;hspi->pRxBuffPtr += sizeof(uint8_t);hspi->RxXferCount--;}}if (hspi->ErrorCode != HAL_SPI_ERROR_NONE){errorcode = HAL_ERROR;}error :hspi->State = HAL_SPI_STATE_READY;return errorcode;}

author: SiliconWizard
author_rank: Super Contributor
timestamp: July 27, 2021, 06:30:55 pm
content: Have you enabled a watchdog? Although a time-out may not be useful in your case if you don't know how to handle it if it happens, then it would make sense to at least have a watchdog enabled. While blocking during a SPI transfer shouldn't happen in master mode under normal conditions, it could if anything goes wrong internally.Otherwise, nothing wrong optimizing HAL functions for your particular use. Yes they are written to cover a large range of cases and conditions.

author: peter-h
author_rank: Super Contributor
timestamp: July 27, 2021, 06:40:57 pm
content: Yes, there is a watchdog, but if this SPI stuff hangs it will just take you back to the same place.The SPI is always a master in this case, and the receive mode always clocks-in the byte, so "it cannot possbly hang".

author: SiliconWizard
author_rank: Super Contributor
timestamp: July 27, 2021, 07:33:21 pm
content: Quote from: peter-h on July 27, 2021, 06:40:57 pmAs I said, unless something goes wrong inside the MCU, which can happen for a number of reasons. For instance, some EMI. Letting a MCU "hang" forever is never good, especially for users. Some conditions such as EMI or ESD could get the SPI peripheral (/or the whole peripheral bus) to "hang". A watchdog will restart the MCU in this case and get things back to normal. Worst case would be a permanent condition, but it's far less probable than a temporary one. Yes it DOES happen in the field. Everything that can go wrong eventually will. You can count on this.Handling time-outs in code has the added benefit of giving you an opportunity to take specific actions (if there is any that makes sense), or even just log the event in Flash or something. Can always be useful to know if that indeed happens while the device operates.For I2C, even in master mode, handling time-outs is even more critical than with SPI (for the latter, as I said, it would mainly be due to a hardware malfunction.)A I2C transaction can hang forever if one of the slaves on the bus holds the clock (SCL) signal low for some reason. Even when you're master.As a more general rule, unless a watchdog reset is appropriate to deal with this, which it is in many cases, I add a time-out in all potentially infinite loops (anything waiting for a condition.)

author: peter-h
author_rank: Super Contributor
timestamp: July 27, 2021, 07:39:00 pm
content: Does the code I posted make sense?Re errors which I don't think are possible:if ((pTxData == NULL) || (pRxData == NULL) || (Size == 0U)){errorcode = HAL_ERROR;goto error;}

author: SiliconWizard
author_rank: Super Contributor
timestamp: July 27, 2021, 08:04:38 pm
content: Quote from: peter-h on July 27, 2021, 07:39:00 pmFrom a quick look, it seems OK.Quote from: peter-h on July 27, 2021, 07:39:00 pmThey of course are possible. The function could be called with invalid parameters. Here, those are parameters that are indentified as being invalid.You may think that you will only ever call this function with "constant" parameters, and thus can guarantee they are always valid, but this function could be called with variables instead, and those variables could be invalid at some point if there's any bug in your code.The above is called "parameter validation", and something I highly recommend doing in every situation, and only omit in extremely specific ones. I took a habit of adding parameter validation to all functions I write. Exceptions can be: when there is actually NO incorrect parameter for a given function:A very simple example would be:Code:double PlusOne(double x){return x + 1.0;}obviously there is no possible invalid value for 'x'.Other exceptions would be when the function is a 'helper' function that is strictly only called inside another function in which, by construction, the paramters cannot be invalid. But even in this case, not doing parameter validation (for optimization reasons for instance) doesn't necessarily help anything, because such helper functions are very likely to get automatically inlined by the compiler, which can statically infer that parameters are always valid (would always make the paramter validation tests pass), and thus would optimize out those tests anyway.Paramter validation is mandated by MISRA-C if I'm not mistaken, and ST claims that the HAL is MISRA-C compliant, so no surprise they would add parameter validation to all their functions too.

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 27, 2021, 08:12:15 pm
content: As you said, being the master you have nothing to worry about, there's nothing that could stop you from sending the clocks unless the spi hangs.So why so much bothering when you can just put a huge number there and forget about it? Use HAL_MAX_DELAY as parameter which is about 49 days.But that's a STM32. Anything could happen. It could hang, tear your clothes apart while trying finding the problem, then in a couple of months st releases an updated errata file adding a new section where the spi would hang in certain conditions.So it's never a bad thing. You probably know how much time it shoudl take, so put 10x value or whatever and if by any means it doesn't do the job in time, reset the spi peripheral and try again.If you open cubeMX and make any changes, it will regenerate the code, wiping all your work. So put them somewhere outside the HAL files!

author: peter-h
author_rank: Super Contributor
timestamp: July 27, 2021, 08:16:39 pm
content: OK how about this bit/* Check the end of the transaction */if (SPI_EndRxTxTransaction(hspi, Timeout, tickstart) != HAL_OK){errorcode = HAL_ERROR;hspi->ErrorCode = HAL_SPI_ERROR_FLAG;goto error;}static HAL_StatusTypeDef SPI_EndRxTxTransaction(SPI_HandleTypeDef *hspi, uint32_t Timeout, uint32_t Tickstart){/* Control the BSY flag */if (SPI_WaitFlagStateUntilTimeout(hspi, SPI_FLAG_BSY, RESET, Timeout, Tickstart) != HAL_OK){SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);return HAL_TIMEOUT;}return HAL_OK;}To me it looks like the operation is finished at that point.

author: peter-h
author_rank: Super Contributor
timestamp: July 27, 2021, 09:19:08 pm
content: "If you open cubeMX and make any changes, it will regenerate the code, wiping all your work. So put them somewhere outside the HAL files!"Yes; I am not using the code generator feature of this program. That part of the project was done by someone else. I actually doubt that anybody can produce properly working non-trivial projects using the Cube code generator. It is handy for generating code snippets which you then put somewhere else, and for the config for the clock divider chain.My hope for this project is to get it all working and debugged and then once the API is done (which I have spent a huge amount of time testing) it will never need to be touched again at low level.The hardware reference is ~300 pages and I read every page and the PCB worked first time. The software manual is ~2000 pages and I don't think anybody read it in that way. Perhaps one should...

author: DavidAlfa
author_rank: Super Contributor
timestamp: July 28, 2021, 01:11:19 am
content: They really need to make another 3000 pages manual for the HAL library. Because it sucks.The actual one is like: "HAL_SPI_BlahBlah" -> Does "BlahBlah". Oh, really? Nice catch!What to call after this to stop the thing? What to do when it returns "Error", "Busy", or whatever? How to interpret the Handler struct, what does each member do or mean?The actual way to learn HAL is by studying the HAL source code, then debug a lot of hours trying to fix the problems that will appear for sure!The datasheet itself is just a quick description. I think I only use it for the pinouts and the AC/DC characteristics! There's only 10 lines barely explaining each peripheral, and little register information if any.It might look a lot, but after all you'll see it's not that much. You'll go to the peripheral section you're interested in, and after reading you'll always want more!

author: peter-h
author_rank: Super Contributor
timestamp: July 28, 2021, 04:41:29 pm
content: Yes, but the "reality of life" is that almost nobody has the time to read that much stuff. The 300 page chip manualhasto be read whole if you are doing a PCB, however. I found all kinds of small but crucial details in there...What STM should have done is provided a much more granular code generator. It is crazy to have so many lines of code just to write a byte to SPI. They get away with this because at 168MHz the bottlenecks are generally elsewhere, and the tiny % of users who need max speed do their own code.I agree one should validate input values (my mistake to quote the wrong bit of code above) but some of the stuff in that code looks to me like pointless checking of something which can't happen. But then I admit I don't understand what they are doing a lot of the time.