From 257dbc749f6513e12dbc1edb5926af9c89f68264 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 22 Feb 2019 11:40:44 -0800 Subject: [PATCH 01/15] CPLD: Checksum tool. --- firmware/tools/cpld_crc.py | 321 +++++++++++++++++++++++++++++++++++++ 1 file changed, 321 insertions(+) create mode 100755 firmware/tools/cpld_crc.py diff --git a/firmware/tools/cpld_crc.py b/firmware/tools/cpld_crc.py new file mode 100755 index 00000000..0a3dd601 --- /dev/null +++ b/firmware/tools/cpld_crc.py @@ -0,0 +1,321 @@ +#!/usr/bin/env python3 + +import struct + +class DumbCRC32(object): + def __init__(self): + self._remainder = 0xffffffff + self._reversed_polynomial = 0xedb88320 + self._final_xor = 0xffffffff + + def update(self, data): + bit_count = len(data) * 8 + for bit_n in range(bit_count): + bit_in = data[bit_n >> 3] & (1 << (bit_n & 7)) + self._remainder ^= 1 if bit_in != 0 else 0 + bit_out = (self._remainder & 1) + self._remainder >>= 1; + if bit_out != 0: + self._remainder ^= self._reversed_polynomial; + + def digest(self): + return self._remainder ^ self._final_xor + + def hexdigest(self): + return '%08x' % self.digest() + +class XSVFParser(object): + def __init__(self): + self._handlers = { + 0x00: self.XCOMPLETE , + 0x01: self.XTDOMASK , + 0x02: self.XSIR , + 0x03: self.XSDR , + 0x04: self.XRUNTEST , + 0x07: self.XREPEAT , + 0x08: self.XSDRSIZE , + 0x09: self.XSDRTDO , + 0x0a: self.XSETSDRMASKS, + 0x0b: self.XSDRINC , + 0x0c: self.XSDRB , + 0x0d: self.XSDRC , + 0x0e: self.XSDRE , + 0x0f: self.XSDRTDOB , + 0x10: self.XSDRTDOC , + 0x11: self.XSDRTDOE , + 0x12: self.XSTATE , + 0x13: self.XENDIR , + 0x14: self.XENDDR , + 0x15: self.XSIR2 , + 0x16: self.XCOMMENT , + 0x17: self.XWAIT , + } + + def read_byte(self): + return self.read_bytes(1)[0] + + def read_bytes(self, n): + c = self._f.read(n) + if len(c) == n: + return c + else: + raise RuntimeError('unexpected end of file') + + def read_bits(self, n): + length_bytes = (n + 7) >> 3 + return self.read_bytes(length_bytes) + + def read_u32(self): + return struct.unpack('>I', self.read_bytes(4))[0] + + def parse(self, f, debug=False): + self._f = f + self._debug = debug + self._xcomplete = False + self._xenddr = None + self._xendir = None + self._xruntest = 0 + self._xsdrsize = None + self._xtdomask = None + self._commands = [] + + while self._xcomplete == False: + self.read_instruction() + + self._f = None + + return self._commands + + def read_instruction(self): + instruction_id = self.read_byte() + if instruction_id in self._handlers: + instruction_handler = self._handlers[instruction_id] + result = instruction_handler() + if result is not None: + self._commands.append(result) + else: + raise RuntimeError('unexpected instruction 0x%02x' % instruction_id) + + def XCOMPLETE(self): + self._xcomplete = True + + def XTDOMASK(self): + length_bits = self._xsdrsize + self._xtdomask = self.read_bits(length_bits) + + def XSIR(self): + length_bits = self.read_byte() + tdi = self.read_bits(length_bits) + if self._debug: + print('XSIR tdi=%d:%s' % (length_bits, tdi.hex())) + return { + 'type': 'xsir', + 'tdi': { + 'length': length_bits, + 'data': tdi + }, + } + + def XSDR(self): + length_bits = self._xsdrsize + tdi = self.read_bits(length_bits) + if self._debug: + print('XSDR tdi=%d:%s' % (length_bits, tdi.hex())) + return { + 'type': 'xsdr', + 'tdi': { + 'length': length_bits, + 'data': tdi, + }, + } + + def XRUNTEST(self): + self._xruntest = self.read_u32() + if self._debug: + print('XRUNTEST number=%d' % self._xruntest) + + def XREPEAT(self): + repeat = self.read_byte() + # print('XREPEAT times=%d' % repeat) + + def XSDRSIZE(self): + self._xsdrsize = self.read_u32() + + def XSDRTDO(self): + length_bits = self._xsdrsize + tdi = self.read_bits(length_bits) + tdo_mask = self._xtdomask + self._tdo_expected = (length_bits, self.read_bits(length_bits)) + wait = self._xruntest + if wait == 0: + end_state = self._xenddr + else: + end_state = 1 # Run-Test/Idle + if self._debug: + print('XSDRTDO tdi=%d:%s tdo_mask=%d:%s tdo_expected=%d:%s end_state=%u wait=%u' % ( + length_bits, tdi.hex(), + length_bits, tdo_mask.hex(), + self._tdo_expected[0], self._tdo_expected[1].hex(), + end_state, + wait, + )) + return { + 'type': 'xsdrtdo', + 'tdi': { + 'length': length_bits, + 'data': tdi + }, + 'tdo_mask': { + 'length': length_bits, + 'data': tdo_mask, + }, + 'tdo_expected': { + 'length': self._tdo_expected[0], + 'data': self._tdo_expected[1], + }, + 'end_state': end_state, + 'wait': wait, + } + + def XSETSDRMASKS(self): + raise RuntimeError('unimplemented') + + def XSDRINC(self): + raise RuntimeError('unimplemented') + + def XSDRB(self): + raise RuntimeError('unimplemented') + + def XSDRC(self): + raise RuntimeError('unimplemented') + + def XSDRE(self): + raise RuntimeError('unimplemented') + + def XSDRTDOB(self): + raise RuntimeError('unimplemented') + + def XSDRTDOC(self): + raise RuntimeError('unimplemented') + + def XSDRTDOE(self): + raise RuntimeError('unimplemented') + + def XSTATE(self): + state = self.read_byte() + if self._debug: + print('XSTATE %u' % state) + return { + 'type': 'xstate', + 'state': state, + } + + def XENDIR(self): + self._xendir = self.read_byte() + + def XENDDR(self): + self._xenddr = self.read_byte() + + def XSIR2(self): + raise RuntimeError('unimplemented') + + def XCOMMENT(self): + raise RuntimeError('unimplemented') + + def XWAIT(self): + wait_state = self.read_byte() + end_state = self.read_byte() + wait_time = self.read_u32() + +####################################################################### +# Command line argument parsing. +####################################################################### + +import sys + +if len(sys.argv) != 2: + print("Usage: cpld_crc.py Date: Fri, 22 Feb 2019 13:17:29 -0800 Subject: [PATCH 02/15] CPLD: Extract library code from CRC tool. --- firmware/tools/cpld_crc.py | 228 +---------------------------------- firmware/tools/dumb_crc32.py | 22 ++++ firmware/tools/xsvf.py | 205 +++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+), 226 deletions(-) create mode 100644 firmware/tools/dumb_crc32.py create mode 100644 firmware/tools/xsvf.py diff --git a/firmware/tools/cpld_crc.py b/firmware/tools/cpld_crc.py index 0a3dd601..bbfa8c3b 100755 --- a/firmware/tools/cpld_crc.py +++ b/firmware/tools/cpld_crc.py @@ -1,231 +1,7 @@ #!/usr/bin/env python3 -import struct - -class DumbCRC32(object): - def __init__(self): - self._remainder = 0xffffffff - self._reversed_polynomial = 0xedb88320 - self._final_xor = 0xffffffff - - def update(self, data): - bit_count = len(data) * 8 - for bit_n in range(bit_count): - bit_in = data[bit_n >> 3] & (1 << (bit_n & 7)) - self._remainder ^= 1 if bit_in != 0 else 0 - bit_out = (self._remainder & 1) - self._remainder >>= 1; - if bit_out != 0: - self._remainder ^= self._reversed_polynomial; - - def digest(self): - return self._remainder ^ self._final_xor - - def hexdigest(self): - return '%08x' % self.digest() - -class XSVFParser(object): - def __init__(self): - self._handlers = { - 0x00: self.XCOMPLETE , - 0x01: self.XTDOMASK , - 0x02: self.XSIR , - 0x03: self.XSDR , - 0x04: self.XRUNTEST , - 0x07: self.XREPEAT , - 0x08: self.XSDRSIZE , - 0x09: self.XSDRTDO , - 0x0a: self.XSETSDRMASKS, - 0x0b: self.XSDRINC , - 0x0c: self.XSDRB , - 0x0d: self.XSDRC , - 0x0e: self.XSDRE , - 0x0f: self.XSDRTDOB , - 0x10: self.XSDRTDOC , - 0x11: self.XSDRTDOE , - 0x12: self.XSTATE , - 0x13: self.XENDIR , - 0x14: self.XENDDR , - 0x15: self.XSIR2 , - 0x16: self.XCOMMENT , - 0x17: self.XWAIT , - } - - def read_byte(self): - return self.read_bytes(1)[0] - - def read_bytes(self, n): - c = self._f.read(n) - if len(c) == n: - return c - else: - raise RuntimeError('unexpected end of file') - - def read_bits(self, n): - length_bytes = (n + 7) >> 3 - return self.read_bytes(length_bytes) - - def read_u32(self): - return struct.unpack('>I', self.read_bytes(4))[0] - - def parse(self, f, debug=False): - self._f = f - self._debug = debug - self._xcomplete = False - self._xenddr = None - self._xendir = None - self._xruntest = 0 - self._xsdrsize = None - self._xtdomask = None - self._commands = [] - - while self._xcomplete == False: - self.read_instruction() - - self._f = None - - return self._commands - - def read_instruction(self): - instruction_id = self.read_byte() - if instruction_id in self._handlers: - instruction_handler = self._handlers[instruction_id] - result = instruction_handler() - if result is not None: - self._commands.append(result) - else: - raise RuntimeError('unexpected instruction 0x%02x' % instruction_id) - - def XCOMPLETE(self): - self._xcomplete = True - - def XTDOMASK(self): - length_bits = self._xsdrsize - self._xtdomask = self.read_bits(length_bits) - - def XSIR(self): - length_bits = self.read_byte() - tdi = self.read_bits(length_bits) - if self._debug: - print('XSIR tdi=%d:%s' % (length_bits, tdi.hex())) - return { - 'type': 'xsir', - 'tdi': { - 'length': length_bits, - 'data': tdi - }, - } - - def XSDR(self): - length_bits = self._xsdrsize - tdi = self.read_bits(length_bits) - if self._debug: - print('XSDR tdi=%d:%s' % (length_bits, tdi.hex())) - return { - 'type': 'xsdr', - 'tdi': { - 'length': length_bits, - 'data': tdi, - }, - } - - def XRUNTEST(self): - self._xruntest = self.read_u32() - if self._debug: - print('XRUNTEST number=%d' % self._xruntest) - - def XREPEAT(self): - repeat = self.read_byte() - # print('XREPEAT times=%d' % repeat) - - def XSDRSIZE(self): - self._xsdrsize = self.read_u32() - - def XSDRTDO(self): - length_bits = self._xsdrsize - tdi = self.read_bits(length_bits) - tdo_mask = self._xtdomask - self._tdo_expected = (length_bits, self.read_bits(length_bits)) - wait = self._xruntest - if wait == 0: - end_state = self._xenddr - else: - end_state = 1 # Run-Test/Idle - if self._debug: - print('XSDRTDO tdi=%d:%s tdo_mask=%d:%s tdo_expected=%d:%s end_state=%u wait=%u' % ( - length_bits, tdi.hex(), - length_bits, tdo_mask.hex(), - self._tdo_expected[0], self._tdo_expected[1].hex(), - end_state, - wait, - )) - return { - 'type': 'xsdrtdo', - 'tdi': { - 'length': length_bits, - 'data': tdi - }, - 'tdo_mask': { - 'length': length_bits, - 'data': tdo_mask, - }, - 'tdo_expected': { - 'length': self._tdo_expected[0], - 'data': self._tdo_expected[1], - }, - 'end_state': end_state, - 'wait': wait, - } - - def XSETSDRMASKS(self): - raise RuntimeError('unimplemented') - - def XSDRINC(self): - raise RuntimeError('unimplemented') - - def XSDRB(self): - raise RuntimeError('unimplemented') - - def XSDRC(self): - raise RuntimeError('unimplemented') - - def XSDRE(self): - raise RuntimeError('unimplemented') - - def XSDRTDOB(self): - raise RuntimeError('unimplemented') - - def XSDRTDOC(self): - raise RuntimeError('unimplemented') - - def XSDRTDOE(self): - raise RuntimeError('unimplemented') - - def XSTATE(self): - state = self.read_byte() - if self._debug: - print('XSTATE %u' % state) - return { - 'type': 'xstate', - 'state': state, - } - - def XENDIR(self): - self._xendir = self.read_byte() - - def XENDDR(self): - self._xenddr = self.read_byte() - - def XSIR2(self): - raise RuntimeError('unimplemented') - - def XCOMMENT(self): - raise RuntimeError('unimplemented') - - def XWAIT(self): - wait_state = self.read_byte() - end_state = self.read_byte() - wait_time = self.read_u32() +from dumb_crc32 import DumbCRC32 +from xsvf import XSVFParser ####################################################################### # Command line argument parsing. diff --git a/firmware/tools/dumb_crc32.py b/firmware/tools/dumb_crc32.py new file mode 100644 index 00000000..411a836d --- /dev/null +++ b/firmware/tools/dumb_crc32.py @@ -0,0 +1,22 @@ + +class DumbCRC32(object): + def __init__(self): + self._remainder = 0xffffffff + self._reversed_polynomial = 0xedb88320 + self._final_xor = 0xffffffff + + def update(self, data): + bit_count = len(data) * 8 + for bit_n in range(bit_count): + bit_in = data[bit_n >> 3] & (1 << (bit_n & 7)) + self._remainder ^= 1 if bit_in != 0 else 0 + bit_out = (self._remainder & 1) + self._remainder >>= 1; + if bit_out != 0: + self._remainder ^= self._reversed_polynomial; + + def digest(self): + return self._remainder ^ self._final_xor + + def hexdigest(self): + return '%08x' % self.digest() diff --git a/firmware/tools/xsvf.py b/firmware/tools/xsvf.py new file mode 100644 index 00000000..b6e60332 --- /dev/null +++ b/firmware/tools/xsvf.py @@ -0,0 +1,205 @@ + +import struct + +class XSVFParser(object): + def __init__(self): + self._handlers = { + 0x00: self.XCOMPLETE , + 0x01: self.XTDOMASK , + 0x02: self.XSIR , + 0x03: self.XSDR , + 0x04: self.XRUNTEST , + 0x07: self.XREPEAT , + 0x08: self.XSDRSIZE , + 0x09: self.XSDRTDO , + 0x0a: self.XSETSDRMASKS, + 0x0b: self.XSDRINC , + 0x0c: self.XSDRB , + 0x0d: self.XSDRC , + 0x0e: self.XSDRE , + 0x0f: self.XSDRTDOB , + 0x10: self.XSDRTDOC , + 0x11: self.XSDRTDOE , + 0x12: self.XSTATE , + 0x13: self.XENDIR , + 0x14: self.XENDDR , + 0x15: self.XSIR2 , + 0x16: self.XCOMMENT , + 0x17: self.XWAIT , + } + + def read_byte(self): + return self.read_bytes(1)[0] + + def read_bytes(self, n): + c = self._f.read(n) + if len(c) == n: + return c + else: + raise RuntimeError('unexpected end of file') + + def read_bits(self, n): + length_bytes = (n + 7) >> 3 + return self.read_bytes(length_bytes) + + def read_u32(self): + return struct.unpack('>I', self.read_bytes(4))[0] + + def parse(self, f, debug=False): + self._f = f + self._debug = debug + self._xcomplete = False + self._xenddr = None + self._xendir = None + self._xruntest = 0 + self._xsdrsize = None + self._xtdomask = None + self._commands = [] + + while self._xcomplete == False: + self.read_instruction() + + self._f = None + + return self._commands + + def read_instruction(self): + instruction_id = self.read_byte() + if instruction_id in self._handlers: + instruction_handler = self._handlers[instruction_id] + result = instruction_handler() + if result is not None: + self._commands.append(result) + else: + raise RuntimeError('unexpected instruction 0x%02x' % instruction_id) + + def XCOMPLETE(self): + self._xcomplete = True + + def XTDOMASK(self): + length_bits = self._xsdrsize + self._xtdomask = self.read_bits(length_bits) + + def XSIR(self): + length_bits = self.read_byte() + tdi = self.read_bits(length_bits) + if self._debug: + print('XSIR tdi=%d:%s' % (length_bits, tdi.hex())) + return { + 'type': 'xsir', + 'tdi': { + 'length': length_bits, + 'data': tdi + }, + } + + def XSDR(self): + length_bits = self._xsdrsize + tdi = self.read_bits(length_bits) + if self._debug: + print('XSDR tdi=%d:%s' % (length_bits, tdi.hex())) + return { + 'type': 'xsdr', + 'tdi': { + 'length': length_bits, + 'data': tdi, + }, + } + + def XRUNTEST(self): + self._xruntest = self.read_u32() + if self._debug: + print('XRUNTEST number=%d' % self._xruntest) + + def XREPEAT(self): + repeat = self.read_byte() + # print('XREPEAT times=%d' % repeat) + + def XSDRSIZE(self): + self._xsdrsize = self.read_u32() + + def XSDRTDO(self): + length_bits = self._xsdrsize + tdi = self.read_bits(length_bits) + tdo_mask = self._xtdomask + self._tdo_expected = (length_bits, self.read_bits(length_bits)) + wait = self._xruntest + if wait == 0: + end_state = self._xenddr + else: + end_state = 1 # Run-Test/Idle + if self._debug: + print('XSDRTDO tdi=%d:%s tdo_mask=%d:%s tdo_expected=%d:%s end_state=%u wait=%u' % ( + length_bits, tdi.hex(), + length_bits, tdo_mask.hex(), + self._tdo_expected[0], self._tdo_expected[1].hex(), + end_state, + wait, + )) + return { + 'type': 'xsdrtdo', + 'tdi': { + 'length': length_bits, + 'data': tdi + }, + 'tdo_mask': { + 'length': length_bits, + 'data': tdo_mask, + }, + 'tdo_expected': { + 'length': self._tdo_expected[0], + 'data': self._tdo_expected[1], + }, + 'end_state': end_state, + 'wait': wait, + } + + def XSETSDRMASKS(self): + raise RuntimeError('unimplemented') + + def XSDRINC(self): + raise RuntimeError('unimplemented') + + def XSDRB(self): + raise RuntimeError('unimplemented') + + def XSDRC(self): + raise RuntimeError('unimplemented') + + def XSDRE(self): + raise RuntimeError('unimplemented') + + def XSDRTDOB(self): + raise RuntimeError('unimplemented') + + def XSDRTDOC(self): + raise RuntimeError('unimplemented') + + def XSDRTDOE(self): + raise RuntimeError('unimplemented') + + def XSTATE(self): + state = self.read_byte() + if self._debug: + print('XSTATE %u' % state) + return { + 'type': 'xstate', + 'state': state, + } + + def XENDIR(self): + self._xendir = self.read_byte() + + def XENDDR(self): + self._xenddr = self.read_byte() + + def XSIR2(self): + raise RuntimeError('unimplemented') + + def XCOMMENT(self): + raise RuntimeError('unimplemented') + + def XWAIT(self): + wait_state = self.read_byte() + end_state = self.read_byte() + wait_time = self.read_u32() From 20975e931340a47713788a9a2c02502fe0bc3046 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 22 Feb 2019 13:23:48 -0800 Subject: [PATCH 03/15] CPLD: Tool argument parsing. --- firmware/tools/cpld_crc.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/firmware/tools/cpld_crc.py b/firmware/tools/cpld_crc.py index bbfa8c3b..14f658f2 100755 --- a/firmware/tools/cpld_crc.py +++ b/firmware/tools/cpld_crc.py @@ -7,13 +7,12 @@ from xsvf import XSVFParser # Command line argument parsing. ####################################################################### -import sys +import argparse -if len(sys.argv) != 2: - print("Usage: cpld_crc.py Date: Fri, 22 Feb 2019 13:25:01 -0800 Subject: [PATCH 04/15] CPLD tool: Move imports to smallest scopes. --- firmware/tools/cpld_crc.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/firmware/tools/cpld_crc.py b/firmware/tools/cpld_crc.py index 14f658f2..45317123 100755 --- a/firmware/tools/cpld_crc.py +++ b/firmware/tools/cpld_crc.py @@ -1,8 +1,5 @@ #!/usr/bin/env python3 -from dumb_crc32 import DumbCRC32 -from xsvf import XSVFParser - ####################################################################### # Command line argument parsing. ####################################################################### @@ -19,9 +16,9 @@ args = parser.parse_args() # against the CPLD. ####################################################################### -parser = XSVFParser() with open(args.hackrf_xc2c_cpld_xsvf, "rb") as f: - commands = parser.parse(f, debug=args.debug) + from xsvf import XSVFParser + commands = XSVFParser().parse(f, debug=args.debug) ####################################################################### # Extraction of verify row addresses and data/masks. @@ -86,6 +83,7 @@ if False: else: # Use my home-grown, simple, slow CRC32 object to avoid additional # Python dependencies. + from dumb_crc32 import DumbCRC32 crc = DumbCRC32() for address, data, mask in data: From 30cd9586de75e0837639b80903ec6146a434ebdc Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 22 Feb 2019 13:28:32 -0800 Subject: [PATCH 05/15] CPLD tool: Flag to use crcmod library. --- firmware/tools/cpld_crc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware/tools/cpld_crc.py b/firmware/tools/cpld_crc.py index 45317123..a678351c 100755 --- a/firmware/tools/cpld_crc.py +++ b/firmware/tools/cpld_crc.py @@ -7,6 +7,7 @@ import argparse parser = argparse.ArgumentParser() +parser.add_argument('--crcmod', action='store_true') parser.add_argument('--debug', action='store_true') parser.add_argument('hackrf_xc2c_cpld_xsvf', type=str) args = parser.parse_args() @@ -76,7 +77,7 @@ assert(address_sequence == expected_address_sequence) data = data[1] byte_count = (274 + 7) // 8 -if False: +if args.crcmod: # Use a proper CRC library import crcmod crc = crcmod.predefined.Crc('crc-32') From 0b4c714e0de601474d07ac1afd1d7ab230e013bf Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 22 Feb 2019 13:28:52 -0800 Subject: [PATCH 06/15] CPLD tool: Remove commented code. --- firmware/tools/cpld_crc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/firmware/tools/cpld_crc.py b/firmware/tools/cpld_crc.py index a678351c..96ff2045 100755 --- a/firmware/tools/cpld_crc.py +++ b/firmware/tools/cpld_crc.py @@ -49,12 +49,10 @@ for command in commands: if tdi_length == 7 and end_state == 1: address = int(command['tdi']['data'].hex(), 16) data[-1].append([address]) - # print('address: %02x' % address) elif tdi_length == 274 and end_state == 0: mask = int(command['tdo_mask']['data'].hex(), 16) expected = int(command['tdo_expected']['data'].hex(), 16) data[-1][-1].extend([expected, mask]) - # print('mask:%x tdo:%x' % (mask, expected)) ####################################################################### # Check that extracted data conforms to expectations. From 5695f29c8dff672fbc8bd253f799b50e3b55c9bc Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 22 Feb 2019 13:32:15 -0800 Subject: [PATCH 07/15] CPLD tool: Add arguments help. --- firmware/tools/cpld_crc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware/tools/cpld_crc.py b/firmware/tools/cpld_crc.py index 96ff2045..c876b423 100755 --- a/firmware/tools/cpld_crc.py +++ b/firmware/tools/cpld_crc.py @@ -7,9 +7,9 @@ import argparse parser = argparse.ArgumentParser() -parser.add_argument('--crcmod', action='store_true') -parser.add_argument('--debug', action='store_true') -parser.add_argument('hackrf_xc2c_cpld_xsvf', type=str) +parser.add_argument('--crcmod', action='store_true', help='Use Python crcmod library instead of built-in CRC32 code') +parser.add_argument('--debug', action='store_true', help='Enable debug output') +parser.add_argument('hackrf_xc2c_cpld_xsvf', type=str, help='HackRF Xilinx XC2C64A CPLD XSVF file containing erase/program/verify phases') args = parser.parse_args() ####################################################################### From f70186644c1bd172d6c27560083d2ec5cd2f6080 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Fri, 22 Feb 2019 13:35:47 -0800 Subject: [PATCH 08/15] CPLD tool: Add checksum and code generation mode flags. --- firmware/tools/cpld_crc.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/firmware/tools/cpld_crc.py b/firmware/tools/cpld_crc.py index c876b423..2c20007e 100755 --- a/firmware/tools/cpld_crc.py +++ b/firmware/tools/cpld_crc.py @@ -7,6 +7,9 @@ import argparse parser = argparse.ArgumentParser() +action_group = parser.add_mutually_exclusive_group(required=True) +action_group.add_argument('--checksum', action='store_true', help='Calculate bitstream read-back CRC32 value') +action_group.add_argument('--code', action='store_true', help='Generate C code for bitstream loading/programming/verification') parser.add_argument('--crcmod', action='store_true', help='Use Python crcmod library instead of built-in CRC32 code') parser.add_argument('--debug', action='store_true', help='Enable debug output') parser.add_argument('hackrf_xc2c_cpld_xsvf', type=str, help='HackRF Xilinx XC2C64A CPLD XSVF file containing erase/program/verify phases') @@ -75,19 +78,20 @@ assert(address_sequence == expected_address_sequence) data = data[1] byte_count = (274 + 7) // 8 -if args.crcmod: - # Use a proper CRC library - import crcmod - crc = crcmod.predefined.Crc('crc-32') -else: - # Use my home-grown, simple, slow CRC32 object to avoid additional - # Python dependencies. - from dumb_crc32 import DumbCRC32 - crc = DumbCRC32() +if args.checksum: + if args.crcmod: + # Use a proper CRC library + import crcmod + crc = crcmod.predefined.Crc('crc-32') + else: + # Use my home-grown, simple, slow CRC32 object to avoid additional + # Python dependencies. + from dumb_crc32 import DumbCRC32 + crc = DumbCRC32() -for address, data, mask in data: - valid_data = data & mask - bytes = valid_data.to_bytes(byte_count, byteorder='little') - crc.update(bytes) + for address, data, mask in data: + valid_data = data & mask + bytes = valid_data.to_bytes(byte_count, byteorder='little') + crc.update(bytes) -print('0x%s' % crc.hexdigest().lower()) + print('0x%s' % crc.hexdigest().lower()) From 9aa3a78d786eb24f1984a68d62116a348b72633e Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Feb 2019 20:19:12 -0800 Subject: [PATCH 09/15] CPLD tool: Add code generation, more bitstream checks. Code is now generated from programming block, checked against verify block, and also provides mask for verification process. --- firmware/tools/cpld_crc.py | 243 ++++++++++++++++++++++++++++++------- firmware/tools/xsvf.py | 3 + 2 files changed, 201 insertions(+), 45 deletions(-) diff --git a/firmware/tools/cpld_crc.py b/firmware/tools/cpld_crc.py index 2c20007e..7739d851 100755 --- a/firmware/tools/cpld_crc.py +++ b/firmware/tools/cpld_crc.py @@ -1,5 +1,148 @@ #!/usr/bin/env python3 +# Xilinx CoolRunner II XC2C64A characteristics + +bits_of_address = 7 +bits_of_data = 274 +bytes_of_data = (bits_of_data + 7) // 8 +bits_in_program_row = bits_of_address + bits_of_data + +def values_list_line_wrap(values): + line_length = 16 + return [' '.join(values[n:n+line_length]) for n in range(0, len(values), line_length)] + +def dec_lines(bytes): + return values_list_line_wrap(['%d,' % n for n in bytes]) + +def hex_lines(bytes): + return values_list_line_wrap(['0x%02x,' % n for n in bytes]) + +def reverse_bits(n, bit_count): + byte_count = (bit_count + 7) >> 3 + # n = int(bytes.hex(), 16) + n_bits = bin(n)[2:].zfill(bit_count) + n_bits_reversed = n_bits[::-1] + n_reversed = int(n_bits_reversed, 2) + return n_reversed.to_bytes(byte_count, byteorder='little') + +def extract_addresses(block): + return tuple([row['address'] for row in block]) + +def extract_data(block): + return tuple([row['data'] for row in block]) + +def extract_mask(block): + return tuple([row['mask'] for row in block]) + +def equal_blocks(block1, block2, mask): + block1_data = extract_data(block1) + block2_data = extract_data(block2) + assert(len(block1_data) == len(block2_data)) + assert(len(block1_data) == len(mask)) + for row1, row2, mask in zip(block1_data, block2_data, mask): + differences = (row1 ^ row2) & mask + if differences != 0: + return False + return True + +def dump_block(rows, endian='little'): + data_bytes = (bits_of_data + 7) >> 3 + for row in rows: + print('%02x %s' % (row['address'], row['data'].to_bytes(data_bytes, byteorder=endian).hex())) + +def extract_programming_data(commands): + ir_map = { + 0x01: 'idcode', + 0xc0: 'conld', + 0xe8: 'enable', + 0xea: 'program', + 0xed: 'erase', + 0xee: 'verify', + 0xf0: 'init', + 0xff: 'bypass', + # Other instructions unimplemented and if encountered, will cause tool to crash. + } + + ir = None + program = [] + verify = [] + for command in commands: + if command['type'] == 'xsir': + ir = ir_map[command['tdi']['data'][0]] + if ir == 'program': + program.append([]) + if ir == 'verify': + verify.append([]) + elif ir == 'verify' and command['type'] == 'xsdrtdo': + tdi_length = command['tdi']['length'] + end_state = command['end_state'] + if tdi_length == bits_of_address and end_state == 1: + address = int(command['tdi']['data'].hex(), 16) + verify[-1].append({'address': address}) + elif tdi_length == bits_of_data and end_state == 0: + mask = int(command['tdo_mask']['data'].hex(), 16) + expected = int(command['tdo_expected']['data'].hex(), 16) + verify[-1][-1]['data'] = expected + verify[-1][-1]['mask'] = mask + elif ir == 'program' and command['type'] == 'xsdrtdo': + tdi_length = command['tdi']['length'] + end_state = command['end_state'] + if tdi_length == bits_in_program_row and end_state == 0: + tdi = int(command['tdi']['data'].hex(), 16) + address = (tdi >> bits_of_data) & ((1 << bits_of_address) - 1) + data = tdi & ((1 << bits_of_data) - 1) + program[-1].append({ + 'address': address, + 'data': data + }) + + return { + 'program': program, + 'verify': verify, + } + +def validate_programming_data(programming_data): + expected_address_sequence = (0x00, 0x40, 0x60, 0x20, 0x30, 0x70, 0x50, 0x10, 0x18, 0x58, 0x78, 0x38, 0x28, 0x68, 0x48, 0x08, 0x0c, 0x4c, 0x6c, 0x2c, 0x3c, 0x7c, 0x5c, 0x1c, 0x14, 0x54, 0x74, 0x34, 0x24, 0x64, 0x44, 0x04, 0x06, 0x46, 0x66, 0x26, 0x36, 0x76, 0x56, 0x16, 0x1e, 0x5e, 0x7e, 0x3e, 0x2e, 0x6e, 0x4e, 0x0e, 0x0a, 0x4a, 0x6a, 0x2a, 0x3a, 0x7a, 0x5a, 0x1a, 0x12, 0x52, 0x72, 0x32, 0x22, 0x62, 0x42, 0x02, 0x03, 0x43, 0x63, 0x23, 0x33, 0x73, 0x53, 0x13, 0x1b, 0x5b, 0x7b, 0x3b, 0x2b, 0x6b, 0x4b, 0x0b, 0x0f, 0x4f, 0x6f, 0x2f, 0x3f, 0x7f, 0x5f, 0x1f, 0x17, 0x57, 0x77, 0x37, 0x27, 0x67, 0x47, 0x07, 0x05, 0x45,) + + # Validate program blocks: + + # There should be two extracted program blocks. The first contains the + # the bitstream with done bit(s) not asserted. The second updates the + # "done" bit(s) to finish the process. + assert(len(programming_data['program']) == 2) + + # First program phase writes the bitstream to flash (or SRAM) with + # special bit(s) not asserted, so the bitstream is not yet valid. + assert(extract_addresses(programming_data['program'][0]) == expected_address_sequence) + + # Second program phase updates a single row to finish the programming + # process. + assert(len(programming_data['program'][1]) == 1) + assert(programming_data['program'][1][0]['address'] == 0x05) + + # Validate verify blocks: + + # There should be two extracted verify blocks. + assert(len(programming_data['verify']) == 2) + + # The two verify blocks should match. + assert(programming_data['verify'][0] == programming_data['verify'][1]) + + # Check the row address order of the second verify block. + assert(extract_addresses(programming_data['verify'][0]) == expected_address_sequence) + assert(extract_addresses(programming_data['verify'][1]) == expected_address_sequence) + + # Checks across programming and verification: + + # Check that program data matches data expected during verification. + assert(equal_blocks(programming_data['program'][0], programming_data['verify'][0], extract_mask(programming_data['verify'][0]))) + assert(equal_blocks(programming_data['program'][0], programming_data['verify'][1], extract_mask(programming_data['verify'][1]))) + +def make_sram_program(program_blocks): + program_sram = list(program_blocks[0]) + program_sram[-2] = program_blocks[1][0] + return program_sram + ####################################################################### # Command line argument parsing. ####################################################################### @@ -24,50 +167,16 @@ with open(args.hackrf_xc2c_cpld_xsvf, "rb") as f: from xsvf import XSVFParser commands = XSVFParser().parse(f, debug=args.debug) -####################################################################### -# Extraction of verify row addresses and data/masks. -####################################################################### - -ir_map = { - 0x01: 'idcode', - 0xc0: 'conld', - 0xe8: 'enable', - 0xea: 'program', - 0xed: 'erase', - 0xee: 'verify', - 0xf0: 'init', - 0xff: 'bypass', -} - -ir = None -data = [] -for command in commands: - if command['type'] == 'xsir': - ir = ir_map[command['tdi']['data'][0]] - if ir == 'verify': - data.append([]) - elif ir == 'verify' and command['type'] == 'xsdrtdo': - tdi_length = command['tdi']['length'] - end_state = command['end_state'] - if tdi_length == 7 and end_state == 1: - address = int(command['tdi']['data'].hex(), 16) - data[-1].append([address]) - elif tdi_length == 274 and end_state == 0: - mask = int(command['tdo_mask']['data'].hex(), 16) - expected = int(command['tdo_expected']['data'].hex(), 16) - data[-1][-1].extend([expected, mask]) +programming_data = extract_programming_data(commands) +validate_programming_data(programming_data) ####################################################################### -# Check that extracted data conforms to expectations. +# Patch the second programming phase into the first for SRAM +# programming. ####################################################################### -# There should two extracted verify blocks. -assert(len(data) == 2) - -# Check the row address order of the second verify block. -address_sequence = tuple([row[0] for row in data[1]]) -expected_address_sequence = (0x00, 0x40, 0x60, 0x20, 0x30, 0x70, 0x50, 0x10, 0x18, 0x58, 0x78, 0x38, 0x28, 0x68, 0x48, 0x08, 0x0c, 0x4c, 0x6c, 0x2c, 0x3c, 0x7c, 0x5c, 0x1c, 0x14, 0x54, 0x74, 0x34, 0x24, 0x64, 0x44, 0x04, 0x06, 0x46, 0x66, 0x26, 0x36, 0x76, 0x56, 0x16, 0x1e, 0x5e, 0x7e, 0x3e, 0x2e, 0x6e, 0x4e, 0x0e, 0x0a, 0x4a, 0x6a, 0x2a, 0x3a, 0x7a, 0x5a, 0x1a, 0x12, 0x52, 0x72, 0x32, 0x22, 0x62, 0x42, 0x02, 0x03, 0x43, 0x63, 0x23, 0x33, 0x73, 0x53, 0x13, 0x1b, 0x5b, 0x7b, 0x3b, 0x2b, 0x6b, 0x4b, 0x0b, 0x0f, 0x4f, 0x6f, 0x2f, 0x3f, 0x7f, 0x5f, 0x1f, 0x17, 0x57, 0x77, 0x37, 0x27, 0x67, 0x47, 0x07, 0x05, 0x45,) -assert(address_sequence == expected_address_sequence) +verify_blocks = programming_data['verify'] +program_blocks = programming_data['program'] ####################################################################### # Calculate CRC of data read from CPLD during the second verification @@ -75,9 +184,6 @@ assert(address_sequence == expected_address_sequence) # bits (turning them to zero) and extending rows to the next full byte. ####################################################################### -data = data[1] -byte_count = (274 + 7) // 8 - if args.checksum: if args.crcmod: # Use a proper CRC library @@ -89,9 +195,56 @@ if args.checksum: from dumb_crc32 import DumbCRC32 crc = DumbCRC32() - for address, data, mask in data: + verify_block = verify_blocks[1] + for address, data, mask in verify_block: valid_data = data & mask - bytes = valid_data.to_bytes(byte_count, byteorder='little') + bytes = valid_data.to_bytes(bytes_of_data, byteorder='little') crc.update(bytes) print('0x%s' % crc.hexdigest().lower()) + +if args.code: + program_sram = make_sram_program(program_blocks) + verify_block = verify_blocks[1] + verify_masks = tuple(frozenset(extract_mask(verify_block))) + verify_mask_index = dict([(k, v) for v, k in enumerate(verify_masks)]) + verify_mask_row_index = [verify_mask_index[row['mask']] for row in verify_block] + + result = [] + result.extend(( + '/* WARNING: Auto-generated file. Do not edit. */', + '', + '#include ', + '', + '#define CPLD_XC2C64A_ROWS (%d)' % len(program_sram), + '#define CPLD_XC2C64A_BITS_IN_ROW (%d)' % bits_of_data, + '#define CPLD_XC2C64A_BYTES_IN_ROW ((CPLD_XC2C64A_BITS_IN_ROW + 7) / 8)', + '', + 'const uint8_t cpld_xc2c64a_row_address[CPLD_XC2C64A_ROWS] = {', + )) + result.extend(['\t%s' % line for line in hex_lines(extract_addresses(program_sram))]) + result.extend(( + '};', + '', + 'const uint8_t cpld_xc2c64a_row_data_sram[CPLD_XC2C64A_ROWS][CPLD_XC2C64A_BYTES_IN_ROW] = {', + )) + data_lines = [', '.join(['0x%02x' % n for n in row['data'].to_bytes(bytes_of_data, byteorder='little')]) for row in program_sram] + result.extend(['\t{ %s },' % line for line in data_lines]) + result.extend(( + '};', + '', + 'const uint8_t cpld_xc2c64a_mask[%d][CPLD_XC2C64A_BYTES_IN_ROW] = {' % len(verify_masks), + )) + verify_mask_lines = [', '.join(['0x%02x' % n for n in mask.to_bytes(bytes_of_data, byteorder='little')]) for mask in verify_masks] + result.extend(['\t{ %s },' % line for line in verify_mask_lines]) + result.extend(( + '};', + '', + 'const uint8_t cpld_xc2c64a_row_mask_index[CPLD_XC2C64A_ROWS] = {', + )) + result.extend(['\t%s' % line for line in dec_lines(verify_mask_row_index)]) + result.extend(( + '};', + '', + )) + print('\n'.join(result)) diff --git a/firmware/tools/xsvf.py b/firmware/tools/xsvf.py index b6e60332..ecd3c2c5 100644 --- a/firmware/tools/xsvf.py +++ b/firmware/tools/xsvf.py @@ -28,6 +28,9 @@ class XSVFParser(object): 0x17: self.XWAIT , } + def tdomask(self): + return self._xtdomask + def read_byte(self): return self.read_bytes(1)[0] From a04ed17a654c06f80e569f5da5110512fd4dabd2 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Feb 2019 20:19:51 -0800 Subject: [PATCH 10/15] CPLD tool: Ignore .pyc files that get pooped out with firmware tools. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index b2507075..d26a771d 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,5 @@ firmware/cpld/**/*.xst firmware/cpld/**/*.xwbt firmware/**/build + +*.pyc From e7424dfcdc32f2a3f2d4a36be93be4523253761d Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Feb 2019 21:19:40 -0800 Subject: [PATCH 11/15] CPLD tool: Tweaks to produce cleaner program and verify structs. --- firmware/tools/cpld_crc.py | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/firmware/tools/cpld_crc.py b/firmware/tools/cpld_crc.py index 7739d851..ac70742f 100755 --- a/firmware/tools/cpld_crc.py +++ b/firmware/tools/cpld_crc.py @@ -214,36 +214,27 @@ if args.code: result.extend(( '/* WARNING: Auto-generated file. Do not edit. */', '', - '#include ', + '#include ', '', - '#define CPLD_XC2C64A_ROWS (%d)' % len(program_sram), - '#define CPLD_XC2C64A_BITS_IN_ROW (%d)' % bits_of_data, - '#define CPLD_XC2C64A_BYTES_IN_ROW ((CPLD_XC2C64A_BITS_IN_ROW + 7) / 8)', - '', - 'const uint8_t cpld_xc2c64a_row_address[CPLD_XC2C64A_ROWS] = {', - )) - result.extend(['\t%s' % line for line in hex_lines(extract_addresses(program_sram))]) - result.extend(( - '};', - '', - 'const uint8_t cpld_xc2c64a_row_data_sram[CPLD_XC2C64A_ROWS][CPLD_XC2C64A_BYTES_IN_ROW] = {', + 'const cpld_xc2c64a_program_t cpld_hackrf_program_sram = { {', )) data_lines = [', '.join(['0x%02x' % n for n in row['data'].to_bytes(bytes_of_data, byteorder='little')]) for row in program_sram] - result.extend(['\t{ %s },' % line for line in data_lines]) + result.extend(['\t{ { %s } },' % line for line in data_lines]) result.extend(( - '};', + '} };', '', - 'const uint8_t cpld_xc2c64a_mask[%d][CPLD_XC2C64A_BYTES_IN_ROW] = {' % len(verify_masks), + 'const cpld_xc2c64a_verify_t cpld_hackrf_verify = {', + '\t.mask = {', )) verify_mask_lines = [', '.join(['0x%02x' % n for n in mask.to_bytes(bytes_of_data, byteorder='little')]) for mask in verify_masks] - result.extend(['\t{ %s },' % line for line in verify_mask_lines]) + result.extend(['\t\t{ { %s } },' % line for line in verify_mask_lines]) result.extend(( - '};', - '', - 'const uint8_t cpld_xc2c64a_row_mask_index[CPLD_XC2C64A_ROWS] = {', + '\t},' + '\t.mask_index = {', )) - result.extend(['\t%s' % line for line in dec_lines(verify_mask_row_index)]) + result.extend(['\t\t%s' % line for line in dec_lines(verify_mask_row_index)]) result.extend(( + '\t}', '};', '', )) From afb55e18dd90a9bf58b0d1705e9028b9b3bf5f2e Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Feb 2019 21:22:08 -0800 Subject: [PATCH 12/15] CPLD: Load bitstream to SRAM at start-up. --- firmware/CMakeLists.txt | 18 ++++ firmware/common/cpld_xc2c.c | 168 +++++++++++++++++++++++------ firmware/common/cpld_xc2c.h | 40 ++++++- firmware/hackrf_usb/CMakeLists.txt | 7 ++ firmware/hackrf_usb/hackrf_usb.c | 13 +++ firmware/hackrf_usb/usb_api_cpld.c | 2 +- 6 files changed, 216 insertions(+), 32 deletions(-) diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index c458b7f1..5dd9fca3 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -25,5 +25,23 @@ set(CMAKE_TOOLCHAIN_FILE toolchain-arm-cortex-m.cmake) project (hackrf_firmware_all C) +SET(PATH_HACKRF_FIRMWARE ${CMAKE_CURRENT_LIST_DIR}) +SET(PATH_HACKRF_CPLD_XSVF ${PATH_HACKRF_FIRMWARE}/cpld/sgpio_if/default.xsvf) +SET(PATH_HACKRF ${PATH_HACKRF_FIRMWARE}/..) +SET(PATH_HACKRF_FIRMWARE_COMMON ${PATH_HACKRF_FIRMWARE}/common) +SET(LIBOPENCM3 ${PATH_HACKRF_FIRMWARE}/libopencm3) +SET(PATH_DFU_PY ${PATH_HACKRF_FIRMWARE}/dfu.py) +SET(PATH_XSVF_TOOL ${PATH_HACKRF_FIRMWARE}/tools/cpld_crc.py) +set(PATH_HACKRF_CPLD_DATA_C ${CMAKE_CURRENT_BINARY_DIR}/hackrf_cpld_data.c) + +include(ExternalProject) +ExternalProject_Add(libopencm3 + SOURCE_DIR "${LIBOPENCM3}" + BUILD_IN_SOURCE true + DOWNLOAD_COMMAND "" + CONFIGURE_COMMAND "" + INSTALL_COMMAND "" +) + add_subdirectory(blinky) add_subdirectory(hackrf_usb) diff --git a/firmware/common/cpld_xc2c.c b/firmware/common/cpld_xc2c.c index 4220abb5..d43221b4 100644 --- a/firmware/common/cpld_xc2c.c +++ b/firmware/common/cpld_xc2c.c @@ -26,10 +26,6 @@ #include #include -#define CPLD_XC2C64A_ROWS (98) -#define CPLD_XC2C64A_BITS_IN_ROW (274) -#define CPLD_XC2C64A_BYTES_IN_ROW ((CPLD_XC2C64A_BITS_IN_ROW + 7) / 8) - static const uint8_t cpld_xc2c64a_row_address[CPLD_XC2C64A_ROWS] = { 0x00, 0x40, 0x60, 0x20, 0x30, 0x70, 0x50, 0x10, 0x18, 0x58, 0x78, 0x38, 0x28, 0x68, 0x48, 0x08, 0x0c, 0x4c, 0x6c, 0x2c, 0x3c, 0x7c, 0x5c, 0x1c, 0x14, 0x54, 0x74, 0x34, 0x24, 0x64, 0x44, 0x04, @@ -40,25 +36,6 @@ static const uint8_t cpld_xc2c64a_row_address[CPLD_XC2C64A_ROWS] = { 0x05, 0x45, }; -static const uint8_t cpld_xc2c64a_mask[6][CPLD_XC2C64A_BYTES_IN_ROW] = { - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }, - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, }, - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, }, - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xf8, 0x1f, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, }, - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x80, 0x1f, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, }, - { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x80, 0x07, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, }, -}; - -static const uint8_t cpld_xc2c64a_row_mask_index[CPLD_XC2C64A_ROWS] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 2, 2, 3, 4, 2, 2, 2, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 2, 2, 2, 2, 2, 5, 2, 2, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, -}; - typedef enum { CPLD_XC2C_IR_INTEST = 0b00000010, CPLD_XC2C_IR_BYPASS = 0b11111111, @@ -121,12 +98,11 @@ static bool cpld_xc2c_jtag_clock(const jtag_t* const jtag, const uint32_t tms, c return gpio_read(jtag->gpio->gpio_tdo); } -static void cpld_xc2c_jtag_shift_ptr(const jtag_t* const jtag, uint8_t* const tdi_tdo, const size_t count) { - for(size_t i=0; i> 3; const size_t bit_n = i & 7; const uint32_t mask = (1U << bit_n); - const bool tms = (i == (count - 1)); const uint32_t tdo = cpld_xc2c_jtag_clock(jtag, tms, tdi_tdo[byte_n] & mask) ? 1 : 0; @@ -135,6 +111,13 @@ static void cpld_xc2c_jtag_shift_ptr(const jtag_t* const jtag, uint8_t* const td } } +static void cpld_xc2c_jtag_shift_ptr(const jtag_t* const jtag, uint8_t* const tdi_tdo, const size_t count) { + if( count > 0 ) { + cpld_xc2c_jtag_shift_ptr_tms(jtag, tdi_tdo, 0, count - 1, false); + cpld_xc2c_jtag_shift_ptr_tms(jtag, tdi_tdo, count - 1, count, true); + } +} + static uint32_t cpld_xc2c_jtag_shift_u32(const jtag_t* const jtag, const uint32_t tms, const uint32_t tdi, const size_t count) { uint32_t tdo = 0; @@ -189,11 +172,18 @@ static uint8_t cpld_xc2c_jtag_shift_ir(const jtag_t* const jtag, const cpld_xc2c return cpld_xc2c_jtag_shift_ir_pause(jtag, ir, 0); } +static void cpld_xc2c_jtag_reset(const jtag_t* const jtag) { + /* Five TMS=1 to reach Test-Logic-Reset from any point in the TAP state diagram. + */ + cpld_xc2c_jtag_shift_u32(jtag, 0b11111, 0, 5); +} + static void cpld_xc2c_jtag_reset_and_idle(const jtag_t* const jtag) { /* Five TMS=1 to reach Test-Logic-Reset from any point in the TAP state diagram. * One TMS=0 to move from Test-Logic-Reset to Run-Test-Idle. */ - cpld_xc2c_jtag_shift_u32(jtag, 0b011111, 0, 6); + cpld_xc2c_jtag_reset(jtag); + cpld_xc2c_jtag_shift_u32(jtag, 0, 0, 1); } static uint32_t cpld_xc2c_jtag_idcode(const jtag_t* const jtag) { @@ -215,6 +205,20 @@ static void cpld_xc2c_jtag_conld(const jtag_t* const jtag) { static void cpld_xc2c_jtag_enable(const jtag_t* const jtag) { cpld_xc2c_jtag_shift_ir(jtag, CPLD_XC2C_IR_ISC_ENABLE); + cpld_xc2c_jtag_clocks(jtag, 800); +} + +static void cpld_xc2c_jtag_disable(const jtag_t* const jtag) { + cpld_xc2c_jtag_shift_ir(jtag, CPLD_XC2C_IR_ISC_DISABLE); + cpld_xc2c_jtag_clocks(jtag, 100); +} + +static void cpld_xc2c_jtag_sram_write(const jtag_t* const jtag) { + cpld_xc2c_jtag_shift_ir(jtag, CPLD_XC2C_IR_ISC_WRITE); +} + +static void cpld_xc2c_jtag_sram_read(const jtag_t* const jtag) { + cpld_xc2c_jtag_shift_ir(jtag, CPLD_XC2C_IR_ISC_SRAM_READ); } static uint32_t cpld_xc2c_jtag_bypass(const jtag_t* const jtag, const bool shift_dr) { @@ -265,7 +269,11 @@ static void cpld_xc2c64a_jtag_read_row(const jtag_t* const jtag, uint8_t address cpld_xc2c_jtag_clocks(jtag, 100); } -bool cpld_xc2c64a_jtag_checksum(const jtag_t* const jtag, uint32_t* const crc_value) { +bool cpld_xc2c64a_jtag_checksum( + const jtag_t* const jtag, + const cpld_xc2c64a_verify_t* const verify, + uint32_t* const crc_value +) { cpld_xc2c_jtag_reset_and_idle(jtag); if( cpld_xc2c64a_jtag_idcode_ok(jtag) && cpld_xc2c_jtag_read_write_protect(jtag) && @@ -287,9 +295,9 @@ bool cpld_xc2c64a_jtag_checksum(const jtag_t* const jtag, uint32_t* const crc_va const size_t address = cpld_xc2c64a_row_address[row]; cpld_xc2c64a_jtag_read_row(jtag, address, dr); - const size_t mask_index = cpld_xc2c64a_row_mask_index[row]; + const size_t mask_index = verify->mask_index[row]; for(size_t i=0; imask[mask_index].value[i]; } /* Important checksum calculation NOTE: @@ -318,3 +326,103 @@ bool cpld_xc2c64a_jtag_checksum(const jtag_t* const jtag, uint32_t* const crc_va return false; } + +static void cpld_xc2c64a_jtag_sram_write_row(const jtag_t* const jtag, uint8_t address, const uint8_t* const data) { + uint8_t write[CPLD_XC2C64A_BYTES_IN_ROW]; + memcpy(&write[0], data, sizeof(write)); + + /* Update-IR or Run-Test/Idle -> Shift-DR */ + cpld_xc2c_jtag_shift_u32(jtag, 0b001, 0b000, 3); + + /* Shift-DR -> Shift-DR */ + cpld_xc2c_jtag_shift_ptr_tms(jtag, &write[0], 0, CPLD_XC2C64A_BITS_IN_ROW, false); + + /* Shift-DR -> Exit1-DR */ + cpld_xc2c_jtag_shift_u32(jtag, 0b1000000, address, 7); + + /* Exit1-DR -> Update-DR -> Run-Test/Idle */ + cpld_xc2c_jtag_shift_u32(jtag, 0b01, 0b00, 2); +} + +static void cpld_xc2c64a_jtag_sram_read_row(const jtag_t* const jtag, uint8_t* const data, const uint8_t next_address) { + /* Run-Test/Idle -> Shift-DR */ + cpld_xc2c_jtag_shift_u32(jtag, 0b001, 0b000, 3); + + /* Shift-DR */ + cpld_xc2c_jtag_shift_ptr_tms(jtag, data, 0, CPLD_XC2C64A_BITS_IN_ROW, false); + + /* Shift-DR -> Exit1-DR */ + cpld_xc2c_jtag_shift_u32(jtag, 0b1000000, next_address, 7); + + /* Weird, non-IEEE1532 compliant path through TAP machine, described in Xilinx + * Programmer Qualification Specification, applicable only to XC2C64/A. + * Exit1-DR -> Pause-DR -> Exit2-DR -> Update-DR -> Run-Test/Idle + */ + cpld_xc2c_jtag_shift_u32(jtag, 0b0110, 0b0000, 4); +} + +static bool cpld_xc2c64a_jtag_sram_compare_row(const jtag_t* const jtag, const uint8_t* const expected, const uint8_t* const mask, const uint8_t next_address) { + /* Run-Test/Idle -> Shift-DR */ + uint8_t read[CPLD_XC2C64A_BYTES_IN_ROW]; + memset(read, 0xff, sizeof(read)); + cpld_xc2c64a_jtag_sram_read_row(jtag, &read[0], next_address); + + bool matched = true; + if( (expected != NULL) && (mask != NULL) ) { + for(size_t i=0; irow[row].data[0]); + } + + cpld_xc2c_jtag_disable(jtag); + cpld_xc2c_jtag_bypass(jtag, false); + cpld_xc2c_jtag_reset(jtag); +} + +bool cpld_xc2c64a_jtag_sram_verify( + const jtag_t* const jtag, + const cpld_xc2c64a_program_t* const program, + const cpld_xc2c64a_verify_t* const verify +) { + cpld_xc2c_jtag_reset_and_idle(jtag); + cpld_xc2c_jtag_enable(jtag); + + cpld_xc2c_jtag_sram_read(jtag); + + /* Tricky loop to read dummy row first, then first address, then loop back to get + * the first row's data. + */ + bool matched = true; + for(size_t address_row=0; address_row<=CPLD_XC2C64A_ROWS; address_row++) { + const int data_row = (int)address_row - 1; + const size_t mask_index = (data_row >= 0) ? verify->mask_index[data_row] : 0; + const uint8_t* const expected = (data_row >= 0) ? &program->row[data_row].data[0] : NULL; + const uint8_t* const mask = (data_row >= 0) ? &verify->mask[mask_index].value[0] : NULL; + const uint8_t next_address = (address_row < CPLD_XC2C64A_ROWS) ? cpld_xc2c64a_row_address[address_row] : 0; + matched &= cpld_xc2c64a_jtag_sram_compare_row(jtag, expected, mask, next_address); + } + + cpld_xc2c_jtag_disable(jtag); + cpld_xc2c_jtag_bypass(jtag, false); + cpld_xc2c_jtag_reset(jtag); + + return matched; +} diff --git a/firmware/common/cpld_xc2c.h b/firmware/common/cpld_xc2c.h index d9b9231b..ccd3c5f1 100644 --- a/firmware/common/cpld_xc2c.h +++ b/firmware/common/cpld_xc2c.h @@ -27,6 +27,44 @@ #include "cpld_jtag.h" -bool cpld_xc2c64a_jtag_checksum(const jtag_t* const jtag, uint32_t* const crc_value); +/* Xilinx CoolRunner II XC2C64A bitstream attributes */ +#define CPLD_XC2C64A_ROWS (98) +#define CPLD_XC2C64A_BITS_IN_ROW (274) +#define CPLD_XC2C64A_BYTES_IN_ROW ((CPLD_XC2C64A_BITS_IN_ROW + 7) / 8) + +typedef struct { + uint8_t data[CPLD_XC2C64A_BYTES_IN_ROW]; +} cpld_xc2c64a_row_data_t; + +typedef struct { + cpld_xc2c64a_row_data_t row[CPLD_XC2C64A_ROWS]; +} cpld_xc2c64a_program_t; + +typedef struct { + uint8_t value[CPLD_XC2C64A_BYTES_IN_ROW]; +} cpld_xc2c64a_row_mask_t; + +typedef struct { + cpld_xc2c64a_row_mask_t mask[6]; + uint8_t mask_index[CPLD_XC2C64A_ROWS]; +} cpld_xc2c64a_verify_t; + +bool cpld_xc2c64a_jtag_checksum( + const jtag_t* const jtag, + const cpld_xc2c64a_verify_t* const verify, + uint32_t* const crc_value +); +void cpld_xc2c64a_jtag_sram_write( + const jtag_t* const jtag, + const cpld_xc2c64a_program_t* const program +); +bool cpld_xc2c64a_jtag_sram_verify( + const jtag_t* const jtag, + const cpld_xc2c64a_program_t* const program, + const cpld_xc2c64a_verify_t* const verify +); + +extern const cpld_xc2c64a_program_t cpld_hackrf_program_sram; +extern const cpld_xc2c64a_verify_t cpld_hackrf_verify; #endif/*__CPLD_XC2C_H__*/ diff --git a/firmware/hackrf_usb/CMakeLists.txt b/firmware/hackrf_usb/CMakeLists.txt index e6c827c1..43865bdb 100644 --- a/firmware/hackrf_usb/CMakeLists.txt +++ b/firmware/hackrf_usb/CMakeLists.txt @@ -25,6 +25,12 @@ project(hackrf_usb C) include(../hackrf-common.cmake) +add_custom_command( + OUTPUT ${PATH_HACKRF_CPLD_DATA_C} + COMMAND ${PATH_XSVF_TOOL} --code ${PATH_HACKRF_CPLD_XSVF} >${PATH_HACKRF_CPLD_DATA_C} + DEPENDS ${PATH_XSVF_TOOL} ${PATH_HACKRF_CPLD_XSVF} +) + set(SRC_M4 hackrf_usb.c "${PATH_HACKRF_FIRMWARE_COMMON}/tuning.c" @@ -48,6 +54,7 @@ set(SRC_M4 "${PATH_HACKRF_FIRMWARE_COMMON}/fault_handler.c" "${PATH_HACKRF_FIRMWARE_COMMON}/cpld_jtag.c" "${PATH_HACKRF_FIRMWARE_COMMON}/cpld_xc2c.c" + "${PATH_HACKRF_CPLD_DATA_C}" "${PATH_HACKRF_FIRMWARE_COMMON}/crc.c" "${PATH_HACKRF_FIRMWARE_COMMON}/xapp058/lenval.c" "${PATH_HACKRF_FIRMWARE_COMMON}/xapp058/micro.c" diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 8bb8b417..a575963c 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -45,6 +45,7 @@ #include "usb_api_sweep.h" #include "usb_api_transceiver.h" #include "usb_bulk_buffer.h" +#include "cpld_xc2c.h" #include "hackrf-ui.h" @@ -209,6 +210,14 @@ void usb_set_descriptor_by_serial_number(void) } } +static bool cpld_jtag_sram_load(jtag_t* const jtag) { + cpld_jtag_take(jtag); + cpld_xc2c64a_jtag_sram_write(jtag, &cpld_hackrf_program_sram); + const bool success = cpld_xc2c64a_jtag_sram_verify(jtag, &cpld_hackrf_program_sram, &cpld_hackrf_verify); + cpld_jtag_release(jtag); + return success; +} + int main(void) { pin_setup(); enable_1v8_power(); @@ -220,6 +229,10 @@ int main(void) { #endif cpu_clock_init(); + if( !cpld_jtag_sram_load(&jtag_cpld) ) { + // TODO: Fail, do not continue booting. + } + #ifndef DFU_MODE usb_set_descriptor_by_serial_number(); #endif diff --git a/firmware/hackrf_usb/usb_api_cpld.c b/firmware/hackrf_usb/usb_api_cpld.c index f1cf1ec4..9b94e27c 100644 --- a/firmware/hackrf_usb/usb_api_cpld.c +++ b/firmware/hackrf_usb/usb_api_cpld.c @@ -106,7 +106,7 @@ usb_request_status_t usb_vendor_request_cpld_checksum( if (stage == USB_TRANSFER_STAGE_SETUP) { cpld_jtag_take(&jtag_cpld); - const bool checksum_success = cpld_xc2c64a_jtag_checksum(&jtag_cpld, &cpld_crc); + const bool checksum_success = cpld_xc2c64a_jtag_checksum(&jtag_cpld, &cpld_hackrf_verify, &cpld_crc); cpld_jtag_release(&jtag_cpld); if(!checksum_success) { From 75adda314e068a0c90c4d3cadd410da67b025285 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Feb 2019 21:32:03 -0800 Subject: [PATCH 13/15] LED: Refactor halt function from CPLD update to core API. Also call if CPLD load fails. --- firmware/common/hackrf_core.c | 15 +++++++++++++++ firmware/common/hackrf_core.h | 1 + firmware/hackrf_usb/hackrf_usb.c | 2 +- firmware/hackrf_usb/usb_api_cpld.c | 17 +---------------- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/firmware/common/hackrf_core.c b/firmware/common/hackrf_core.c index 5ca74b7d..4d01c0e2 100644 --- a/firmware/common/hackrf_core.c +++ b/firmware/common/hackrf_core.c @@ -906,3 +906,18 @@ void led_toggle(const led_t led) { void hw_sync_enable(const hw_sync_mode_t hw_sync_mode){ gpio_write(&gpio_hw_sync_enable, hw_sync_mode==1); } + +void halt_and_flash(const uint32_t duration) { + /* blink LED1, LED2, and LED3 */ + while (1) + { + led_on(LED1); + led_on(LED2); + led_on(LED3); + delay(duration); + led_off(LED1); + led_off(LED2); + led_off(LED3); + delay(duration); + } +} \ No newline at end of file diff --git a/firmware/common/hackrf_core.h b/firmware/common/hackrf_core.h index b30cfdff..57d01ebe 100644 --- a/firmware/common/hackrf_core.h +++ b/firmware/common/hackrf_core.h @@ -315,6 +315,7 @@ void led_toggle(const led_t led); void hw_sync_enable(const hw_sync_mode_t hw_sync_mode); +void halt_and_flash(const uint32_t duration); #ifdef __cplusplus } diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index a575963c..2c8fca1a 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -230,7 +230,7 @@ int main(void) { cpu_clock_init(); if( !cpld_jtag_sram_load(&jtag_cpld) ) { - // TODO: Fail, do not continue booting. + halt_and_flash(6000000); } #ifndef DFU_MODE diff --git a/firmware/hackrf_usb/usb_api_cpld.c b/firmware/hackrf_usb/usb_api_cpld.c index 9b94e27c..fc5ab51f 100644 --- a/firmware/hackrf_usb/usb_api_cpld.c +++ b/firmware/hackrf_usb/usb_api_cpld.c @@ -61,8 +61,6 @@ static void refill_cpld_buffer(void) void cpld_update(void) { - #define WAIT_LOOP_DELAY (6000000) - int i; int error; usb_queue_flush_endpoint(&usb_endpoint_bulk_in); @@ -75,20 +73,7 @@ void cpld_update(void) refill_cpld_buffer); if(error == 0) { - /* blink LED1, LED2, and LED3 on success */ - while (1) - { - led_on(LED1); - led_on(LED2); - led_on(LED3); - for (i = 0; i < WAIT_LOOP_DELAY; i++) /* Wait a bit. */ - __asm__("nop"); - led_off(LED1); - led_off(LED2); - led_off(LED3); - for (i = 0; i < WAIT_LOOP_DELAY; i++) /* Wait a bit. */ - __asm__("nop"); - } + halt_and_flash(6000000); }else { /* LED3 (Red) steady on error */ From 057b9273d5094bf4f0a27e8a17ec548d28cfb8fc Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Feb 2019 22:00:46 -0800 Subject: [PATCH 14/15] CPLD tool: Rename to cpld_bitstream.py --- firmware/CMakeLists.txt | 2 +- firmware/hackrf_usb/CMakeLists.txt | 4 ++-- firmware/tools/{cpld_crc.py => cpld_bitstream.py} | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename firmware/tools/{cpld_crc.py => cpld_bitstream.py} (99%) diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index 5dd9fca3..8d96480e 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -31,7 +31,7 @@ SET(PATH_HACKRF ${PATH_HACKRF_FIRMWARE}/..) SET(PATH_HACKRF_FIRMWARE_COMMON ${PATH_HACKRF_FIRMWARE}/common) SET(LIBOPENCM3 ${PATH_HACKRF_FIRMWARE}/libopencm3) SET(PATH_DFU_PY ${PATH_HACKRF_FIRMWARE}/dfu.py) -SET(PATH_XSVF_TOOL ${PATH_HACKRF_FIRMWARE}/tools/cpld_crc.py) +SET(PATH_CPLD_BITSTREAM_TOOL ${PATH_HACKRF_FIRMWARE}/tools/cpld_bitstream.py) set(PATH_HACKRF_CPLD_DATA_C ${CMAKE_CURRENT_BINARY_DIR}/hackrf_cpld_data.c) include(ExternalProject) diff --git a/firmware/hackrf_usb/CMakeLists.txt b/firmware/hackrf_usb/CMakeLists.txt index 43865bdb..fb58e08e 100644 --- a/firmware/hackrf_usb/CMakeLists.txt +++ b/firmware/hackrf_usb/CMakeLists.txt @@ -27,8 +27,8 @@ include(../hackrf-common.cmake) add_custom_command( OUTPUT ${PATH_HACKRF_CPLD_DATA_C} - COMMAND ${PATH_XSVF_TOOL} --code ${PATH_HACKRF_CPLD_XSVF} >${PATH_HACKRF_CPLD_DATA_C} - DEPENDS ${PATH_XSVF_TOOL} ${PATH_HACKRF_CPLD_XSVF} + COMMAND ${PATH_CPLD_BITSTREAM_TOOL} --code ${PATH_HACKRF_CPLD_XSVF} >${PATH_HACKRF_CPLD_DATA_C} + DEPENDS ${PATH_CPLD_BITSTREAM_TOOL} ${PATH_HACKRF_CPLD_XSVF} ) set(SRC_M4 diff --git a/firmware/tools/cpld_crc.py b/firmware/tools/cpld_bitstream.py similarity index 99% rename from firmware/tools/cpld_crc.py rename to firmware/tools/cpld_bitstream.py index ac70742f..6825726d 100755 --- a/firmware/tools/cpld_crc.py +++ b/firmware/tools/cpld_bitstream.py @@ -196,8 +196,8 @@ if args.checksum: crc = DumbCRC32() verify_block = verify_blocks[1] - for address, data, mask in verify_block: - valid_data = data & mask + for row in verify_block: + valid_data = row['data'] & row['mask'] bytes = valid_data.to_bytes(bytes_of_data, byteorder='little') crc.update(bytes) From 9ba4e50ee1426c7d0d6e6bf28eb16ec41d5524d2 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Thu, 28 Feb 2019 22:01:05 -0800 Subject: [PATCH 15/15] CPLD tool: Fix --checksum breakage. --- firmware/tools/cpld_bitstream.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/tools/cpld_bitstream.py b/firmware/tools/cpld_bitstream.py index 6825726d..ac70742f 100755 --- a/firmware/tools/cpld_bitstream.py +++ b/firmware/tools/cpld_bitstream.py @@ -196,8 +196,8 @@ if args.checksum: crc = DumbCRC32() verify_block = verify_blocks[1] - for row in verify_block: - valid_data = row['data'] & row['mask'] + for address, data, mask in verify_block: + valid_data = data & mask bytes = valid_data.to_bytes(bytes_of_data, byteorder='little') crc.update(bytes)