This week was one where a big setback was experienced in the stack of code for bitstream reading. The stack was then analysed to find the overhead in the code so that the target read times can be achieved. Other than that, some more modifications were made to improve the overall functionality of the program. It was certainly an eventful week.
A big Woops!
The error in the current code structure was found when the bitstream upload stack was being tested for speed optimization. While uploading test files, it was noticed that for any file, whenever the PIC reset itself after the upload, the same amount of time was taken to read the corresponding file. For the current test bitstream, this was expected as the filesize (denoted by the first 4 bytes of the bitstream itself), however, some older test files have a very large number when the first 4 bytes are combined to a value (one file had “4F A0 DC 23” which is actually the decimal number 1335942179, a far greater number than our usual bitstream size of 350kB). Even though it would take the PIC a considerable amount of time to attempt to read this amount of bytes, the LED indications still showed that the same amount of time was taken. This was verified using a Timer in the PIC and checking the value read out of the timer. The value was always around the same. The code was then analyzed using the debugger and that is when the blunder reared it ugly head.
It turned out that in the “for” loop used to read the data, the loop-counter was set as an integer which by default is a 16-bit value, whereas the filesize counter was a 32 bit value. Thus whenever the loop-counter overflowed (reached a value of FFFF) the loop exited and indicated that the read process was completed. Correcting the loop-counter caused the read process to correctly complete (the individual bytes of the last sector were verified using the debugger) but caused the overall process to slow down. Using the PIC’s timer, the time measured to read the whole bitsteam was found to be around 5.1s. Far too slow for the desired application.
Deeper Analysis of the bitstream read times
Having isolated the problem, it was required to analyze the writing process to figure out where exactly is the overhead for the reading stack. The code was broken down to analyze the time taken for each section of the process; to read the bytes from the SD card, to transmit about 350kB of data through the UART(this was tested by sending the character “a” for the same amount as the filesize). The PIC’s internal timer was used to measure the time taken for each process.
It was clear that the problem was caused by the amount of time the PIC needed to run the loop to complete the read process. So, this amount was reduced by reading more bytes at a time. The times visibly decreased and using only 8-bit counters in nested loop provided an improvement on the read times. This was however not a strong enough improvement so the loop put to the test by using an incremental number of bytes being read per loop. The following graph illustrates the read times obtained.
It is evident from this analysis that the number of bytes read per loop was a factor but not a decisive one. The best times able to be extracted from the tests was only about 4.1s. The results were discussed with the supervisor and the general consensus was that the overhead in this process is actually the time taken to call the read function and then to call the “putc1USART” function.
Prospective solutions and improvements
The supervisor advised on rewriting the Read-Transmit process in in-line assembly instead. The general idea is to read straight from the SPI buffer and send the data to the transmit register of the UART. Doing this for most part of the reading process will ensure that the PIC performs minimal amount of instructions to execute the desired process.
The final design to be written in assembly is as follows:
It is hoped that this will yield better speed times than the current version. The task is hoped to be completed in the coming days.
Minor modifications to the FSInit
One more problem address this week was the requirement of CRC at unnecessary instances during the execution of the code. For instance, under the current conditions, to even read out the MBR or the data from the SD card, a proper CRC-7 byte must be sent to the SD card, otherwise the process will fail. Considering that for most SD cards, the information in the MBR as well as the information on the boot sector (when the SD card is formatted) may vary greatly, so it will be impossible to hard code in any CRC values without a standard to follow. Following discussions with the supervisor, it was decided that the FSInit function should be modified to switch the CRC On or Off, when needed. In fairness, the main purpose of the CRC is the data packet checksum for when the data is being transferred over the ethernet. So, for most of the PIC to SD card communication related purposes, the CRC poses more of a hassle then protection.
The FSInit function was modified to take in a Boolean True or False argument to indicate the desired CRC status. If the CRC is required, a TRUE argument is fed in and FALSE is fed in when the CRC is not needed. This modification also helped standardize a few functions in the SD-SPI stack which where merged together, separated by conditions to save code space. The current flash memory is at about 69kB and it is hoped that more unnecessary code, as well as similar multiple instances of codes can be merged to greatly save the flash memory space.