utils: bitstream_analyzer: Cleanup bitstream analyzer and add TODOs Signed-off-by: Tomasz Michalak <tmichalak@antmicro.com>
diff --git a/utils/bitstream_analyzer.py b/utils/bitstream_analyzer.py index 8411445..6bbe500 100755 --- a/utils/bitstream_analyzer.py +++ b/utils/bitstream_analyzer.py
@@ -1,11 +1,11 @@ #!/usr/bin/env python3 ''' -Spartan 6 bitstream analyzer tool. +UltraScalePlus bitstream analyzer tool. -This script reads a Spartan6 bitstream and prints out some useful information. +This script reads a UltraScalePlus bitstream and prints out some useful information. It can also create a frames file with the configuration data words. The bitstream is analyzed word by word and interpreted according to -the UG380 Configuration User Guide. +the UG570 Configuration User Guide. The tool can be used to derive the initialization, startup and finalization sequence as well as the configuration data. The latter is written to a frames @@ -61,50 +61,10 @@ opcodes = ("NOP", "READ", "WRITE", "UNKNOWN") - -def KnuthMorrisPratt(text, pattern): - ''' - Yields all starting positions of copies of the pattern in the text. - Calling conventions are similar to string.find, but its arguments can be - lists or iterators, not just strings, it returns all matches, not just - the first one, and it does not need the whole text in memory at once. - Whenever it yields, it will have read the text exactly up to and including - the match that caused the yield. - ''' - - # allow indexing into pattern and protect against change during yield - pattern = list(pattern) - - # build table of shift amounts - shifts = [1] * (len(pattern) + 1) - shift = 1 - for pos in range(len(pattern)): - while shift <= pos and pattern[pos] != pattern[pos - shift]: - shift += shifts[pos - shift] - shifts[pos + 1] = shift - - # do the actual search - startPos = 0 - matchLen = 0 - for c in text: - while matchLen == len(pattern) or \ - matchLen >= 0 and pattern[matchLen] != c: - startPos += shifts[matchLen] - matchLen -= shifts[matchLen] - matchLen += 1 - if matchLen == len(pattern): - yield startPos - - class Bitstream: def __init__(self, file_name, verbose=False): self.frame_data = [] - self.idcode = 0 - self.exp_sign = 0 - self.far_min = 0 - self.far_maj = 0 - self.curr_fdri_write_len = 0 - self.curr_crc_check = 0 + self.fdri_write_len = 0 self.fdri_in_progress = False self.words = [] with open(file_name, "rb") as f: @@ -113,22 +73,20 @@ self.words.append(int.from_bytes(word, byteorder = 'big')) word = f.read(4) pos, self.header = self.get_header() - self.body = self.words[pos:] + self.body = self.words[pos + 1:] self.parse_bitstream(verbose) def get_header(self): - pos = next(KnuthMorrisPratt(self.words, [0xaa995566])) - return pos + 1, self.words[:pos + 1] + '''Return position and content of header''' + pos = self.words.index(0xaa995566) + return pos, self.words[:pos + 1] def parse_bitstream(self, verbose): payload_len = 0 for word in self.body: if payload_len > 0: - if verbose: - print("\tWord: ", hex(word)) - #payload_len = self.parse_reg( - # reg_addr, word, payload_len, verbose) - payload_len -= 1 + payload_len = self.parse_reg( + reg_addr, word, payload_len, verbose) continue else: packet_header = self.parse_packet_header(word) @@ -137,12 +95,18 @@ words = packet_header["word_count"] type = packet_header["type"] if verbose: - print( - "\tWord: ", hex(word), + if not opcode: + print("\n\tNOP") + else: + print( + "\n\tConfiguration Register Word: ", hex(word), 'Type: {}, Op: {}, Addr: {} ({}), Words: {}'.format( type, opcodes[opcode], conf_regs[reg_addr] if reg_addr in conf_regs else "UNKNOWN", reg_addr, words)) if opcode and reg_addr in conf_regs: payload_len = words + if conf_regs[reg_addr] == "FDRI" and type == 1: + self.fdri_in_progress = True + self.fdri_write_len = payload_len continue def parse_packet_header(self, word): @@ -162,207 +126,45 @@ "word_count": word_count } - def parse_command(self, word): - return cmd_reg_codes[word] + def parse_command(self, word, verbose): + if verbose: + print("\tCommand: {} ({})".format(cmd_reg_codes[word], word)) - def parse_cor1(self, word): - return word - - def parse_cor2(self, word): - return word - - def parse_ctl(self, word): - #decryption - dec = (word >> 6) & 1 - #security bits - sb = (word >> 4) & 3 - #persist - p = (word >> 3) & 1 - #use efuse - efuse = (word >> 2) & 1 - #crc extstat disable - crc = (word >> 1) & 1 - return { - "decryption": dec, - "security bits": sb, - "pesist": p, - "use efuse": efuse, - "crc extstat disable": crc - } - - def parse_cclk_freq(self, word): - ext_mclk = (word >> 14) & 1 - mclk_freq = word & 0x3FF - return (ext_mclk, mclk_freq) - - def parse_pwrdn(self, word): - en_eyes = (word >> 14) & 1 - filter_b = (word >> 5) & 1 - en_pgsr = (word >> 4) & 1 - en_pwrdn = (word >> 2) & 1 - keep_sclk = word & 1 - return { - "en_eyes": en_eyes, - "filter_b": filter_b, - "en_pgsr": en_pgsr, - "en_pwrdn": en_pwrdn, - "keep_sclk": keep_sclk - } - - def parse_eye_mask(self, word): - return word & 0xFF - - def parse_hc_opt(self, word): - return (word >> 6) & 1 - - def parse_cwdt(self, word): - return word - - def parse_pu_gwe(self, word): - return word & 0x3FF - - def parse_pu_gts(self, word): - return word & 0x3FF - - def parse_mode(self, word): - new_mode = (word >> 13) & 0x1 - buswidth = (word >> 11) & 0x3 - bootmode = (word >> 8) & 0x7 - bootvsel = word & 0xFF - return { - "new_mode": new_mode, - "buswidth": buswidth, - "bootmode": bootmode, - "bootvsel": bootvsel - } - - def parse_seu(self, word): - seu_freq = (word >> 4) & 0x3FF - seu_run_on_err = (word >> 3) & 0x1 - glut_mask = (word >> 1) & 0x1 - seu_enable = word & 0x1 - return { - "seu_freq": seu_freq, - "seu_run_on_err": seu_run_on_err, - "glut_mask": glut_mask, - "seu_enable": seu_enable - } + #TODO Add COR0 options parsing + def parse_cor0(self, word, verbose): + if verbose: + print("\tCOR0 options: {:X}".format(word)) def parse_reg(self, reg_addr, word, payload_len, verbose): + assert reg_addr in conf_regs reg = conf_regs[reg_addr] if reg == "CMD": - command = self.parse_command(word) - if verbose: - print("Command: {}\n".format(command)) - elif reg == "FLR": - frame_length = word - if verbose: - print("Frame length: {}\n".format(frame_length)) - elif reg == "COR1": - conf_options = self.parse_cor1(word) - if verbose: - print("COR1 options: {}\n".format(conf_options)) - elif reg == "COR2": - conf_options = self.parse_cor2(word) - if verbose: - print("COR2 options: {}\n".format(conf_options)) - elif reg == "IDCODE": - assert payload_len < 3 - if payload_len == 2: - self.idcode = word << 16 - elif payload_len == 1: - self.idcode |= word - if verbose: - print("IDCODE: {}\n".format(hex(self.idcode))) - elif reg == "MASK": - mask = word - if verbose: - print("Mask value: {}\n".format(mask)) - elif reg == "CTL": - ctl_options = self.parse_ctl(word) - if verbose: - print("CTL options: {}\n".format(ctl_options)) - elif reg == "CCLK_FREQ": - cclk_freq_options = self.parse_cclk_freq(word) - if verbose: - print("CCLK_FREQ options: {}\n".format(cclk_freq_options)) - elif reg == "PWRDN_REG": - suspend_reg_options = self.parse_pwrdn(word) - if verbose: - print("{} options: {}\n".format(reg, suspend_reg_options)) - elif reg == "EYE_MASK": - eye_mask = self.parse_eye_mask(word) - if verbose: - print("{} options: {}\n".format(reg, eye_mask)) - elif reg == "HC_OPT_REG": - hc_options = self.parse_hc_opt(word) - if verbose: - print("{} options: {}\n".format(reg, hc_options)) - elif reg == "CWDT": - cwdt_options = self.parse_cwdt(word) - if verbose: - print("{} options: {}\n".format(reg, cwdt_options)) - elif reg == "PU_GWE": - pu_gwe_sequence = self.parse_pu_gwe(word) - if verbose: - print("{} options: {}\n".format(reg, pu_gwe_sequence)) - elif reg == "PU_GTS": - pu_gts_sequence = self.parse_pu_gts(word) - if verbose: - print("{} options: {}\n".format(reg, pu_gts_sequence)) - elif reg == "MODE_REG": - mode_options = self.parse_mode(word) - if verbose: - print("{} options: {}\n".format(reg, mode_options)) - elif reg == "GENERAL1" or reg == "GENERAL2" \ - or reg == "GENERAL3" or reg == "GENERAL4" \ - or reg == "GENERAL5": - general_options = word - if verbose: - print("{} options: {}\n".format(reg, general_options)) - elif reg == "SEU_OPT": - seu_options = self.parse_seu(word) - if verbose: - print("{} options: {}\n".format(reg, seu_options)) - elif reg == "EXP_SIGN": - if payload_len == 2: - self.exp_sign = word << 16 - elif payload_len == 1: - self.exp_sign |= word - if verbose: - print("{}: {}\n".format(reg, self.exp_sign)) - elif reg == "FAR": - if payload_len == 2: - self.current_far_maj = word - elif payload_len == 1: - self.current_far_min = word - if verbose: - print( - "{}: {} FAR_MIN: {}\n".format( - reg, self.far_maj, self.far_min)) + self.parse_command(word, verbose) + elif reg == "COR0": + self.parse_cor0(word, verbose) elif reg == "FDRI": + # We are in progress of a FDRI operation + # Keep adding data words if self.fdri_in_progress: + if verbose: + print("\t{}. 0x{:X}".format(self.fdri_write_len - payload_len, word)) self.frame_data.append(word) if payload_len == 1: self.fdri_in_progress = False return 0 - elif payload_len == 2: - self.curr_fdri_write_len = (word & 0xFFF) << 16 - elif payload_len == 1: - self.curr_fdri_write_len |= word - self.fdri_in_progress = True + else: + #FIXME add Type 2 FDRI writes + assert False + #self.curr_fdri_write_len = word + #self.fdri_in_progress = True # Check if 0 words actually means read something - payload_len = self.curr_fdri_write_len + 2 - if verbose: - print("{}: {}\n".format(reg, self.curr_fdri_write_len)) - return payload_len - elif reg == "CRC": - if payload_len == 2: - self.curr_crc_check = (word & 0xFFF) << 16 - elif payload_len == 1: - self.curr_crc_check |= word - if verbose: - print("{}: {}\n".format(reg, self.curr_crc_check)) + #payload_len = self.curr_fdri_write_len + #if verbose: + # print("\t{}: {}\n".format(reg, self.curr_fdri_write_len)) + #return payload_len + else: + if verbose: + print("\tRegister: {} Value: 0x{:X}".format(reg, word)) payload_len -= 1 return payload_len @@ -370,26 +172,20 @@ '''Write frame data in a more readable format''' frame_stream = StringIO() for i in range(len(self.frame_data)): - if i % 65 == 0: - frame_stream.write("\nFrame {:4}\n".format(i // 65)) - #IOB word - if i % 65 == 32: - frame_stream.write( - "\n#{:3}:{:6}\n".format(i % 65, hex(self.frame_data[i]))) - else: - frame_stream.write( - "#{:3}:{:6},".format(i % 65, hex(self.frame_data[i]))) + if i % 93 == 0: + frame_stream.write("\nFrame {:4}\n".format(i // 93)) with open(file_name, "w") as f: print(frame_stream.getvalue(), file=f) + #FIXME Add storing of frame address def write_frames(self, file_name): '''Write configuration data to frames file''' frame_stream = StringIO() for i in range(len(self.frame_data)): - if i % 65 == 0: - frame_stream.write("0x{:08x} ".format(i // 65)) + if i % 93 == 0: + frame_stream.write("0x{:08x} ".format(i // 93)) frame_stream.write("0x{:04x}".format(self.frame_data[i])) - if i % 65 == 64: + if i % 93 == 92: frame_stream.write("\n") elif i < len(self.frame_data) - 1: frame_stream.write(",") @@ -403,8 +199,7 @@ print("Frame data length: ", len(bitstream.frame_data)) if args.frames_out: bitstream.write_frames(args.frames_out) - if verbose: - bitstream.write_frames_txt(args.frames_out + ".txt") + #bitstream.write_frames_txt(args.frames_out + ".txt") if __name__ == "__main__":