Bootstrap

IC测试chroma的pattern转爱德万pattern脚本

1. 功能

在IC测试中,需要将测试机从chroma升级到高端测试机爱德万,但大量的测试pattern无法转平台,爱德万也不提供技术支持,因此,本人写了一个脚本,用于批量且快速的转换。

2. 代码

首先需要具备两个目录CHROMA_PATADV_PAT与此脚本同级目录,然后放置一个或多个chroma pat到CHROMA_PAT目录,运行此脚本即可。

2.1 chroma pat 例子:

SET_DEC_FILE "xxxxx_pin.dec"
HEADER
NRST,PA_7,PA_8,PA_9,PA_10,PA_11,PA_12,PA_13,PA_14,PA_15,PB_0,PB_1,PB_10,PB_11,PB_12,PB_13,PC_0,PA_4,PA_5;
SPM_PATTERN (ompress_occ_all1) {
 *10000000000XXXX0000* TS8; //start
 *1KK00000000XXXX0000* ; //start
 *10000000000XXXX0000* STOP; //start
}

2.2 转换完成的adv pat 例子:

将转换完成的pat进行解包如下:

Comments.cmt:

1 start
2 start
3 start

Program.sprg:

<?xml version="1.0" encoding="UTF-8"?>
<Program xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Program.xsd">
  <!-- patfile: ompress_occ_all1.pat, use decfile: ss517a_pin.dec -->
  <Instrument id="NRST,PA_7,PA_8,PA_9,PA_10,PA_11,PA_12,PA_13,PA_14,PA_15,PB_0,PB_1,PB_10,PB_11,PB_12,PB_13,PC_0,PA_4,PA_5">
    <Instruction id="genVec" value="3"/>
  </Instrument>
</Program>

Vectors.vec:

10000000000XXXX0000
1KK00000000XXXX0000
10000000000XXXX0000

2.3 chroma2adv_pat脚本:

"""
@auther:刘泽文
@data:2023.05.23
@function:chroma.pat->adv.pat
"""

import os
import sys
import time
import re

import shutil

# 将log写入文件
# sys.stdout = open("conversion_log.log", "w", encoding="utf-8")

# 切换到本文件目录
os.chdir(os.path.dirname(__file__))

sys.path.append("./dos2unix/bin")

CHROMA_PAT_PATH = "./CHROMA_PAT"
ADV_PAT_PATH = "./ADV_PAT"

CRLF = "\r\n"
LF = "\n"
line_end = LF

LOG = list()

# 找出CHROMA_PAT_PATH目录下所有.pat结尾文件
def list_all_file(dir_path, file_ext):
    file_list = list()
    for root, dirs, files in os.walk(dir_path): 
        for f in files:
            if f.lower().endswith(file_ext):  
                file_list.append(f)
    return file_list

# 找字符串substr在str中第time次出现的位置
def findSubStrIndex(str, substr, time):
    times = str.count(substr)
    if (times == 0) or (times < time):
        pass
    else:
        i = 0
        index = -1
        while i < time:
            index = str.find(substr, index+1)
            i += 1
        return index

# 获取一个文件中 第二对"{}"之间的字符串
def get_str_between_two_char(s, left="", right="", time_l=1, time_r=1):
    # 判断是否存在左右括号
    if left not in s or right not in s:
        return ""
    # 找到左括号的位置
    left_pos = findSubStrIndex(s, left, time_l)
    # 找到右括号的位置
    right_pos = findSubStrIndex(s, right, time_r)
    # 返回左右括号之间的字符串
    return s[left_pos:right_pos]

# 添加头部
def add_program_header(program_lines):
    program_lines.append(f"<?xml version=\"1.0\" encoding=\"UTF-8\"?>{line_end}")
    program_lines.append(f"<Program xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"Program.xsd\">{line_end}")
    return program_lines
def add_program_header_end(program_lines):
    program_lines.append(f"</Program>{line_end}")
    return program_lines

def set_program_xmode(program_lines, xmode_value):
    program_lines.append(f"<Assignment id=\"xmode\" value=\"{int(xmode_value)}\"/>{line_end}")
    return program_lines

# 添加PINLIST
def add_program_pin(program_lines, pinlist):
    pinlist_str = ",".join(pinlist)
    program_lines.append(f"  <Instrument id=\"{pinlist_str}\">{line_end}")
    return program_lines
def add_program_pin_end(program_lines):
    program_lines.append(f"  </Instrument>{line_end}")
    return program_lines

def add_program_genvec(program_lines, line_number):
    program_lines.append(f"    <Instruction id=\"genVec\" value=\"{int(line_number)}\"/>{line_end}")
    return program_lines

# 增加repeat
def add_program_repeat(program_lines, d_line_number, repeat_number):
    program_lines.append(f"    <Instruction id=\"genVec\" value=\"{int(d_line_number)}\">{line_end}")
    program_lines.append(f"      <Assignment id=\"repeat\" value=\"{int(repeat_number)}\"/>{line_end}")
    program_lines.append(f"    </Instruction>{line_end}")
    return program_lines

# 增加loop
def add_program_loop(program_lines, loop_number):
    program_lines.append(f"    <Instruction id=\"loop\" value=\"{int(loop_number)}\" />{line_end}")
    return program_lines
def add_program_loopend(program_lines):
    program_lines.append(f"    <Instruction id=\"loopEnd\" />{line_end}")
    return program_lines

def add_program_comment(program_lines, comment):
    program_lines.append(f"  <!-- {comment} -->{line_end}")
    return program_lines

CHROMA_PAT_FILE_LIST = list_all_file(CHROMA_PAT_PATH, ".pat")

# 批量处理所有pat
for CHROMA_PAT_FILE in CHROMA_PAT_FILE_LIST:

    print(f"\n*********************************************** 开始处理文件{CHROMA_PAT_FILE} ***********************************************\n")

    PIN_LIST = list()

    comments_lines = list()

    vectors_count = 0
    vectors_lines = list()
    # 距离上一次有标记的line过去多少行了
    differ_line_count = 0
    differ_line_count_all = 0

    program_lines = list()
    
    # 打开所有pat
    with open(os.path.join(CHROMA_PAT_PATH, CHROMA_PAT_FILE), "r", encoding="utf-8", errors='ignore') as chroma_pat:
        chroma_pat_lines = chroma_pat.readlines()

        PIN_LIST = list()

        pin_def_in = False
        for line in chroma_pat_lines:
            if "HEADER" in line:
                pin_def_in = True
            if "SPM_PATTERN" in line:
                pin_def_in = False
            if pin_def_in:
                if re.search(r'//.*', line, re.I):
                    line = line.split("//")[0]
                if re.search(r'[A-Za-z0-9_]+\s*[,;]+', line, re.I):
                    pin_list = re.findall(r'([A-Za-z0-9_]+)\s*[,;]+', line, re.I)
                    if len(pin_list) > 0:
                        for i in range(0, len(pin_list)):
                            PIN_LIST.append(pin_list[i])

        PIN_LIST = [i.strip() for i in PIN_LIST if i != ""]

        chroma_pat_str = "".join(chroma_pat_lines)

        DEC_FILENAME = ""
        if re.search(r'"[A-Za-z0-9_]+\.dec"', chroma_pat_str, re.I):
            DEC_FILENAME = re.findall(r'"([A-Za-z0-9_]+\.dec)"', chroma_pat_str, re.I)[0].strip()

        program_lines = add_program_header(program_lines)
        program_lines = add_program_comment(program_lines, f"patfile: {CHROMA_PAT_FILE}, use decfile: {DEC_FILENAME}")
        program_lines = add_program_pin(program_lines, PIN_LIST)

        # print(PIN_LIST)
        
        for line in chroma_pat_lines:
            # 查找pat行
            if re.search(r'\*.+\*.*;', line, re.I):
                vectors_count = vectors_count + 1
                differ_line_count = differ_line_count + 1
                vectors_line = re.findall(r'\*(.+)\*.*;', line, re.I)[0].replace(" ", "").strip() + line_end
                # 去除vectors_line里面的所有空格
                vectors_lines.append(vectors_line)
                # 取log
                if re.search(r'\*.+\*.*;\s*//.+', line, re.I):
                    comments_line = f"{vectors_count} " + re.findall(r'\*.+\*.*;\s*//(.+)', line, re.I)[0].strip() + line_end
                    # print(comments_line, end="")
                    comments_lines.append(comments_line)
                if re.search(r'\*.+\*\s*RPT\s*\d+\s*;', line, re.I):
                    rpt_number = int(re.findall(r'\*.+\*\s*RPT\s*(\d+)\s*;', line, re.I)[0].strip())
                    # print(f"RPT {rpt_number}")
                    if differ_line_count > 1:
                        program_lines = add_program_genvec(program_lines, differ_line_count-1)
                        program_lines = add_program_repeat(program_lines, 1, rpt_number)
                    else:
                        program_lines = add_program_repeat(program_lines, differ_line_count, rpt_number)
                    differ_line_count_all = differ_line_count_all + differ_line_count
                    # 清空计数
                    differ_line_count = 0
                
                if re.search(r'\*.+\*\s*SETX\d*\s*\d+\s*;', line, re.I):
                    loop_number = int(re.findall(r'\*.+\*\s*SETX\d*\s*(\d+)\s*;', line, re.I)[0].strip())
                    # print(f"LOOP {loop_number}")
                    # 从该行前面开始循环,所以differ_line_count-1
                    if differ_line_count > 1:
                        program_lines = add_program_genvec(program_lines, differ_line_count - 1)
                    program_lines = add_program_loop(program_lines, loop_number)
                    differ_line_count_all = differ_line_count_all + differ_line_count - 1
                    # 清空计数
                    differ_line_count = 1
                elif re.search(r'\*.+\*\s*JNZ.+\s*;', line, re.I):
                    # 从该行后面结束循环,所以differ_line_count不用-1
                    if differ_line_count > 0:
                        program_lines = add_program_genvec(program_lines, differ_line_count)
                    program_lines = add_program_loopend(program_lines)
                    differ_line_count_all = differ_line_count_all + differ_line_count
                    # 清空计数
                    differ_line_count = 0
    
    program_lines = add_program_genvec(program_lines, differ_line_count)
    program_lines = add_program_pin_end(program_lines)
    program_lines = add_program_header_end(program_lines)

    differ_line_count_all = differ_line_count_all + differ_line_count
    # print("总行数为:%d"%(differ_line_count_all))

    # 校验中间文件总数是否等于pat总行数
    if vectors_count != differ_line_count_all:
        print(f"{CHROMA_PAT_FILE}文件 行数{vectors_count}{differ_line_count_all} 不一致!")
        LOG.append(f"{CHROMA_PAT_FILE}文件 行数{vectors_count}{differ_line_count_all} 不一致!\n")

    # 文件夹名
    ADV_PAT_FILE_DIR = os.path.join(ADV_PAT_PATH, CHROMA_PAT_FILE.split(".")[0])

    # 创建以该pat名为名的文件夹
    if not os.path.exists(ADV_PAT_FILE_DIR):
        os.mkdir(ADV_PAT_FILE_DIR)

    # 将所有参数写入三个文件
    ADV_PAT_FILE_COMMENT_FILE = os.path.join(ADV_PAT_FILE_DIR, "Comments.cmt")
    with open(ADV_PAT_FILE_COMMENT_FILE, "w", encoding="utf-8") as comments_file:
        comments_file.writelines(comments_lines)
    ADV_PAT_FILE_PROGRAM_FILE = os.path.join(ADV_PAT_FILE_DIR, "Program.sprg")
    with open(ADV_PAT_FILE_PROGRAM_FILE, "w", encoding="utf-8") as program_file:
        program_file.writelines(program_lines)
    ADV_PAT_FILE_VECTORS_FILE = os.path.join(ADV_PAT_FILE_DIR, "Vectors.vec")
    with open(ADV_PAT_FILE_VECTORS_FILE, "w", encoding="utf-8") as vectors_file:
        vectors_file.writelines(vectors_lines)

    # dos系统文件转unix格式,行尾序列转换
    os.system(f"dos2unix {ADV_PAT_FILE_COMMENT_FILE}")
    os.system(f"dos2unix {ADV_PAT_FILE_PROGRAM_FILE}")
    os.system(f"dos2unix {ADV_PAT_FILE_VECTORS_FILE}")
    
    # 压缩打包为zip
    os.system("7z a -tzip %s.zip %s/*.* -r -mx=9 -mm=Deflate"%(ADV_PAT_FILE_DIR, ADV_PAT_FILE_DIR))

    # 删除已存在的.pat
    if os.path.exists(os.path.join(ADV_PAT_PATH, CHROMA_PAT_FILE)):
        os.remove(os.path.join(ADV_PAT_PATH, CHROMA_PAT_FILE))

    # 将.zip重命名为.pat
    os.rename(f"{ADV_PAT_FILE_DIR}.zip", os.path.join(ADV_PAT_PATH, CHROMA_PAT_FILE))

    # shutil.rmtree(ADV_PAT_FILE_DIR)

    LOG.append(f"{CHROMA_PAT_FILE}文件 转换OK!\n")

with open("conversion_log.log", "w", encoding="utf-8") as conversion_log:
    conversion_log.writelines(LOG)
;