简介:这是一份面向半导体工艺工程师、晶圆制造与质量检测人员的标准参考文档,围绕SEMI(国际半导体设备与材料协会)发布的关键工艺控制规范展开,可用于产线工艺监控、检测方法选型以及新人培训时的标准索引。内容涉及晶片对准标线、非接触式电介质评定、二次离子质谱法测氮浓度、晶片直径测量、热解吸附气相色谱法检测有机污染物、表面轮廓功谱密度分析、择优腐蚀缺陷记录、腐蚀溶液选择、栅极氧完整性电压斜线上升评估、线性偏光镜测试、氧化诱生缺陷检测、结晶学完整性分析以及酸萃取-原子吸收光谱法测金属沾污等具体方法,并延伸至平整度、厚度、载流子复合寿命、翘曲度、深能级实验等测试规程。资源包共1个pdf文件,大小约85KB,按标准号与名称逐条编号,便于快速定位查阅,已有110人学习。对于需要建立工艺控制框架、对照SEMI标准核查检测方法或撰写规范文件的读者,可作为一份实用的案头参考资料。
1. 拿到一份 69 条 SEMI 标准清单,先想清楚它是干什么用的
产线上硅片参数超规,老工程师的第一反应通常不是去动机台,而是先确认这次量测用的是哪一版标准。手上这份《半导体工艺控制标准参考.pdf》解决的正是这个问题:它把 69 条 SEMI 标准按序号铺开,从 SEMI MS1-0306 的晶片结合对准标线,到 SEMI MF 2166-0304 的非接触式电介质评定,覆盖了硅材料工艺控制里的几何尺寸、电阻率、氧碳含量、缺陷与金属沾污几条主线。它不是操作手册,更像一张索引表——测电阻率该翻 MF 84 还是 MF 374,测间隙氧该走短基线红外还是布鲁斯特角入射,翻到对应行就有答案。
这类清单最容易踩的坑是「看着全、用起来乱」。69 条里 MF 前缀占 67 条,MS 与 ME 各 1 条,编号从 26 排到 2166,跨度几十年,版本日期码又散落在标准号尾部,靠肉眼比对极易搞错新旧。而且这份 PDF 是网页内容录入后打印的产物,正文里能见到「SEMI M F 374」这种多空格写法,也能见到 MF 534-9796 这类不符合日期码规则的条目。
所以它的用法分两层:工艺和 QA 拿它做测试项到标准的对照追溯;做量测自动化或数据平台的同学,则先把它解析成结构化数据,再按参数、方法、版本去检索。
2. SEMI MF/MS/ME 标准号编码规则与硅片工艺控制对象的对应关系
标准号不是流水号,它本身携带三组信息:谁管的(前缀)、管什么(数字编号)、哪一版(尾部日期码)。把这三段拆开,一张 69 行的清单才能变成可排序、可分组、可追版本的索引。很多人直接拿整串标准号当字符串比对,结果在同一条标准出现两个版本时判断失误,这是最典型的误用。
2.1 MF、MS、ME 三种前缀在索引里的实际差别
这份清单里 MF 出现了 67 次,MS 和 ME 各一次,说明它主体是硅材料分册。前缀决定了这条标准挂在哪个技术委员会的体系下,MH、MS 这类条目查到的正文结构、术语定义习惯都不一样。落到数据层面,前缀必须参与主键,原因是清单里同时存在 SEMI MF 1392-1103(带汞探针的 C-V 法测净载流子密度分布)和 SEMI ME1392-3005(角分辨光散射测量指南)——数字部分完全相同,只有前缀能区分。
提示:只用数字编号做唯一键,这两条会互相覆盖,检索「1392」时永远只返回一条。
2.2 后四位日期码:判断版本新旧的第一依据
清单里绝大多数标准号尾部是四位数字,按 YYMM 解读,例如 MF 1619-1105 是 2005 年 11 月批准,MF 1811-0704 是 2004 年 7 月。写个二十行的脚本就能把这一列校验一遍:
import re CODE_RE = re.compile(r'^SEMI\s+(?P<prefix>M[FES]?)\s*(?P<num>\d+)-(?P<yy>\d{2})(?P<mm>\d{2})$') for c in ['SEMI MF 84-1105', 'SEMI MF 374-1105', 'SEMI MF 534-9796', 'SEMI ME 1392-3005']: m = CODE_RE.match(c) yy, mm = int(m['yy']), int(m['mm']) year = 1900 + yy if yy >= 70 else 2000 + yy ok = 1 <= mm <= 12 and 1970 <= year <= 2026 flag = '合法' if ok else '可疑,需按官方目录复核' print(f"{c:22s} prefix={m['prefix']:3s} num={int(m['num']):4d} -> {year}-{mm:02d} {flag}")正则把标准号切成前缀、数字编号、两位年、两位月四段。yy >= 70的分支是为了兼容上世纪的两位年份写法。跑完会发现 MF 534-9796 的月份是 96,ME 1392-3005 的年份落到 2030,两条都越界。这类越界多半不是脚本读错,而是原始网页录入时漏字或串行,处理方式是单独打上「待核」标记,而不是硬塞进日期字段。
2.3 按控制对象把 69 条标准分成七类
标准号排序看不出工艺逻辑,按控制对象分组才有用。下面这七类基本能把清单吃干净:
| 分类 | 典型标准 | 控制对象 |
|---|---|---|
| 几何尺寸 | MF 533 / MF 657 / MF 1530 / MF 928 / MF 671 / MF 728 | 厚度、总厚度变化、翘曲度、边缘轮廓、参考面长度 |
| 电阻率与导电类型 | MF 84 / MF 81 / MF 374 / MF 723 / MF 42 / MF 43 / MF 673 / MF 525 / MF 397 | 体电阻率、径向变化、薄层电阻、掺杂换算、纵向剖面 |
| 氧与碳 | MF 1188 / MF 1619 / MF 951 / MF 1391 / MF 1630 / MF 1239 / MF 1366 | 间隙氧、替位碳、氧析出行为 |
| 金属与表面沾污 | MF 1724 / MF 1618 / MF 154 / MF 1982 / MF 1809 / MF 1810 | 金属沾污、有机污染物、腐蚀坑、缺陷记录 |
| 电学完整性与寿命 | MF 28 / MF 391 / MF 1535 / MF 978 / MF 1153 / MF 1388 / MF 1771 | 少子寿命、扩散长度、深能级、C-V 特性、栅氧完整性 |
| 晶体结构与取向 | MF 26 / MF 847 / MF 1725 / MF 1726 / MF 1727 | 晶体取向、结晶完整性、氧化诱生缺陷 |
| 参考材料与系统校准 | MF 1569 / MF 1527 / MF 2166 / MS1 | 参考晶片、仪器校准、对准标线 |
分组的意义在于反向查找:遇到「外延层厚度测不准」这类问题,先落到几何或电学分类里锁定候选标准,再比对方法的适用边界,比在 69 行里逐条读名称快得多。分类列建议单独存在数据库里,后续检索时它比标准名称里的用词更可靠——很多名称是翻译腔,关键词未必落在你想到的那个字上。
3. 用 pdfplumber 与正则把标准清单结构化,落成可查询的标准库
3.1 先判断 PDF 是文本层还是扫描图
动手写解析前先探底,否则会白写一版 OCR 流程。最省事的办法是先看文本层:
# -layout 保留列对齐,方便肉眼核对表格有没有串行 pdftotext -layout "半导体工艺控制标准参考.pdf" semi_raw.txt wc -l semi_raw.txt head -20 semi_raw.txt如果semi_raw.txt里能直接看到「SEMI MF 2166-0304」这样的完整串,说明 PDF 带文本层,用 pdfplumber 或 pdftotext 就够了。用 pdfplumber 再确认一次页面的字符与图片数量:
import pdfplumber with pdfplumber.open('半导体工艺控制标准参考.pdf') as pdf: p = pdf.pages[0] print('pages:', len(pdf.pages), 'chars:', len(p.chars), 'images:', len(p.images))chars接近零、images不为零,才是扫描件,需要走 OCR。这份清单属于前者,但页面里混了页眉、录入人信息和重复表头,所以真正的活儿在清洗,不在识别。
3.2 正则拆标准号:前缀、数字、版本三件套
清洗加解析可以写成一个模块,一次跑完 69 条:
import re, unicodedata, sqlite3 import pandas as pd NOISE = [ r'有色金属.*?录入[::].*?王\s*凯', # 页眉 r'\d{4}-\d{1,2}-\d{1,2}', # 录入日期 r'人气[::]\s*\d+', r'序号\s*标准号\s*标准名称.*?卷号', # 每页重复的表头 ] LINE_RE = re.compile( r'^(?P<seq>\d{1,3})\s*' r'(?P<code>SEMI\s+M\s?[FES]?\s*\d{2,4}\s*-\s*\d{4})' r'\s*(?P<title>[\u4e00-\u9fa5].*)$' ) def clean(line: str) -> str: """NFKC 归一化全角符号与罗马数字,再剥掉噪声行""" line = unicodedata.normalize('NFKC', line) for pat in NOISE: line = re.sub(pat, '', line) return re.sub(r'\s+', ' ', line).strip() def parse_code(raw: str): """'SEMI M F 374-1105' -> ('MF', 374, '1105')""" s = re.sub(r'\s+', ' ', raw.upper()) s = s.replace('M F', 'MF').replace('M S', 'MS').replace('M E', 'ME') m = re.match(r'SEMI (M[FES]?)\s*(\d+)-(\d{4})', s) return m.group(1), int(m.group(2)), m.group(3) def rev_to_ym(rev: str): """后四位按 YYMM 解读,越界返回 None 作为待核标记""" yy, mm = int(rev[:2]), int(rev[2:]) year = 1900 + yy if yy >= 70 else 2000 + yy if not 1 <= mm <= 12 or not 1970 <= year <= 2026: return None return f'{year:04d}-{mm:02d}'LINE_RE只认「序号开头 + 带短横线的标准号 + 中文名称」这一种行形态,页眉和目录行会在第一道过滤里掉出去。unicodedata.normalize('NFKC')是关键一步:原文里的「Ⅲ-Ⅴ」是全角减号和罗马数字,归一化后变成III-V;「SEMI M F 374」也会因为空格折叠与M F替换,和前后的SEMI MF 374落到同一种写法。rev_to_ym返回None的行会自动聚成一份待核清单。
接着把清洗后的文本灌进解析循环:
rows = [] for raw in open('semi_raw.txt', encoding='utf-8'): m = LINE_RE.match(clean(raw)) if not m: continue prefix, num, rev = parse_code(m['code']) rows.append({ 'seq': int(m['seq']), 'code': f'SEMI {prefix} {num}-{rev}', 'prefix': prefix, 'num': num, 'rev': rev, 'approved_ym': rev_to_ym(rev), 'title': m['title'], 'category': None, # 第 2 章那张表的分类结果回填到这里 }) df = pd.DataFrame(rows).drop_duplicates('seq').sort_values('seq') print(len(df), df.head(3).to_string(index=False))drop_duplicates('seq')防的是同一行被两页同时抓到,sort_values('seq')让结果回到原文档顺序,便于和 PDF 对照。打印出来的条数应该是 69;只要不是 69,就说明有行格式特殊,需要回看原文而不是硬调正则。
3.3 落库 SQLite 并建可检索索引
解析结果落进 SQLite 就够了,量级摆在这儿,没必要上重型数据库:
conn = sqlite3.connect('semi_std.db') df.to_sql('semi_std', conn, if_exists='replace', index=False) conn.executescript(""" CREATE UNIQUE INDEX IF NOT EXISTS idx_code ON semi_std(code); CREATE INDEX IF NOT EXISTS idx_cat ON semi_std(category); -- trigram 分词器支持中文子串匹配;unicode61 会把一整段中文当成单个 token CREATE VIRTUAL TABLE IF NOT EXISTS semi_fts USING fts5( code, title, category, tokenize='trigram' ); INSERT INTO semi_fts (code, title, category) SELECT code, title, COALESCE(category, '') FROM semi_std; """)idx_code做唯一约束,避免同一版本被写入两次。分词器选型有个坑值得记一笔:unicode61按 Unicode 字母类切词,连续中文会被切成一个整体 token,查「平整度」时只有整段标题恰好等于该词才命中;换成trigram才能做子串匹配。但 trigram 也有限制,查询词至少三个字符才走索引,所以:
-- 三个字及以上,走 FTS 索引 SELECT code, title FROM semi_fts WHERE semi_fts MATCH '平整度'; -- 两个字的词低于 trigram 最小长度,退回 LIKE SELECT code, title FROM semi_std WHERE title LIKE '%翘曲%';把这两条查询包成一个函数,传入关键词自动判断长度选路径,日常查标准就不用再翻 PDF。
4. 从标准号到量测方案:电阻率、间隙氧、少子寿命与翘曲度的选型
4.1 电阻率体系的五条线
清单里跟电阻率相关的条目最多,但它们分工完全不同。MF 84 是直排四探针测抛光片体电阻率的基准方法,MF 374 测的是外延层、扩散层、多晶硅和离子注入层的薄层电阻,MF 81 管径向变化,MF 723 负责把电阻率换算成掺杂浓度,MF 525 与 MF 672 用扩展电阻探针做纵向剖面。现实中出错最多的就是拿 MF 84 去套外延层:外延层下面是重掺杂衬底,四探针的电流会从衬底分流,读出来的薄层电阻明显偏低。
| 待测参数 | 首选标准 | 方法原理 | 边界与注意 |
|---|---|---|---|
| 抛光片体电阻率 | MF 84 | 直排四探针 | 需已知厚度并做探针间距修正 |
| 外延/扩散/注入层薄层电阻 | MF 374 | 直排四探针 | 结深与衬底漏电会拉低读数,不可套用 MF 84 |
| 径向电阻率变化 | MF 81 | 多点四探针 | 与 MF 84 配套,报告中必须写明测量点分布 |
| 电阻率换算掺杂浓度 | MF 723 | 经验换算表 | 只覆盖 B/P/As,补偿掺杂样品误差大 |
| 纵向载流子剖面 | MF 525 / MF 672 | 扩展电阻探针 | 需磨角制样,属破坏性测试 |
4.2 氧碳与寿命:红外法和光电导法各自的边界
间隙氧有三条路线:MF 1188 用短基线红外吸收法测常规含量,MF 1619 用布鲁斯特角入射 P 偏振红外测,MF 951 专门测径向变化。三条共用同一套红外谱采集条件,区别在入射角和基线扣法,所以做氧含量时通常把 MF 1391 的替位碳一起测掉,一次进样出两个参数。当样品已经出现氧析出,MF 1239 的氧减少量法比直接测总氧更能反映热历史。
少子寿命这边,MF 28 是光电导衰减的基准方法,MF 1535 用微波反射非接触光电导衰减,胜在不接触、可扫图,产线全检基本走这条;MF 391 测的是表面光电压法下的扩散长度,和寿命互为补充,但结果对表面钝化状态很敏感,抛光片和钝化片的数不能直接比。
4.3 几何与表面缺陷类标准怎么组合
几何参数的标准名称很像,定义差别却会直接影响判级。MF 533 管厚度和厚度变化,MF 657 管翘曲度和总厚度变化,MF 1530 用非接触自动扫描测平整度、厚度和厚度变化,MF 1390 用同类方法只测翘曲度。做报告时把「总厚度变化」和「厚度变化」混用,是审核时最常被挑出来的问题,因为两者一个基于整片、一个基于局部基准面。
表面类则可以按「先看后测」串起来:MF 523 的抛光片目检规程定的是检查条件与判据,MF 154 是识别镜面表面结构与沾污的指南,MF 1809 负责挑腐蚀溶液,MF 1810 负责把择优腐蚀后的缺陷记录下来。产线上一般先目检粗筛,再用腐蚀法复现缺陷,最后按 MF 1810 的格式留档。
4.4 接触式与非接触式的交叉验证
同一参数往往有接触式和非接触式两条路线,比如电阻率有 MF 84 与 MF 673(非接触涡流),翘曲度有 MF 657 与 MF 1390。常见做法是非接触设备跑全检、接触式方法做仲裁:非接触效率高但受表面状态和校准片影响,一旦全检数据出现漂移,用接触式方法在同一批参考片上复测,再对比 MF 1527 定义的参考晶片校准记录,就能判断是片子变了还是机台飘了。用一条 SQL 把这组「同参数多方法」的标准捞出来:
-- 按分类列出标准,并按批准年月倒序,方便优先看最新版本 SELECT seq, code, title FROM semi_std WHERE category IN ('电阻率/导电类型', '几何尺寸') ORDER BY category, CAST(SUBSTR(approved_ym, 1, 4) AS INTEGER) DESC, num;SUBSTR(approved_ym,1,4)取出年份再转整数排序,避免字符串序把1997排到2005后面。分类字段来自第 2 章那张表,回填一次就能长期复用。
5. 版本核查与检索技巧:把标准清单变成随手能查的工作台
5.1 多版本识别与过期条目清理
清单是按录入顺序排的,同一个数字编号若出现多次,肉眼很难发现。用一条聚合语句就能全部揪出来:
SELECT prefix, num, COUNT(*) AS ver_cnt, GROUP_CONCAT(code, ' | ') AS versions, MAX(approved_ym) AS latest FROM semi_std GROUP BY prefix, num HAVING ver_cnt > 1;GROUP BY prefix, num保证 MF 1392 和 ME 1392 分属两组,不会被误判成两个版本。latest列给出该编号下最新的批准年月,直接作为选版依据。同时把待核条目单独捞出来:
SELECT seq, code, title FROM semi_std WHERE approved_ym IS NULL;这批记录不参与版本排序,交回人工按官方目录核对,避免把录入错误当成过期版本处理。
5.2 ripgrep 定位与序号断档自检
不想开数据库时,文本层直接搜最快:
# 统计标准号出现次数,与清单条目数互相印证 rg -o "SEMI\s+M\s?[FES]?\s?\d{2,4}-\d{4}" semi_raw.txt | wc -l # 一次命中所有与翘曲、平整度相关的条目 rg -n "翘曲|平整度|峰谷差|总厚度变化" semi_raw.txt第一条命令用于交叉校验:如果 wc 输出的数量和数据库里的行数对不上,说明有行没被正则吃进去。第二条命令的价值在于中文同义词联想——标准名称里可能写「峰谷差」而你要查的是平整度,用管道把几个近义词连起来搜,比单查一个词命中率高得多。
最后加一道序号自检,这是防止解析悄悄丢数据的兜底:
import pandas as pd df = pd.read_sql('SELECT seq, code FROM semi_std ORDER BY seq', conn) missing = sorted(set(range(1, 70)) - set(df.seq)) dup = df[df.duplicated('seq', keep=False)] print('断档序号:', missing) # 期望是空列表 print('重复序号:', dup.to_dict('records'))range(1, 70)对应清单的 1 到 69 条,set差集优先于逐行比对,duplicated(keep=False)把重复项整组标出来而不是只留后一条。把这套自检和 5.1 的版本清理包成一个脚本,PDF 每次更新后跑一遍,比人工逐行核对 69 条记录省事得多。
本文还有配套的精品资源,点击获取