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__":