(Last Mod: 04 November 2010 18:14:40 )
Bookmarks on this page
Files
Source Code (ANSI-C) v1.1
Executable (WinXP) v1.1
A ROM (Read Only Memory) contains data. Nothing more complicated than that. That data can mean all kinds of things, but at the end of the day it is just a bunch of data. Embedded systems, such as microcontrollers, almost always use some sort of non-volatile memory to store the programs (called firmware) that runs on the microcontroller. Similarly, devices such as FPGAs (Field Programmable Gate Arrays) are often coupled with some sort of ROM device to hold the configuration information for the FPGA since most FPGAs do not (yet) incorporate non-volatile memory.
The final output of most development tools for embedded systems is nothing more than the data that must be downloaded into the ROM associated with the embedded devices -- this is generally referred to as the "firmware image". The simplest format for this output is a binary file that is simply a verbatim copy of the data that needs to be written to the Flash ROM, byte for byte, nothing more, nothing less. While the simplest format, it has the disadvantage that the entire contents of the ROM has to be in the file (some tools aren't this strict, but in general this is the case). This has three significant disadvantages: First, you have to reprogram the entire ROM even if you are only changing a few bytes. Not only can this be slow, but typically ROMs can only be rewritten a fairly small number of times before they become unreliable. Second, the ROM frequently contains several different sections of data and it is not uncommon to work with the different sections separately and it would be convenient to be able to reprogram only those sections that being modified and, more to the point, have some assurance that the other sections are not being modified. Third, a binary file is very hard for humans to work with and it also contains no integrity information to guard against corruption; in most embedded systems, even a single bit error is likely to be catastophic.
To help address these issues, Motorola developed a format for representing a binary image in an ASCII text file that has been widely adopted. This format is known as the Motorola S-Record Format, and although the files should probably be referred to as SREC files, they are almost universally referred to s19 files for reasons that will be evident shortly. A similar, but different, format that serves a similar purpose is the Intel HEX file, which will not be discussed here.
In an SREC file (as well as Intel HEX), each Data Record explicityly contains the starting address for the data in that record. Because of this, and because a Data Record can contain as little as a single byte of data, the reader can told to only write to the specific addresses included in that file while leaving all of the other ROM data intact. This makes it trivial to separate portions of the overall ROM image in separate files and to change only the one of interest.
An SREC files consists of a series of lines, each of which known as an "S-Record" There are presently eight defined S-Record types as follows:
S0 - Contains vender-specific information
S1 - Data Block using a 2-byte address (64KB address space)
S2 - Data Block using a 3-byte address (16MB address space)
S3 - Data Block using a 4-byte address (4GB address space)
S4 - Unused (will likely be a Data Block using a 6-byte address) (256TB address space)
S5 - Record Count
S6 - Unused (will likely be a Block Termination using a 6-byte address)
S7 - Block Termination using a 4-byte address
S8 - Block Termination using a 3-byte address
S9 - Block Termination using a 2-byte address
Which types of records must appear in a file and in what order is not specified, but certain simple common conventions are widely observed. Among them are the following:
Any and all S0 records will appear first in the file.
All Data Blocks should be of the same type.
All Data Blocks should be ordered according to the address contained.
A single S5 record should appear after the last Data Block records and before the Block Termination record.
A single Block Termination record should be the last line in the file.
It should be noted, however, that none of these are required and a good reader should be tolerant of deviations. In particular, S5 and Block End records are frequently omitted; Data Blocks of different types are sometimes included; and S0 records may appear in other places, especially at the end of the file. In general, unless a reader encounters something that indicates uncertainty about the data that needs to be written to the ROM, it should proceed to process the file, though producing warnings is reasonable.
For 64KB and smaller ROMS, an SREC file nominally consists of a bunch of S1 records followed by an S9 record, hence the name s19 file. SREC files for ROMs up to 16MB should arguably be, and frequently are, called s28 files, while the largest SREC files presently defined are known as s37 files. However, the term "s19" has become so widespread that it refers to all three types. This can cause problems because there are lots of programs out there that only recognize S1 and S9 records and do not know how to deal with S2 or S3 records.
All S-Records have the following in common:
All characters are case-insensitive.
All values are represented in hexadecimal.
All records start with a two-character Type field (specifically the characters "S1" through "S9").
The Type field is followed by a two-character Byte Count field.
All records end with a two-character Checksum field.
The Byte Count field is the number of bytes in the remainder of the records, the Type field and Byte Count fields are not included but the Checksum field is.
The Checksum field is the 1's compliment of the modulo-256 sum of all bytes in the record between (and not including) the Type field and the Checksum field. More simply put, sum up all of the bytes in the record starting with the Byte Count and ending with the last byte prior to the Checksum field and keep only the least significant byte. Finally, invert all of the bits. Alternatively, subtract the least significant byte from 255. The result is the Checksum.
The contents between the Byte Count and the Checksum Fields varies according to the record type as follows:
S0 records can contain any data up to 254 bytes (due to the Byte Count).
Data Block records contain an address (Big Endian) followed by data bytes to written starting at that address.
S5 records contain the number of Data Block records as a two-byte (Big Endian) value.
Block Termination records only contain an address, generally the Start Address.
The following are the first few and last few records from the SREC file for the ProxMark3 Summer 2009 bootrom
S01200006F626A2F626F6F74726F6D2E73313976
S31500000000060000EA0C0000EA0C0000EA0C0000EA18
S315000000100C0000EA0C0000EA0C0000EA050000EA09
S3150000002008D09FE50A0000EB04309FE513FF2FE19F
...
S315000017D0006E0074000000008315200087152000AD
S309000017E09F1520002B
S70500100000EA
Parsing this into fields, we have:
S0 12 00006F626A2F626F6F74726F6D2E733139 76
S3 15 00000000 060000EA0C0000EA0C0000EA0C0000EA 18
S3 15 00000010 0C0000EA0C0000EA0C0000EA050000EA 09
S3 15 00000020 08D09FE50A0000EB04309FE513FF2FE1 9F
...
S3 15 000017D0 006E0074000000008315200087152000 AD
S3 09 000017E0 9F152000 2B
S7 05 00100000 EA
As you can see, there is no S5 record. The meaning of the address value (1MB) in the S7 record is unknown, as is the meaning of the data in the S0 record. However, none of this is relevant provided it is not required by a particular programmer. All that matter are the S3 records. Turning our attention to those, you can see that all but the first one has a Byte Count of 0x15, which is 21. Since S3 records have a 4-byte address and a fifth byte is used for the Checksum, this means that 16 (0x10) bytes of data are present on each line, which is consistent with the incrementing of the addresses from record to record. For simplicity, we can look at the S7 record to see that the Checksum is, indeed, as described. The only two non-zero bytes included in the sum are 0x05 and 0x10, which total to 0x15. Subtracting this from 0xFF yields 0xEA, the same result obtained by inverting all of the bits in 0x15.
If you run the utility, srec2bin, without any command line arguments it will display the Help Screen.
The Source Code is not very well documented at this point, nor is the code particularly well structured. Keep in mind that it was written in about three hours. There is a fair amount of error-trapping, but certainly not an exhaustive amount.
If the program flow looks a bit strange, it is probably because I wrote this program in a manner that doesn't require extensive or variable amounts of memory. The SREC files are read one character at a time and all decisions are based upon the present state and the new character. No previous characters are retained. It is essentially what is called a finite automaton (though I would have to be a little flexible in what constitutes the "state" of the machine and do some reorganization if someone really wanted to be strict about it). I did it this way because I have a friend that recently asked for a non-trivial example of using such a machine.