aboutsummaryrefslogblamecommitdiff
path: root/ecdsa_microcode_parser.py
blob: edd8328a368422d0caf3e45c24d2342d05ef8b03 (plain) (tree)
1
2
3
4
5
6
7
8




                           


                                                         










                                                                             


                                                                        

























                                                                               
                                        
        
                                        
        


                                        
        


                                        
        

                                        
                
                                        
        

                                        






























                                                                                  
                                                               


                                                                                
                                                               



                                                                              
                                                              


                                                              


































                                                                                                                                                   
                                                                                             
                                    

                                                                                                     
                                    



                                                                                                                                   
                                    

                                                                                              
                                    

                                                                                                 
                                    
                                                                                             


















                                                                                                   
                                                 
    

                                                 
    



                                                 
    

                                                 
    

                                                 
    
                                                 
    









































                                                                                                   
                                                                                                                
        






                                                                                                                                 
        

                                                                                                                 
        

                                                                                                                    
        
                                                                                                                
        


                                                   


                                                           
                                                     

                                                                    





                                                             




















                                                                            








                                                         








                                                     


                                                                  


                                                    


                                                                  
                                    
                                                            
























                                                           














                                                               









                                                                                                      

                                                                                                       
























































































































































































































































                                                                                      











                                                                                                                                                   






















                                                                                    
#
# ecdsa_microcode_parser.py
#
# Author: Pavel Shatov
#
# Copyright 2018 NORDUnet A/S
# Copyright 2021 The Commons Conservancy Cryptech Project
# SPDX-License-Identifier: BSD-3-Clause
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
#   contributors may be used to endorse or promote products derived from
#   this software without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Imports
import re
import sys
from enum import Enum

# Source File
C_FILES = [ "ecdsa_fpga_curve_microcode.cpp",
            "ecdsa_fpga_microcode.cpp"]

class MICROCODE_PARSER:

    # enumerate microcode pieces
    class MICROCODE_PIECE_ENUM(Enum):
    
        NONE                        = -1
        
        PREPARE                     =  0
        
        CYCLE_DOUBLE_R0             =  1
        CYCLE_DOUBLE_R1             =  2
        CYCLE_ADD                   =  3
        
        CYCLE_ADD_R0_AT_INFINITY    =  4
        CYCLE_ADD_R1_AT_INFINITY    =  5
        CYCLE_ADD_REGULAR           =  6
        
        CYCLE_K0                    =  7
        CYCLE_K1                    =  8
                
        CONVERT                     =  9
        
        INVERT_P256                 = 10
        INVERT_P384                 = 11


    # magic pair of begin/end markers
    MARKER_BEGIN_MICROCODE  = "BEGIN_MICROCODE:"
    MARKER_END_MICROCODE    = "END_MICROCODE"

    # names of banks
    MICROCODE_C_NAME_BANK_LO    = "BANK_LO"
    MICROCODE_C_NAME_BANK_HI    = "BANK_HI"

    # micro-operation names
    MICROCODE_C_NAME_UOP_CMPZ       = "uop_cmpz"
    MICROCODE_C_NAME_UOP_MOVE       = "uop_move"
    MICROCODE_C_NAME_UOP_CALC       = "uop_calc"
    MICROCODE_C_NAME_UOP_CYCLE      = "uop_cycle"
    MICROCODE_C_NAME_UOP_REPEAT     = "uop_repeat"
    MICROCODE_C_NAME_UOP_CALC_EVEN  = "uop_calc_if_even"
    MICROCODE_C_NAME_UOP_CALC_ODD   = "uop_calc_if_odd"

    # calculate micro-operations
    MICROCODE_C_NAME_UOP_MATH_MUL   = "MUL"
    MICROCODE_C_NAME_UOP_MATH_ADD   = "ADD"
    MICROCODE_C_NAME_UOP_MATH_SUB   = "SUB"
    
    # names of banks in C source
    MICROCODE_C_NAMES_BANKS = [MICROCODE_C_NAME_BANK_LO, MICROCODE_C_NAME_BANK_HI]

    # names of operands in C source
    MICROCODE_C_NAMES_OPERANDS = [  "CONST_ZERO",  "CONST_ONE",
                                    "CONST_DELTA",
                                    "CONST_GX",    "CONST_GY",
                                    "INVERT_R1",   "INVERT_R2",
                                    "INVERT_X2",   "INVERT_X3",   "INVERT_X6",
                                    "INVERT_X12",  "INVERT_X15",  "INVERT_X30",
                                    "INVERT_X32",  "INVERT_X60",  "INVERT_X120",
                                    "INVERT_A2",   "INVERT_A3",
                                    "CYCLE_R0X",   "CYCLE_R0Y",   "CYCLE_R0Z",
                                    "CYCLE_R1X",   "CYCLE_R1Y",   "CYCLE_R1Z",
                                    "CYCLE_SX",    "CYCLE_SY",    "CYCLE_SZ",
                                    "CYCLE_TX",    "CYCLE_TY",    "CYCLE_TZ",
                                    "CYCLE_T1",    "CYCLE_T2",
                                    "CYCLE_T3",    "CYCLE_T4",
                                    "CYCLE_T5",    "CYCLE_T6",
                                    "CYCLE_T7",    "CYCLE_T8"]

    # names of banks in Verilog source
    MICROCODE_V_NAME_BANKS_DUMMY = "UOP_BANKS_DUMMY"
    
    MICROCODE_V_NAMES_BANKS = ["UOP_BANKS_LO2HI", "UOP_BANKS_HI2LO"]

    # names of operands in Verilog source
    MICROCODE_V_NAMES_OPERANDS = ["UOP_OPERAND_" + op for op in MICROCODE_C_NAMES_OPERANDS]

    # names of opcodes in Verilog source
    MICROCODE_V_NAME_OPCODE_CMPZ        = "UOP_OPCODE_CMPZ"
    MICROCODE_V_NAME_OPCODE_MOVE        = "UOP_OPCODE_COPY"
    MICROCODE_V_NAME_OPCODE_MUL         = "UOP_OPCODE_MUL"
    MICROCODE_V_NAME_OPCODE_ADD         = "UOP_OPCODE_ADD"
    MICROCODE_V_NAME_OPCODE_SUB         = "UOP_OPCODE_SUB"
    MICROCODE_V_NAME_OPCODE_STOP        = "UOP_OPCODE_STOP"
    
    MICROCODE_V_NAME_OPERAND_DONTCARE   = "UOP_OPERAND_DONTCARE"

    # match group to catch operand names
    MATCH_GROUP_09AZ        = "([0-9A-Z_]+)"
    
    # match group to catch bank names
    MATCH_GROUP_BANK        = "(" + MICROCODE_C_NAME_BANK_LO + "|" + MICROCODE_C_NAME_BANK_HI + ")"

    # match group to catch number of loop iterations
    MATCH_GROUP_NUMBER      = "(\d+)"
    
    # match group to catch math instruction
    MATCH_GROUP_MATH        = "(" + MICROCODE_C_NAME_UOP_MATH_MUL + "|" + MICROCODE_C_NAME_UOP_MATH_ADD + "|" + MICROCODE_C_NAME_UOP_MATH_SUB + ")"
        
    # match group to catch calculation micro-operation
    MATCH_GROUP_CALC        = "(" + MICROCODE_C_NAME_UOP_CALC + "|" + MICROCODE_C_NAME_UOP_CALC_EVEN + "|" + MICROCODE_C_NAME_UOP_CALC_ODD + ")"
    
    # map string microcode piece names to enum values
    MICROCODE_PIECE_DICT    = {     "PREPARE":                  MICROCODE_PIECE_ENUM.PREPARE,
                                    
                                    "CYCLE_DOUBLE_R0":          MICROCODE_PIECE_ENUM.CYCLE_DOUBLE_R0,
                                    "CYCLE_DOUBLE_R1":          MICROCODE_PIECE_ENUM.CYCLE_DOUBLE_R1,
                                    
                                    "CYCLE_ADD":                MICROCODE_PIECE_ENUM.CYCLE_ADD,                                    
                                    "CYCLE_ADD_R0_AT_INFINITY": MICROCODE_PIECE_ENUM.CYCLE_ADD_R0_AT_INFINITY,
                                    "CYCLE_ADD_R1_AT_INFINITY": MICROCODE_PIECE_ENUM.CYCLE_ADD_R1_AT_INFINITY,
                                    "CYCLE_ADD_REGULAR":        MICROCODE_PIECE_ENUM.CYCLE_ADD_REGULAR,
                                    
                                    "CYCLE_K0":                 MICROCODE_PIECE_ENUM.CYCLE_K0,
                                    "CYCLE_K1":                 MICROCODE_PIECE_ENUM.CYCLE_K1,
                                    
                                    "INVERT_P256":              MICROCODE_PIECE_ENUM.INVERT_P256,
                                    "INVERT_P384":              MICROCODE_PIECE_ENUM.INVERT_P384,
                                    
                                    "CONVERT":                  MICROCODE_PIECE_ENUM.CONVERT}
                                    
                                    
    # map C bank names to Verilog bank names
    MICROCODE_BANK_DICT     = dict(zip(MICROCODE_C_NAMES_BANKS,    MICROCODE_V_NAMES_BANKS))

    # map C operand names to Verilog operand names
    MICROCODE_OPERAND_DICT  = dict(zip(MICROCODE_C_NAMES_OPERANDS, MICROCODE_V_NAMES_OPERANDS))

    # map C calculation names to Verilog opcode names
    MICROCODE_MATH_DICT = { MICROCODE_C_NAME_UOP_MATH_MUL:  MICROCODE_V_NAME_OPCODE_MUL,
                            MICROCODE_C_NAME_UOP_MATH_ADD:  MICROCODE_V_NAME_OPCODE_ADD,
                            MICROCODE_C_NAME_UOP_MATH_SUB:  MICROCODE_V_NAME_OPCODE_SUB}

    # microcode format
    MICROCODE_FORMAT_ADDR = "9'd%03d"
    MICROCODE_FORMAT_LINE = MICROCODE_FORMAT_ADDR + ": data <= %s;"
    MICROCODE_FORMAT_OFFSET = "localparam [UOP_ADDR_WIDTH-1:0] %s = " + MICROCODE_FORMAT_ADDR + ";"
                            
    # pieces of microcode
    MICROCODE_LINES_PREPARE                  = []
    
    MICROCODE_LINES_CYCLE_DOUBLE_R0          = []
    MICROCODE_LINES_CYCLE_DOUBLE_R1          = []
    
    MICROCODE_LINES_CYCLE_ADD                = []
    MICROCODE_LINES_CYCLE_ADD_R0_AT_INFINITY = []
    MICROCODE_LINES_CYCLE_ADD_R1_AT_INFINITY = []
    MICROCODE_LINES_CYCLE_ADD_REGULAR        = []
    
    MICROCODE_LINES_CYCLE_K0                 = []
    MICROCODE_LINES_CYCLE_K1                 = []
    
    MICROCODE_LINES_INVERT_P256              = []
    MICROCODE_LINES_INVERT_P384              = []
    
    MICROCODE_LINES_CONVERT                  = []
    
    MICROCODE_LINE_STOP = "{%s, %s, %s, %s, %s}" % (    MICROCODE_V_NAME_OPCODE_STOP,
                                                        MICROCODE_V_NAME_BANKS_DUMMY,
                                                        MICROCODE_V_NAME_OPERAND_DONTCARE,
                                                        MICROCODE_V_NAME_OPERAND_DONTCARE,
                                                        MICROCODE_V_NAME_OPERAND_DONTCARE)
    
    def __init__(self, filenames):
        self.__filenames = filenames
    
    def parse(self):
        for next_filename in self.__filenames:
            print("Parsing '%s'..." % (next_filename))
            parsing_now = False
            line_num = 0
            with open(next_filename, "r") as c_file:
                c_lines = c_file.readlines()
                for next_c_line in c_lines:
                    line_num += 1
                    self.__line_num = line_num
                    self.__next_c_line = next_c_line.strip()
                    if len(self.__next_c_line) == 0: continue
                    if self.__next_c_line.startswith("//"): continue
                    if not parsing_now:
                        self.__current_piece = self.__try_start_parsing()
                        if self.__current_piece != self.MICROCODE_PIECE_ENUM.NONE:
                            print("    Found piece of microcode: %s" % (str(self.__current_piece)))
                            parsing_now = True
                    else:
                        parsing_now = self.__continue_parsing()
        
                    
    def format(self):

        mode = 0
    
        if len(sys.argv) == 2:
            if sys.argv[1] == "P256": mode = 1
            if sys.argv[1] == "P384": mode = 2
    
        if mode == 0:
            sys.exit("sys.exit(): Usage: ecdsa_microcode_parser.py <P256|P384>!")
    
        if len(self.MICROCODE_LINES_PREPARE)                  == 0: sys.exit("sys.exit(): Empty PREPARE piece!")
        
        if len(self.MICROCODE_LINES_CYCLE_DOUBLE_R0)          == 0: sys.exit("sys.exit(): Empty CYCLE_DOUBLE_R0 piece!")
        if len(self.MICROCODE_LINES_CYCLE_DOUBLE_R1)          == 0: sys.exit("sys.exit(): Empty CYCLE_DOUBLE_R1 piece!")
                
        if len(self.MICROCODE_LINES_CYCLE_ADD)                == 0: sys.exit("sys.exit(): Empty CYCLE_ADD piece!")
        if len(self.MICROCODE_LINES_CYCLE_ADD_R0_AT_INFINITY) == 0: sys.exit("sys.exit(): Empty CYCLE_ADD_R0_AT_INFINITY piece!")
        if len(self.MICROCODE_LINES_CYCLE_ADD_R1_AT_INFINITY) == 0: sys.exit("sys.exit(): Empty CYCLE_ADD_R1_AT_INFINITY piece!")
        if len(self.MICROCODE_LINES_CYCLE_ADD_REGULAR)        == 0: sys.exit("sys.exit(): Empty CYCLE_ADD_REGULAR piece!")
        
        if len(self.MICROCODE_LINES_CYCLE_K0)                 == 0: sys.exit("sys.exit(): Empty CYCLE_K0 piece!")
        if len(self.MICROCODE_LINES_CYCLE_K1)                 == 0: sys.exit("sys.exit(): Empty CYCLE_K1 piece!")
        
        if len(self.MICROCODE_LINES_INVERT_P256)              == 0: sys.exit("sys.exit(): Empty INVERT_P256 piece!")
        if len(self.MICROCODE_LINES_INVERT_P384)              == 0: sys.exit("sys.exit(): Empty INVERT_P384 piece!")
        
        if len(self.MICROCODE_LINES_CONVERT)                  == 0: sys.exit("sys.exit(): Empty CONVERT piece!")
        
        length = 0
        length += len(self.MICROCODE_LINES_PREPARE)

        length += len(self.MICROCODE_LINES_CYCLE_DOUBLE_R0)
        length += len(self.MICROCODE_LINES_CYCLE_DOUBLE_R1)
        
        length += len(self.MICROCODE_LINES_CYCLE_ADD)
        length += len(self.MICROCODE_LINES_CYCLE_ADD_R0_AT_INFINITY)
        length += len(self.MICROCODE_LINES_CYCLE_ADD_R1_AT_INFINITY)
        length += len(self.MICROCODE_LINES_CYCLE_ADD_REGULAR)

        length += len(self.MICROCODE_LINES_CYCLE_K0)
        length += len(self.MICROCODE_LINES_CYCLE_K1)

        length += len(self.MICROCODE_LINES_CONVERT)
        
        if mode == 1: length += len(self.MICROCODE_LINES_INVERT_P256)
        if mode == 2: length += len(self.MICROCODE_LINES_INVERT_P384)

        print("Total number of micro-operations (w/o stops): %s" % (length))
        
        
        self.__addr = 0
        
        print("\n -=-=-=-=-=- CUT AND PASTE BELOW -=-=-=-=-=-\n")
        
        num_mul_cycle = 0
        num_mul_invert_p256 = 0
        num_mul_invert_p384 = 0
        
        offset_prepare = self.__addr;
        print("// PREPARE");
        for line in self.MICROCODE_LINES_PREPARE:
            self.__format_line(line)
        self.__format_line(self.MICROCODE_LINE_STOP)

        offset_cycle_double_r0 = self.__addr;
        print("// CYCLE_DOUBLE_R0");
        for line in self.MICROCODE_LINES_CYCLE_DOUBLE_R0:
            num_mul_cycle += self.__format_line(line)
        self.__format_line(self.MICROCODE_LINE_STOP)
        
        offset_cycle_double_r1 = self.__addr;
        print("// CYCLE_DOUBLE_R1");
        for line in self.MICROCODE_LINES_CYCLE_DOUBLE_R1:
            num_mul_cycle += self.__format_line(line)
        self.__format_line(self.MICROCODE_LINE_STOP)

        offset_cycle_add = self.__addr;
        print("// CYCLE_ADD");
        for line in self.MICROCODE_LINES_CYCLE_ADD:
            num_mul_cycle += self.__format_line(line)
        self.__format_line(self.MICROCODE_LINE_STOP)

        offset_cycle_add_r0_at_infinity = self.__addr;
        print("// CYCLE_ADD_R0_AT_INFINITY");
        for line in self.MICROCODE_LINES_CYCLE_ADD_R0_AT_INFINITY:
            self.__format_line(line)
        self.__format_line(self.MICROCODE_LINE_STOP)
        
        offset_cycle_add_r1_at_infinity = self.__addr;
        print("// CYCLE_ADD_R1_AT_INFINITY");
        for line in self.MICROCODE_LINES_CYCLE_ADD_R1_AT_INFINITY:
            self.__format_line(line)
        self.__format_line(self.MICROCODE_LINE_STOP)        

        offset_cycle_add_regular = self.__addr;
        print("// CYCLE_ADD_REGULAR");
        for line in self.MICROCODE_LINES_CYCLE_ADD_REGULAR:
            num_mul_cycle += self.__format_line(line)
        self.__format_line(self.MICROCODE_LINE_STOP)

        offset_cycle_k0 = self.__addr;
        print("// CYCLE_K0");
        for line in self.MICROCODE_LINES_CYCLE_K0:
            num_mul_cycle += self.__format_line(line)
        self.__format_line(self.MICROCODE_LINE_STOP)

        offset_cycle_k1 = self.__addr;
        print("// CYCLE_K1");
        for line in self.MICROCODE_LINES_CYCLE_K1:
            self.__format_line(line)
        self.__format_line(self.MICROCODE_LINE_STOP)

        offset_convert = self.__addr;
        print("// CONVERT");
        for line in self.MICROCODE_LINES_CONVERT:
            num_mul_cycle += self.__format_line(line)
        self.__format_line(self.MICROCODE_LINE_STOP)

        if mode == 1:
            offset_invert_p256 = self.__addr;
            print("// INVERT_P256");
            for line in self.MICROCODE_LINES_INVERT_P256:
                num_mul_invert_p256 += self.__format_line(line)
            self.__format_line(self.MICROCODE_LINE_STOP)

        if mode == 2:
            offset_invert_p384 = self.__addr;
            print("// INVERT_P384");
            for line in self.MICROCODE_LINES_INVERT_P384:
                num_mul_invert_p384 += self.__format_line(line)
            self.__format_line(self.MICROCODE_LINE_STOP)

        print("\n")
        self.__format_offset("UOP_OFFSET_PREPARE                   ", offset_prepare)
        self.__format_offset("UOP_OFFSET_CYCLE_DOUBLE_R0           ", offset_cycle_double_r0)
        self.__format_offset("UOP_OFFSET_CYCLE_DOUBLE_R1           ", offset_cycle_double_r1)
        self.__format_offset("UOP_OFFSET_CYCLE_ADD                 ", offset_cycle_add)
        self.__format_offset("UOP_OFFSET_CYCLE_ADD_R0_AT_INFINITY  ", offset_cycle_add_r0_at_infinity)
        self.__format_offset("UOP_OFFSET_CYCLE_ADD_R1_AT_INFINITY  ", offset_cycle_add_r1_at_infinity)
        self.__format_offset("UOP_OFFSET_CYCLE_ADD_REGULAR         ", offset_cycle_add_regular)
        self.__format_offset("UOP_OFFSET_CYCLE_K0                  ", offset_cycle_k0)
        self.__format_offset("UOP_OFFSET_CYCLE_K1                  ", offset_cycle_k1)
        self.__format_offset("UOP_OFFSET_CONVERT                   ", offset_convert)
        if mode == 1: self.__format_offset("UOP_OFFSET_INVERT_P256               ", offset_invert_p256)
        if mode == 2: self.__format_offset("UOP_OFFSET_INVERT_P384               ", offset_invert_p384)
        
        print("")
        print("num_mul_cycle = %d" % num_mul_cycle)
        print("num_mul_invert_p256 = %d" % num_mul_invert_p256)
        print("num_mul_invert_p384 = %d" % num_mul_invert_p384)
                
    def __format_line(self, line):
        line = self.MICROCODE_FORMAT_LINE % (self.__addr, line) 
        print(line)
        self.__addr += 1
        is_mul = line.find(self.MICROCODE_V_NAME_OPCODE_MUL)
        if is_mul >= 0: return 1
        return 0
    
    def __format_offset(self, name, offset):
        print(self.MICROCODE_FORMAT_OFFSET % (name, offset))
    
    def __try_start_parsing(self):
        piece = self.MICROCODE_PIECE_ENUM.NONE
        begin_marker = re.match("^/\*\s*" + self.MARKER_BEGIN_MICROCODE + "\s*" +
                                            self.MATCH_GROUP_09AZ + "\s*\*/$",
                                            self.__next_c_line);
        if begin_marker:
            piece = self.MICROCODE_PIECE_DICT[begin_marker.group(1)]
            
        return piece
    
    def __encode_uop(self, opcode, banks, src1, src2, dst):
        return "{%s, %s, %s, %s, %s}" % (opcode, banks, src1, src2, dst)
    
    def __continue_parsing(self):
    
        end_marker = re.match("^/\*\s*" +   self.MARKER_END_MICROCODE + "\s*\*/$",
                                            self.__next_c_line);
        if end_marker: return False
        
        # cmpz?
        uop_cmpz = re.match("^" +   self.MICROCODE_C_NAME_UOP_CMPZ + "\(\s*" +
                                    self.MATCH_GROUP_BANK          + "\,\s*" +
                                    self.MATCH_GROUP_09AZ          + "\)\;$",
                                    self.__next_c_line, re.IGNORECASE)
        if uop_cmpz:
            ok = self.__parse_uop_cmpz(uop_cmpz)
            if ok: return True
            else:
                self.__print_parse_error("parse_uop_cmpz() failed!")
                self.__abort()
                
        # move?
        uop_move = re.match("^" +   self.MICROCODE_C_NAME_UOP_MOVE + "\(\s*" +
                                    self.MATCH_GROUP_BANK          + "\,\s*" +
                                    self.MATCH_GROUP_09AZ          + "\,\s*" +
                                    self.MATCH_GROUP_BANK          + "\,\s*" +
                                    self.MATCH_GROUP_09AZ          + "\)\;$",
                                    self.__next_c_line, re.IGNORECASE)
        if uop_move:
            ok = self.__parse_uop_move(uop_move)
            if ok: return True
            else:
                self.__print_parse_error("parse_uop_move() failed!")
                self.__abort()
        
        # calc?
        uop_calc = re.match("^" +   self.MATCH_GROUP_CALC           + "\s*\(" +
                                    self.MATCH_GROUP_MATH           + "\,\s*" +
                                    self.MATCH_GROUP_BANK           + "\,\s*" +
                                    self.MATCH_GROUP_09AZ           + "\,\s*" +
                                    self.MATCH_GROUP_09AZ           + "\,\s*" +
                                    self.MATCH_GROUP_BANK           + "\,\s*" +
                                    self.MATCH_GROUP_09AZ           + "\)\;$",
                                    self.__next_c_line, re.IGNORECASE)
        if uop_calc:
            ok = self.__parse_uop_calc(uop_calc)
            if ok: return True
            else:
                self.__print_parse_error("parse_uop_calc() failed!")
                self.__abort()
        
        # cycle?
        uop_cycle = re.match("^" +  self.MICROCODE_C_NAME_UOP_CYCLE + "\(" +
                                    self.MATCH_GROUP_NUMBER         + "\)\;$",
                                    self.__next_c_line, re.IGNORECASE)
        if uop_cycle:
            ok = self.__parse_uop_cycle(uop_cycle)
            if ok: return True
            else:
                self.__print_parse_error("parse_uop_cycle() failed!")
                self.__abort()

        # repeat?
        uop_repeat = re.match("^" + self.MICROCODE_C_NAME_UOP_REPEAT + "\(" + "\)\;$",
                                    self.__next_c_line, re.IGNORECASE)
        if uop_repeat:
            ok = self.__parse_uop_repeat()
            if ok: return True
            else:
                self.__print_parse_error("__parse_uop_repeat() failed!")
                self.__abort()
                
        self.__print_parse_error("unknown micro-operation!")
        self.__abort()

    def __check_math(self, math):
        if not math in self.MICROCODE_MATH_DICT:
            print_parse_error("bad math!")
            return False
            
        return True

    def __check_banks(self, src, dst):
        if not src in self.MICROCODE_BANK_DICT:
            print_parse_error("bad src bank!")
            return False

        if not dst in self.MICROCODE_BANK_DICT:
            print_parse_error("bad dst bank!")
            return False

        if src == dst:
            print_parse_error("src bank == dst bank!")
            return False
            
        return True
        
    def __check_bank(self, src):
        if not src in self.MICROCODE_BANK_DICT:
            print_parse_error("bad src bank!")
            return False
            
        return True        

    def __check_op3(self, src1, src2, dst):

        if not src1 in self.MICROCODE_OPERAND_DICT:
            self.__print_parse_error("bad src1 operand!")
            return False

        if src2 != "" and not src2 in self.MICROCODE_OPERAND_DICT:
            self.__print_parse_error("bad src2 operand!")
            return False

        if dst != "" and not dst in self.MICROCODE_OPERAND_DICT:
            self.__print_parse_error("bad dst operand!")
            return False
            
        return True

    def __check_op2(self, src, dst):
        return self.__check_op3(src, "", dst)
    
    def __check_op1(self, src):
        return self.__check_op2(src, "")
    
    def __parse_uop_move(self, params):
        bank_src = params.group(1)
        bank_dst = params.group(3)
        op_src   = params.group(2)
        op_dst   = params.group(4)
    
        if not self.__check_banks(bank_src, bank_dst):
            self.__print_parse_error("check_banks() failed!")
            return False
            
        if not self.__check_op2(op_src, op_dst):
            self.__print_parse_error("check_op2() failed!")
            return False
    
        opcode = self.MICROCODE_V_NAME_OPCODE_MOVE
        bank = self.MICROCODE_BANK_DICT[bank_src]
        src1 = self.MICROCODE_OPERAND_DICT[op_src]
        src2 = self.MICROCODE_V_NAME_OPERAND_DONTCARE
        dst = self.MICROCODE_OPERAND_DICT[op_dst]
    
        data = self.__encode_uop(opcode, bank, src1, src2, dst)
        self.__store_uop(data)
        return True
        
    def __parse_uop_cmpz(self, params):
        bank_src = params.group(1)
        op_src   = params.group(2)
    
        if not self.__check_bank(bank_src):
            self.__print_parse_error("check_bank() failed!")
            return False
            
        if not self.__check_op1(op_src):
            self.__print_parse_error("check_op1() failed!")
            return False
    
        opcode = self.MICROCODE_V_NAME_OPCODE_CMPZ
        bank = self.MICROCODE_BANK_DICT[bank_src]
        src1 = self.MICROCODE_OPERAND_DICT[op_src]
        src2 = self.MICROCODE_V_NAME_OPERAND_DONTCARE
        dst = self.MICROCODE_V_NAME_OPERAND_DONTCARE
    
        data = self.__encode_uop(opcode, bank, src1, src2, dst)
        self.__store_uop(data)
        return True        

    def __parse_uop_calc(self, params):
        calc     = params.group(1)
        math     = params.group(2)
        bank_src = params.group(3)
        bank_dst = params.group(6)
        op_src1  = params.group(4)
        op_src2  = params.group(5)
        op_dst   = params.group(7)
    
        if not self.__check_math(math):
            self.__print_parse_error("check_calc() failed!")
            return False
    
        if not self.__check_banks(bank_src, bank_dst):
            self.__print_parse_error("check_banks() failed!")
            return False
            
        if not self.__check_op3(op_src1, op_src2, op_dst):
            self.__print_parse_error("check_op3() failed!")
            return False
    
        opcode = self.MICROCODE_MATH_DICT[math]
        banks = self.MICROCODE_BANK_DICT[bank_src]
        src1 = self.MICROCODE_OPERAND_DICT[op_src1]
        src2 = self.MICROCODE_OPERAND_DICT[op_src2]
        dst = self.MICROCODE_OPERAND_DICT[op_dst]

        data = self.__encode_uop(opcode, banks, src1, src2, dst)
        if   calc == self.MICROCODE_C_NAME_UOP_CALC:        self.__store_uop(data)
        elif calc == self.MICROCODE_C_NAME_UOP_CALC_EVEN:   self.__loop_even = data
        elif calc == self.MICROCODE_C_NAME_UOP_CALC_ODD:    self.__loop_odd = data
        else: return False
        
        return True

    def __parse_uop_cycle(self, params):
        self.__loop_iters = int(params.group(1))
        return True

    def __parse_uop_repeat(self):
        print("        Unrolling loop (%d iters)..." % (self.__loop_iters))
        
        for i in range(0, self.__loop_iters):
            if i % 2 == 0:  self.__store_uop(self.__loop_even)
            else:           self.__store_uop(self.__loop_odd)
        
        return True
        
    def __store_uop(self, data):
        #print("\t" + data)
        if   self.__current_piece == self.MICROCODE_PIECE_ENUM.PREPARE:                  self.MICROCODE_LINES_PREPARE.append(data)
        elif self.__current_piece == self.MICROCODE_PIECE_ENUM.CYCLE_DOUBLE_R0:          self.MICROCODE_LINES_CYCLE_DOUBLE_R0.append(data)
        elif self.__current_piece == self.MICROCODE_PIECE_ENUM.CYCLE_DOUBLE_R1:          self.MICROCODE_LINES_CYCLE_DOUBLE_R1.append(data)
        elif self.__current_piece == self.MICROCODE_PIECE_ENUM.CYCLE_ADD:                self.MICROCODE_LINES_CYCLE_ADD.append(data)
        elif self.__current_piece == self.MICROCODE_PIECE_ENUM.CYCLE_ADD_R0_AT_INFINITY: self.MICROCODE_LINES_CYCLE_ADD_R0_AT_INFINITY.append(data)
        elif self.__current_piece == self.MICROCODE_PIECE_ENUM.CYCLE_ADD_R1_AT_INFINITY: self.MICROCODE_LINES_CYCLE_ADD_R1_AT_INFINITY.append(data)
        elif self.__current_piece == self.MICROCODE_PIECE_ENUM.CYCLE_ADD_REGULAR:        self.MICROCODE_LINES_CYCLE_ADD_REGULAR.append(data)
        elif self.__current_piece == self.MICROCODE_PIECE_ENUM.CYCLE_K0:                 self.MICROCODE_LINES_CYCLE_K0.append(data)
        elif self.__current_piece == self.MICROCODE_PIECE_ENUM.CYCLE_K1:                 self.MICROCODE_LINES_CYCLE_K1.append(data)
        elif self.__current_piece == self.MICROCODE_PIECE_ENUM.INVERT_P256:              self.MICROCODE_LINES_INVERT_P256.append(data)
        elif self.__current_piece == self.MICROCODE_PIECE_ENUM.INVERT_P384:              self.MICROCODE_LINES_INVERT_P384.append(data)
        elif self.__current_piece == self.MICROCODE_PIECE_ENUM.CONVERT:                  self.MICROCODE_LINES_CONVERT.append(data)

    def __print_parse_error(self, msg):
        print("PARSE ERROR: %s" % (msg))
  
    def __abort(self):
        sys.exit("Stopped at line #%d:\n%s" % (self.__line_num, self.__next_c_line))


# -----------------------------------------------------------------------------
def main(filenames):
# -----------------------------------------------------------------------------
    parser = MICROCODE_PARSER(filenames)
    parser.parse()
    parser.format()
    
# -----------------------------------------------------------------------------    
if __name__ == "__main__":
# -----------------------------------------------------------------------------
    main(C_FILES)
    
#
# End-of-File
#