diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..225fc6f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/__pycache__ diff --git a/HighLevelAnalyzer.py b/HighLevelAnalyzer.py new file mode 100644 index 0000000..084e9b8 --- /dev/null +++ b/HighLevelAnalyzer.py @@ -0,0 +1,147 @@ +# High Level Analyzer +# For more information and documentation, please go to https://support.saleae.com/extensions/high-level-analyzer-extensions + +from saleae.analyzers import HighLevelAnalyzer, AnalyzerFrame, NumberSetting +import struct + +# inspired from fasthex +IHEX_SC = ":"#0x3A #collon ":" is start code for all valid lines +#the record type that represents +IHEX_RECT_DT = 0x0 #data +IHEX_RECT_EOF = 0x1 #End of File +IHEX_RECT_OADDR = 0x4 #offset address, e.g. the first part of a 32bit address + +def calc_chksum(data): + """ + calculates the checksum for data + @param data: data + @return: the checksum + """ + return (0x100 - sum(data)) & 0xFF + +def concat_hex_line(laddr,rec_t,data=b""): + """ + concatenates a line of a hex file + @param laddr: the line address where this data starts + @param rec_t: the record type of this line + @param data: the data of this line + @return: a line of a hex file + """ + L = bytearray() + L.append(len(data)) + L.extend(struct.pack(">H",laddr)) + L.append(rec_t) + L.extend(data) + L.append(calc_chksum(L)) + return ":"+L.hex().upper() + +def generate_hex_lines(addr,data,linewidth=0x20): + """ + generates lines for a hex file + @param addr: the address where data starts + @param data: the data + @param linewidth: the byte count how many bytes should be written in a single line + @return: a list of lines + """ + #Todo: this could be easily changed to a generator + lines = [] + lhaddr = -1 + for idx,caddr in enumerate(range(addr,len(data)+addr,linewidth)): + laddr = caddr & 0xFFFF #lower 16bit + if caddr & 0xFFFF0000 != lhaddr: + lhaddr = caddr & 0xFFFF0000 #upper 16 bit + #write a 04 line + lines.append(concat_hex_line(laddr=0,rec_t=IHEX_RECT_OADDR,data=struct.pack(">H",lhaddr >> 16))) + lines.append(concat_hex_line(laddr=laddr,rec_t=IHEX_RECT_DT,data=data[idx*linewidth:(idx+1)*linewidth])) + return lines + +class intelhex(): + + def __init__(self): + """ + intelhex class constructor + """ + self.contents = []#contents will be a list of (addr,bytearray)-tupples + + def to_str(self): + """ + concats the object contents to be written to a hex file + @return: a multi line string + """ + lines = [] + for addr,data in self.contents: + lines.extend([line for line in generate_hex_lines(addr,data)]) + lines.append(concat_hex_line(laddr=0,rec_t=IHEX_RECT_EOF)) + return "\n".join(lines) + + def putz(self,baddr,data): + """ + puts a byte string at a given address + @param baddr: the byte address where to put data + @param data: the data to be put + """ + blen = len(data) + # print(baddr,blen,data,self.contents) + added = False + for addr,_data_ in self.contents: + if (addr < baddr) and ((addr+len(_data_)) > (baddr+blen)): + offset = baddr-addr + _data_[offset:offset+blen] = data + added = True + break + if (addr < baddr) and ((addr+len(_data_)) == baddr): + _data_ += data + added = True + break + if not added: + self.contents.append((baddr,bytearray(data))) + # print(self.contents) + return + + +# High level analyzers must subclass the HighLevelAnalyzer class. +class Hla(HighLevelAnalyzer): + """EEPROM I2C to iHex converter High Level Analyzer.""" + + eep_i2c_addr = NumberSetting(label='EEPROM I2C Address (7-bits)', min_value=0x50, max_value=0x7f) + last_address = NumberSetting(label='Address to trigger iHex printing in Terminal', min_value=0x00, max_value=0xff) + + def __init__(self): + self._for_us: bool = False + self._read: bool = False + self._address: int = 0 + self._byte_pos: int = 0 + self._eep_data = intelhex() + + def decode(self, frame: AnalyzerFrame): + if 'error' in frame.data: + return + if frame.type == 'start': + self._byte_pos = 0 + elif frame.type == 'address': + if frame.data['address'][0] == self.eep_i2c_addr: + self._for_us = True + else: + self._for_us = False + return + self._read = frame.data['read'] + elif frame.type == 'data': + if not self._for_us: + return + raw = frame.data['data'][0] + if self._read: + self._eep_data.putz(self._address+self._byte_pos, frame.data['data']) + else: + if self._byte_pos == 0: + self._address = raw + else: + self._eep_data.putz(self._address+self._byte_pos-1, frame.data['data']) + self._byte_pos += 1 + elif frame.type == 'stop': + if not self._for_us: + return + if self._address == self.last_address: + print() + print("---------- EEPROM iHex ----------") + print(self._eep_data.to_str()) + print("---------------------------------") diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0cc2216 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023-present + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e7598a5 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# EEPROM I2C to iHex converter + +## Features + +* Construct an IntelHEX file accoring to i2c eeprom read and write +* Only 8-bits address EEPROM supported +* Print the iHex file in Terminal on trigger to specific data address (read or write), so choose wisely the last eeprom read/write address of your trace to have a final state of the memory printed. + +## Example + +![Example](https://raw.githubusercontent.com/GPTechinno/eeprom-i2c-ihex-hla/master/demo.png) \ No newline at end of file diff --git a/demo.png b/demo.png new file mode 100644 index 0000000..acda003 Binary files /dev/null and b/demo.png differ diff --git a/examples/demo.sal b/examples/demo.sal new file mode 100644 index 0000000..ddc65fd Binary files /dev/null and b/examples/demo.sal differ diff --git a/extension.json b/extension.json new file mode 100644 index 0000000..f46ae69 --- /dev/null +++ b/extension.json @@ -0,0 +1,13 @@ +{ + "name": "EEPROM I2C to iHex converter", + "apiVersion": "1.0.0", + "author": "GPTechinno", + "version": "0.1.0", + "description": "EEPROM I2C to iHex converter", + "extensions": { + "EEPROM I2C to iHex converter": { + "type": "HighLevelAnalyzer", + "entryPoint": "HighLevelAnalyzer.Hla" + } + } +} \ No newline at end of file