From a5419fb5a4bfc8666db41703c457b58314a45aa2 Mon Sep 17 00:00:00 2001 From: Marcus Comstedt Date: Mon, 5 Aug 2013 13:50:45 +0200 Subject: [PATCH] add saleae-logic16-extract tool. --- firmware/README.saleae-logic16-extract | 13 +++ firmware/parseelf.py | 133 +++++++++++++++++++++++++ firmware/saleae-logic16-extract.py | 54 ++++++++++ 3 files changed, 200 insertions(+) create mode 100644 firmware/README.saleae-logic16-extract create mode 100644 firmware/parseelf.py create mode 100755 firmware/saleae-logic16-extract.py diff --git a/firmware/README.saleae-logic16-extract b/firmware/README.saleae-logic16-extract new file mode 100644 index 0000000..2213676 --- /dev/null +++ b/firmware/README.saleae-logic16-extract @@ -0,0 +1,13 @@ +The saleae-logic16-extract.py tool extracts FPGA bitstreams from the +vendor software forh the Saleae Logic16 USB logic analyzer. Download +the Linux version (either 32-bit or 64-bit will do), and unpack it +to find the main binary called "Logic". + +Usage: + +$ ./saleae-logic16-extract.py Logic +saved 149516 bytes to saleae-logic16-fpga-18.bitstream +saved 149516 bytes to saleae-logic16-fpga-33.bitstream + +Copy the resulting files over to the location where sigrok installed its +firmware files. By default this is /usr/local/share/sigrok-firmware diff --git a/firmware/parseelf.py b/firmware/parseelf.py new file mode 100644 index 0000000..1c6769e --- /dev/null +++ b/firmware/parseelf.py @@ -0,0 +1,133 @@ +#!/usr/bin/python3 +## +## This file is part of the sigrok-util project. +## +## Copyright (C) 2013 Marcus Comstedt +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import struct + +class elf: + + def read_struct(this, struct_fmt, struct_fields): + fmt = this.elf_endianprefix+str.translate(struct_fmt, this.elf_format); + fields = struct.unpack(fmt, this.file.read(struct.calcsize(fmt))) + return dict(zip(struct_fields, fields)) + + def read_ehdr(this): + return this.read_struct('16sHHWNNNWHHHHHH', + ('e_ident', 'e_type', 'e_machine', 'e_version', + 'e_entry', 'e_phoff', 'e_shoff', 'e_flags', + 'e_ehsize', 'e_phentsize', 'e_phnum', + 'e_shentsize', 'e_shnum', 'e_shstrndx')) + + def read_shdr(this): + return this.read_struct('WWNNNNWWNN', + ('sh_name', 'sh_type', 'sh_flags', 'sh_addr', + 'sh_offset', 'sh_size', 'sh_link', 'sh_info', + 'sh_addralign', 'sh_entsize')) + + def read_section(this, shdr): + this.file.seek(shdr['sh_offset']) + return this.file.read(shdr['sh_size']) + + def get_name(this, name, strtab=None): + strtab = strtab or this.strtab + nul = strtab.find(b'\0', name) + if nul < 0: + return bytes.decode(strtab[name:]) + else: + return bytes.decode(strtab[name:nul]) + + def find_section(this, name): + for section in this.shdrs: + if this.get_name(section['sh_name']) == name: + return section + raise KeyError(name) + + def parse_symbol(this): + if this.elf_wordsize == 64: + return this.read_struct('WBBHNX', + ('st_name', 'st_info', 'st_other', + 'st_shndx', 'st_value', 'st_size')) + else: + return this.read_struct('WNWBBH', + ('st_name', 'st_value', 'st_size', + 'st_info', 'st_other', 'st_shndx')) + + def parse_symbols(this, symsecname, strsecname): + try: + symsechdr = this.find_section(symsecname) + strsechdr = this.find_section(strsecname) + except KeyError: + return {} + strsec = this.read_section(strsechdr) + this.file.seek(symsechdr['sh_offset']) + syms = [this.parse_symbol() for i in + range(0, symsechdr['sh_size'] // symsechdr['sh_entsize'])] + return {this.get_name(sym['st_name'], strsec): sym for sym in syms} + + def address_to_offset(this, addr): + for section in this.shdrs: + if (section['sh_addr'] <= addr and + section['sh_addr']+section['sh_size'] > addr): + return section['sh_offset']+(addr-section['sh_addr']) + raise IndexError('address out of range') + + def load_symbol(this, sym): + this.file.seek(this.address_to_offset(sym['st_value'])) + return this.file.read(sym['st_size']) + + def __init__(this, filename): + this.file = open(filename, 'rb') + magic = this.file.read(16) + + if magic[:4] != b'\x7fELF': + raise Exception("ELF signature not found") + + if magic[4] == 1: + this.elf_wordsize = 32 + nativeint = 'Ii' + elif magic[4] == 2: + this.elf_wordsize = 64 + nativeint = 'Qq' + else: + raise Exception("Invalid ELF file class") + + if magic[5] == 1: + this.elf_endianprefix = '<' + elif magic[5] == 2: + this.elf_endianprefix = '>' + else: + raise Exception("Invalid ELF data encoding") + + this.elf_format = str.maketrans('HWwXxNn', 'HIiQq'+nativeint) + + this.file.seek(0) + this.ehdr = this.read_ehdr() + this.file.seek(this.ehdr['e_shoff']) + this.shdrs = [this.read_shdr() for i in range(this.ehdr['e_shnum'])] + + this.strtab = this.read_section(this.shdrs[this.ehdr['e_shstrndx']]) + + this.symtab = this.parse_symbols('.symtab', '.strtab') + this.dynsym = this.parse_symbols('.dynsym', '.dynstr') + + def __del__(this): + try: + this.file.close() + except AttributeError: + pass diff --git a/firmware/saleae-logic16-extract.py b/firmware/saleae-logic16-extract.py new file mode 100755 index 0000000..c325174 --- /dev/null +++ b/firmware/saleae-logic16-extract.py @@ -0,0 +1,54 @@ +#!/usr/bin/python3 +## +## This file is part of the sigrok-util project. +## +## Copyright (C) 2013 Marcus Comstedt +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 3 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, see . +## + +import sys +import parseelf + +def extract_symbol(elf, symname, filename): + blob = elf.load_symbol(elf.dynsym[symname]) + f = open(filename, 'wb') + f.write(blob) + f.close() + print("saved %d bytes to %s" % (len(blob), filename)) + +def extract_bitstream(elf, lv): + extract_symbol(elf, 'gLogic16Lv'+lv+'CompressedBitstream', + 'saleae-logic16-fpga-'+lv+'.bitstream') + +def usage(): + print("saleae-logic16-extract.py ") + sys.exit() + + +# +# main +# + +if len(sys.argv) != 2: + usage() + +try: + filename = sys.argv[1] + elf = parseelf.elf(filename) + extract_bitstream(elf, '18') + extract_bitstream(elf, '33') +except Exception as e: + print("Error: %s" % str(e)) + -- 2.30.2