はじめに
ROMライターでマイコンやフラッシュメモリへバイナリデータを書き込むフォーマット、Motorola S-recordについてまとめる。また、Pythonでフォーマット変換するコードも書いてみた。
目次
Motorola S-recordフォーマット
Motorolaによって開発されたフォーマットで拡張子は.mot
。ASCIIコードの16進数のビックエンディアンで表現するテキストフォーマット。「S」から始まるS-Recordという構造で、「Type」によって、後に続くフォーマットが規定されている。
パラメータ | 内容 |
---|---|
S | 各行の先頭に付ける識別子。 |
Type:0 | Option:ASCIIコードのNULL終端の文字列(16進数表記)。 |
Type:1 | 16bitアドレスのデータレコード。 |
Type:2 | 24bitアドレスのデータレコード。 |
Type:3 | 32bitアドレスのデータレコード。 |
Type:4 | Reserved |
Type:5 | Option:データレコードのカウント(65,535 (0xFFFF)まで)。Addressに表記。Dataなし。 |
Type:6 | Option:データレコードのカウント(16,777,215 (0xFFFFFF) まで)。Addressに表記。Dataなし。 |
Type:7 | S3と対で使う。32bitの終端アドレス。Addressに表記。Dataなし。 |
Type:8 | S2と対で使う。24bitの終端アドレス。Addressに表記。Dataなし。 |
Type:9 | S1と対で使う。16bitの終端アドレス。Addressに表記。Dataなし。 |
Length | AddressとDataとCheckSumの桁数の和を2で割った数(1Byte=2桁) |
Address | Type別に下記の通り。 S0 : 0000固定 S1 : 4桁:Dataを書き込む先頭アドレス XXXX(16bit Address) S2 : 6桁:Dataを書き込む先頭アドレス XXXXXX(24bit Address) S3 : 8桁:Dataを書き込む先頭アドレス XXXXXXXX(32bit Address) S9 : 4桁:終端アドレス XXXX(16bit Address) S8 : 6桁:終端アドレス XXXXXX(24bit Address) S7 : 8桁:終端アドレス XXXXXXXX(32bit Address) S5 : レコードの総数 0xFFFFまで S6 : レコードの総数 0xFFFFFFまで |
Data | 書き込むデータ。2桁で 1Byte。S0の場合は書き込むデータではない。 |
CheckSum | LengthとAddressとDataの各バイトの合計の1の補数。 2文字(1バイト)データ。レコード長、アドレスフィールド、データの各バイト値の合計の1の補数。 計算式はPythonの変換コードの例を参照。 |
Motorola S-record変換例
例として、データ列からS-Recordフォーマットのデータに変換するPythonコードを書いてみた。アドレスフォーマット(32bit,24bit,16bit)、開始アドレス、書き込みデータ列、1レコードのサイズを指定。関数名mot_srecord()
。
data ='\
000102030405060708090A0B0C0D0E0F\
101112131415161718191A1B1C1D1E1F\
202122232425262728292A2B2C2D2E2F\
303132333435363738393A3B3C3D3E3F\
404142434445464748494A4B4C4D4E4F\
505152535455565758595A5B5C5D5E5F\
606162636465666768696A6B6C6D6E6F\
707172737475767778797A7B7C7D7E7F\
808182838485868788898A8B8C8D8E8F\
909192939495969798999A9B9C9D9E9F\
A0A1A2A3A4A5A6A7A8A9AAABACADAEAF\
B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF\
C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF\
D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF\
E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF\
F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF\
ABCDEF\
'
#------------------------------------------------------
# AddrType : アドレスのビット数を指定。(16,24,32)
# StartAddr : 開始アドレスを16進数数値で指定。
# Data : 書き込むデータをHEX文字列で指定。
# SplitBytes : S-RecordのサイズをByte数で指定。
#------------------------------------------------------
def mot_srecord(AddrType, StartAddr, Data, SplitBytes):
#S-Record1行分のデータ毎にリストに分割
data_list = [Data[i:i+SplitBytes*2] for i in range(0,len(Data), SplitBytes*2)]
# データレコード作成
for cnt,data in enumerate(data_list):
if(AddrType == 32):
#StartAddrからoffset(cnt*SplitBytes)を足して8桁のHEX文字列にする。
addr = f'{(StartAddr+(cnt*SplitBytes)&0xFFFFFFFF):08X}'
#addr桁とdata桁とchecksumの2桁を足して2で割って(byte数なので)2桁のHEX文字列にする。
length = f'{((len(addr)+len(data)+2)//2):02X}'
#sum_dataの16進数文字列を2文字ずつ切り取り数値変換してsum()し、1の補数をとる。
sum_data = length+addr+data
checksum = f'{(0xFF&~sum([int(sum_data[i:i+2],16) for i in range(0,len(sum_data), 2)])):02X}'
#ヘッダをつけてS-Recordフォーマットにする
data_list[cnt]=f'S3{length}{addr}{data}{checksum}\n'
elif(AddrType == 24):
addr = f'{(StartAddr+(cnt*SplitBytes)&0xFFFFFF):06X}'
length = f'{((len(addr)+len(data)+2)//2):02X}'
sum_data = length+addr+data
checksum = f'{(0xFF&~sum([int(sum_data[i:i+2],16) for i in range(0,len(sum_data), 2)])):02X}'
data_list[cnt]=f'S2{length}{addr}{data}{checksum}\n'
elif(AddrType == 16):
addr = f'{(StartAddr+(cnt*SplitBytes)&0xFFFF):04X}'
length = f'{((len(addr)+len(data)+2)//2):02X}'
sum_data = length+addr+data
checksum = f'{(0xFF&~sum([int(sum_data[i:i+2],16) for i in range(0,len(sum_data), 2)])):02X}'
data_list[cnt]=f'S1{length}{addr}{data}{checksum}\n'
return ''.join(data_list)
# data列を32bitアドレスの0x80000000を開始アドレスとして、16byte1レコードのS-Recordデータに変換。
print(mot_srecord(32,0x80000000,data,16))
# S31580000000000102030405060708090A0B0C0D0E0FF2
# S31580000010101112131415161718191A1B1C1D1E1FE2
# S31580000020202122232425262728292A2B2C2D2E2FD2
# S31580000030303132333435363738393A3B3C3D3E3FC2
# S31580000040404142434445464748494A4B4C4D4E4FB2
# S31580000050505152535455565758595A5B5C5D5E5FA2
# S31580000060606162636465666768696A6B6C6D6E6F92
# S31580000070707172737475767778797A7B7C7D7E7F82
# S31580000080808182838485868788898A8B8C8D8E8F72
# S31580000090909192939495969798999A9B9C9D9E9F62
# S315800000A0A0A1A2A3A4A5A6A7A8A9AAABACADAEAF52
# S315800000B0B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF42
# S315800000C0C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF32
# S315800000D0D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF22
# S315800000E0E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF12
# S315800000F0F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF02
# S30880000100ABCDEF0F
# data列を16bitアドレスの0x1000を開始アドレスとして、16byte1レコードのS-Recordデータに変換。
print(mot_srecord(16,0x1000,data,16))
# S1131000000102030405060708090A0B0C0D0E0F64
# S1131010101112131415161718191A1B1C1D1E1F54
# S1131020202122232425262728292A2B2C2D2E2F44
# S1131030303132333435363738393A3B3C3D3E3F34
# S1131040404142434445464748494A4B4C4D4E4F24
# S1131050505152535455565758595A5B5C5D5E5F14
# S1131060606162636465666768696A6B6C6D6E6F04
# S1131070707172737475767778797A7B7C7D7E7FF4
# S1131080808182838485868788898A8B8C8D8E8FE4
# S1131090909192939495969798999A9B9C9D9E9FD4
# S11310A0A0A1A2A3A4A5A6A7A8A9AAABACADAEAFC4
# S11310B0B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFB4
# S11310C0C0C1C2C3C4C5C6C7C8C9CACBCCCDCECFA4
# S11310D0D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF94
# S11310E0E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF84
# S11310F0F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF74
# S1061100ABCDEF81