scc 2026.07
SystemC components library
ihex.cpp
1
2#include "ihex.h"
3#include <array>
4#include <cstdint>
5#include <cstring>
6#include <iomanip>
7#include <ostream>
8#include <stdbool.h>
9#include <stdexcept>
10#include <stdint.h>
11#include <vector>
12
13// IHEX file parser state machine
14enum {
15 START_CODE_STATE = 0,
16 BYTE_COUNT_0_STATE = 1,
17 BYTE_COUNT_1_STATE = 2,
18 ADDR_0_STATE = 3,
19 ADDR_1_STATE = 4,
20 ADDR_2_STATE = 5,
21 ADDR_3_STATE = 6,
22 RECORD_TYPE_0_STATE = 7,
23 RECORD_TYPE_1_STATE = 8,
24 DATA_STATE = 9,
25 CHECKSUM_0_STATE = 10,
26 CHECKSUM_1_STATE = 11
27};
28enum record_type_e { DATA, END_OF_FILE, EXTENDED_SEGMENT_ADDRESS, START_SEGMENT_ADDRESS, EXTENDED_LINEAR_ADDRESS, START_LINEAR_ADDRESS };
29
30#define INVALID_HEX_CHAR 'x'
31
32namespace util {
33namespace ihex {
34namespace {
35uint8_t hex2dec(uint8_t h) {
36 if(h >= '0' && h <= '9')
37 return h - '0';
38 else if(h >= 'A' && h <= 'F')
39 return h - 'A' + 0xA;
40 else if(h >= 'a' && h <= 'z')
41 return h - 'a' + 0xA;
42 else
43 return INVALID_HEX_CHAR;
44}
45
46static uint8_t ihex_checksum(const std::vector<uint8_t>& bytes) {
47 // Checksum is two's complement of the LSB of the sum
48 uint32_t sum = 0;
49 for(uint8_t b : bytes)
50 sum += b;
51 return static_cast<uint8_t>(0u - static_cast<uint8_t>(sum));
52}
53
54static void write_record(std::ostream& os, uint8_t len, uint16_t addr, uint8_t type, const uint8_t* data) {
55 std::vector<uint8_t> payload;
56 payload.reserve(4 + len);
57 payload.push_back(len);
58 payload.push_back(static_cast<uint8_t>((addr >> 8) & 0xFF));
59 payload.push_back(static_cast<uint8_t>(addr & 0xFF));
60 payload.push_back(type);
61 for(uint8_t i = 0; i < len; ++i)
62 payload.push_back(data[i]);
63
64 uint8_t csum = ihex_checksum(payload);
65
66 os << ':' << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(len) << std::setw(4)
67 << static_cast<int>(addr) << std::setw(2) << static_cast<int>(type);
68
69 for(uint8_t i = 0; i < len; ++i) {
70 os << std::setw(2) << static_cast<int>(data[i]);
71 }
72
73 os << std::setw(2) << static_cast<int>(csum) << "\n";
74}
75
76} // namespace
77
78bool parse(std::istream& is, std::function<bool(uint64_t, uint64_t, const uint8_t*)> cb) {
79 char c;
80 uint8_t i;
81 unsigned state{0};
82 uint16_t address_lo{0};
83 uint16_t address_hi{0};
84 std::array<uint8_t, IHEX_DATA_SIZE> data;
85 unsigned data_size_in_nibble{0};
86 uint8_t byte_count{0};
87 uint8_t record_type{DATA};
88 uint8_t calculated_cs{0}; // calculate checksum
89 uint8_t saved_high_nibble{0};
90 bool save_high_nibble{true};
91 bool ex_segment_addr_mode{false};
92 while(!is.eof()) {
93 is.get(c);
94 if(c == '\0')
95 return true;
96 if(state) {
97 if((i = hex2dec(c)) == INVALID_HEX_CHAR)
98 return false;
99 if(state <= CHECKSUM_1_STATE) {
100 if(save_high_nibble)
101 saved_high_nibble = i;
102 else
103 calculated_cs += (saved_high_nibble << 4) | i;
104 save_high_nibble = !save_high_nibble;
105 }
106 }
107 switch(state) {
108 case START_CODE_STATE:
109 calculated_cs = 0x00;
110 save_high_nibble = true;
111 if(c == '\r' || c == '\n') {
112 continue;
113 } else if(c == ':') {
114 byte_count = 0;
115 record_type = DATA;
116 address_lo = 0x0000;
117 memset(data.data(), 0, data.size());
118 data_size_in_nibble = 0;
119 calculated_cs = 0;
120 ++state;
121 } else
122 return false;
123 break;
124 case BYTE_COUNT_0_STATE:
125 case BYTE_COUNT_1_STATE:
126 byte_count = (byte_count << 4) | i;
127 ++state;
128 break;
129 case ADDR_0_STATE:
130 case ADDR_1_STATE:
131 case ADDR_2_STATE:
132 case ADDR_3_STATE:
133 address_lo = ((address_lo << 4) | i); // only alter lower 16-bit address
134 ++state;
135 break;
136 case RECORD_TYPE_0_STATE:
137 if(i != 0)
138 return false;
139 ++state;
140 break;
141 case RECORD_TYPE_1_STATE:
142 if(i > START_LINEAR_ADDRESS)
143 return false;
144 record_type = i;
145 if(byte_count == 0) {
146 state = CHECKSUM_0_STATE;
147 } else if(byte_count > sizeof(data))
148 return false;
149 else
150 ++state;
151 break;
152 case DATA_STATE: {
153 uint8_t b_index = data_size_in_nibble / 2;
154 data[b_index] = (data[b_index] << 4) | i;
155
156 ++data_size_in_nibble;
157 if((data_size_in_nibble / 2) >= byte_count)
158 ++state;
159 break;
160 }
161 case CHECKSUM_0_STATE:
162 ++state;
163 break;
164 case CHECKSUM_1_STATE:
165 if((byte_count << 1) != data_size_in_nibble) // Check whether byte count field match the data size
166 return false;
167 if(calculated_cs != 0x00)
168 return false;
169 if(record_type == EXTENDED_SEGMENT_ADDRESS) { // Set extended segment addresss
170 address_hi = ((uint16_t)data[0] << 8) | (data[1]);
171 ex_segment_addr_mode = true;
172 } else if(record_type == EXTENDED_LINEAR_ADDRESS) { // Set linear addresss
173 address_hi = ((uint16_t)data[0] << 8) | (data[1]);
174 ex_segment_addr_mode = false;
175 } else if(record_type == DATA && cb) {
176 uint64_t address = ex_segment_addr_mode ? (static_cast<uint32_t>(address_hi) << 4) + static_cast<uint32_t>(address_lo)
177 : (static_cast<uint32_t>(address_hi) << 16) | static_cast<uint32_t>(address_lo);
178 if(!cb(address, data_size_in_nibble >> 1, data.data()))
179 return false;
180 }
181 state = START_CODE_STATE;
182 break;
183 default:
184 return false;
185 }
186 }
187 return true;
188}
189
190void dump(std::ostream& os, nonstd::span<uint8_t> const& bytes, uint32_t base_address, uint32_t record_len) {
191 if(record_len == 0 || record_len > 255) {
192 throw std::invalid_argument("record_len must be 1..255");
193 }
194
195 uint32_t addr = base_address;
196 uint32_t upper = 0xFFFFFFFFu; // force first extended address if needed
197
198 size_t i = 0;
199 while(i < bytes.size()) {
200 uint32_t new_upper = (addr >> 16) & 0xFFFF;
201
202 // Emit Extended Linear Address record when upper 16 bits change
203 if(new_upper != upper) {
204 upper = new_upper;
205 uint8_t ext[2] = {static_cast<uint8_t>((upper >> 8) & 0xFF), static_cast<uint8_t>(upper & 0xFF)};
206 write_record(os, 2, 0x0000, 0x04, ext);
207 }
208
209 uint16_t low = static_cast<uint16_t>(addr & 0xFFFF);
210
211 // Don't cross a 64KiB boundary inside a data record
212 size_t max_before_boundary = 0x10000u - low;
213 size_t chunk = record_len;
214 if(chunk > bytes.size() - i)
215 chunk = bytes.size() - i;
216 if(chunk > max_before_boundary)
217 chunk = max_before_boundary;
218
219 // Copy to uint8_t buffer for record writer
220 std::vector<uint8_t> buf(chunk);
221 for(size_t k = 0; k < chunk; ++k)
222 buf[k] = static_cast<uint8_t>(bytes[i + k]);
223
224 write_record(os, static_cast<uint8_t>(chunk), low, 0x00, buf.data());
225
226 i += chunk;
227 addr += static_cast<uint32_t>(chunk);
228 }
229
230 // End-of-file record
231 write_record(os, 0, 0x0000, 0x01, nullptr);
232}
233
234} // namespace ihex
235} // namespace util
A utility class for parsing Intel Hex (IHEX) files.
Definition ihex.cpp:33
bool parse(std::istream &is, std::function< bool(uint64_t, uint64_t, const uint8_t *)> cb)
Parses an IHEX file from the given input stream and invokes a callback function for each data record ...
Definition ihex.cpp:78
SCC common utilities.
Definition bit_field.h:30