Fletchgen
The Fletcher Design Generator
srec.cc
1 // Copyright 2018-2019 Delft University of Technology
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "fletchgen/srec/srec.h"
16 #include <fletcher/common.h>
17 #include <cstdlib>
18 #include <sstream>
19 #include <string>
20 #include <algorithm>
21 
22 namespace fletchgen::srec {
23 
24 Record::Record(Type type, uint32_t address, const uint8_t *data, size_t size)
25  : type_(type), size_(size), address_(address) {
26  // Throw if size is too large.
27  if (size > MAX_DATA_BYTES) {
28  throw std::domain_error("SREC Record size cannot exceed " + std::to_string(MAX_DATA_BYTES) + " bytes.");
29  }
30  if (size_ > 0) {
31  // Allocate buffer for data
32  data_ = static_cast<uint8_t *>(calloc(1, size_));
33  // Copy data over
34  memcpy(data_, data, size);
35  }
36 }
37 
39  if (size_ > 0) {
40  free(data_);
41  }
42 }
43 
44 Record Record::Header(const std::string &header_str, uint16_t address) {
45  auto str = header_str.substr(0, std::max(MAX_DATA_BYTES, header_str.size()));
46  return Record(Record::HEADER, address, (const uint8_t *) (str.c_str()), str.size());
47 }
48 
49 uint8_t Record::byte_count() {
50  return address_width() + size_ + 1;
51 }
52 
53 int Record::address_width() {
54  switch (type_) {
55  case DATA24: return 3;
56  case COUNT24: return 3;
57  case TERM24: return 3;
58  case DATA32: return 4;
59  case TERM32: return 4;
60  default:return 2;
61  }
62 }
63 
64 uint8_t Record::checksum() {
65  uint32_t sum = 0;
66  // Byte count
67  sum += byte_count();
68  // Address
69  if (address_width() > 3) sum += (address_ & 0xFF000000u) >> 24u;
70  if (address_width() > 2) sum += (address_ & 0x00FF0000u) >> 16u;
71  sum += (address_ & 0x0000FF00u) >> 8u;
72  sum += (address_ & 0x000000FFu);
73  // Data
74  for (size_t i = 0; i < size_; i++)
75  sum += data_[i];
76  // Keep the least significant byte
77  auto ret = static_cast<uint8_t>(sum & 0xFFu);
78  // Get one's complement
79  ret = ~ret;
80  // Return 1's complement
81  return ret;
82 }
83 
84 std::string Record::ToString(bool line_feed) {
85  // String stream to build output
86  std::stringstream output;
87  // Record type
88  output << 'S' << std::to_string(type_);
89  // Byte count
90  PutHex(output, byte_count());
91  // Address
92  PutHex(output, address_, 2 * address_width());
93  // Data
94  for (size_t i = 0; i < size_; i++) {
95  PutHex(output, data_[i]);
96  }
97  // Checksum
98  PutHex(output, checksum());
99  // Line feed
100  if (line_feed) {
101  output << std::endl;
102  }
103  return output.str();
104 }
105 
106 std::optional<Record> Record::FromString(const std::string &line) {
107  // TODO(johanpel): make this function not break on big endian systems
108  size_t offset = 0;
109  Record ret(RESERVED, 0, nullptr, 0);
110 
111  // Check if line starts with S (1 character)
112  if (line.substr(offset, 1) != "S") {
113  return std::nullopt;
114  }
115  offset++;
116 
117  // Get type (1 character)
118  uint64_t t = std::stoul(line.substr(offset, 1), nullptr, 16);
119  if (t > 9) {
120  return std::nullopt;
121  }
122  ret.type_ = static_cast<Type>(t);
123  offset++;
124 
125  // Get size (2 characters, 1 byte), subtract address width and checksum
126  ret.size_ = std::stoul(line.substr(offset, 2), nullptr, 16) - ret.address_width() - 1;
127  if (ret.size_ > MAX_DATA_BYTES) {
128  return std::nullopt;
129  }
130  offset += 2;
131 
132  // Obtain the address (addr. width * (2 chars or 1 byte))
133  uint32_t addr = 0;
134  for (int i = ret.address_width() - 1; i >= 0; i--) {
135  uint8_t byte = static_cast<uint8_t>(std::stoul(line.substr(offset, 2), nullptr, 16));
136  addr |= static_cast<uint32_t>(byte) << (8u * i);
137  offset += 2;
138  }
139  ret.address_ = addr;
140 
141  // Reserve a data buffer
142  ret.data_ = static_cast<uint8_t *>(calloc(ret.size_, sizeof(uint8_t)));
143 
144  // Obtain the data (2 chars or 1 byte)
145  for (size_t i = 0; i < ret.size_; i++) {
146  auto byte = static_cast<uint8_t>(std::stoul(line.substr(offset, 2), nullptr, 16));
147  ret.data_[i] = byte;
148  offset += 2;
149  }
150 
151  // Validate checksum
152  uint8_t sum = static_cast<uint8_t>(std::stoul(line.substr(offset, 2), nullptr, 16));
153  if (ret.checksum() != sum) {
154  return std::nullopt;
155  }
156 
157  return ret;
158 }
159 
160 File::File(uint32_t start_address, const uint8_t *data, size_t size, const std::string &header_str) {
161  // Create a header
162  auto header = Record::Header(header_str);
163  records.push_back(header);
164  // Chop the data up into MAX_DATA_BYTES Records
165  size_t pos = 0;
166  while (pos < size) {
167  // Determine record size
168  size_t rec_size;
169  if ((size - pos) / Record::MAX_DATA_BYTES > 0) {
170  rec_size = Record::MAX_DATA_BYTES;
171  } else {
172  rec_size = (size - pos) % Record::MAX_DATA_BYTES;
173  }
174  // Create the record
175  auto rec = Record::Data<32>(static_cast<uint32_t>(start_address + pos), &data[pos], rec_size);
176  // Add to the file
177  records.push_back(rec);
178  pos = pos + rec_size;
179  }
180 }
181 
182 void File::write(std::ostream *output) {
183  if (output->good()) {
184  for (auto &r : records) {
185  (*output) << r.ToString(true);
186  }
187  } else {
188  FLETCHER_LOG(ERROR, "Could not write SREC file to output stream.");
189  }
190 }
191 
192 File::File(std::istream *input) {
193  for (std::string line; std::getline(*input, line);) {
194  auto record = Record::FromString(line);
195  if (record) {
196  records.push_back(*record);
197  } else {
198  throw std::runtime_error("Could not parse SREC file.");
199  }
200  }
201 }
202 
203 void File::ToBuffer(uint8_t **buf, size_t *size) {
204  // Find the highest address and total size
205  uint32_t top_addr = 0;
206  const Record *top_rec = nullptr;
207  for (const auto &r : records) {
208  if (r.address() > top_addr) {
209  top_addr = r.address();
210  top_rec = &r;
211  }
212  }
213  // Allocate buffer
214  if (top_rec != nullptr) {
215  *size = top_addr + top_rec->size();
216  *buf = static_cast<uint8_t *>(calloc(*size, sizeof(uint8_t)));
217  } else {
218  // If there werent any records, just return a nullptr and size 0
219  *buf = nullptr;
220  *size = 0;
221  return;
222  }
223  // For each record, copy bytes into the buffer
224  for (const auto &r : records) {
225  std::memcpy(*buf + r.address(), r.data(), r.size());
226  }
227 }
228 
229 } // namespace fletchgen::srec
Structure to build up a single Record of an SREC file.
Definition: srec.h:34
static Record Header(const std::string &header_str="HDR", uint16_t address=0)
Create an SREC header Record.
Definition: srec.cc:44
Type
The SREC Record type.
Definition: srec.h:42
static constexpr size_t MAX_DATA_BYTES
Maximum number of data bytes per Record.
Definition: srec.h:37
Record(const Record &rec)
SREC Record copy constructor.
Definition: srec.h:56
size_t size() const
Return the size in bytes of this record.
Definition: srec.h:108
uint32_t address() const
Return the address of this record.
Definition: srec.h:106
uint8_t * data() const
Return the data source pointer of this record.
Definition: srec.h:110
std::string ToString(bool line_feed=false)
Return the SREC Record string.
Definition: srec.cc:84
~Record()
Record destructor.
Definition: srec.cc:38
static std::optional< Record > FromString(const std::string &line)
Attempt to construct a Record from a string.
Definition: srec.cc:106
std::shared_ptr< Type > data(int width)
Fletcher data.
Definition: basic_types.cc:98
void ToBuffer(uint8_t **buffer, size_t *size)
Convert an SREC file to a raw buffer.
Definition: srec.cc:203
std::vector< Record > records
SREC records in this file.
Definition: srec.h:172
void write(std::ostream *output)
Write the SREC file to an output stream.
Definition: srec.cc:182