半导体制造中的计量单位详解
前言
在半导体制造过程中,我们需要用不同的单位来统计和管理生产进度。颗数、片数、条数是三个最常用的计量单位,就像我们日常生活中用”个”、”箱”、”车”来计算不同的物品一样。理解这些单位对于半导体生产管理非常重要。
半导体制造中的”物料”概念
为了理解这三个计量单位,我们先来看看半导体制造中的物料是什么样的:
形象比喻
想象一下制作饼干的过程:
面团 = 晶圆批次(Lot/Batch)
饼干片 = 单片晶圆(Wafer)
小饼干 = 芯片颗粒(Die/Chip)
包装好的饼干 = 封装器件(Device)
实际的层级结构
一个生产批次
↓
包含多片晶圆(像多张饼干片)
↓
每片晶圆上有很多芯片(像饼干片上的小饼干)
↓
芯片封装后变成最终产品
物料层级关系图
graph TD
A[晶圆批次 Lot] --> B[晶圆片 Wafer]
B --> C[芯片颗粒 Die]
C --> D[封装器件 Device]
A1[批次级统计] --> A
B1[片级统计] --> B
C1[颗粒级统计] --> C
D1[器件级统计] --> D
第一种单位:颗数(Die Count)
什么是”颗”?
颗数就是指芯片的个数,就像数糖果一样数芯片的数量。
通俗解释
想象一下:
一片晶圆就像一张饼干片
这张饼干片上密密麻麻刻着很多小方格
每个小方格就是一个芯片,我们叫它”一颗”
一片12英寸的晶圆上可能有几百到几千颗芯片
实际例子
一片晶圆 = 1片
这片晶圆上有 = 1000颗芯片
如果一个批次有25片晶圆:
总共就有 = 25 × 1000 = 25000颗芯片
为什么要用”颗数”统计?
最精确:可以准确知道到底生产了多少个芯片
算良率:比如投入1000颗,最后合格950颗,良率就是95%
算成本:每颗芯片的成本是多少钱
算产能:设备一小时能处理多少颗芯片
计算方式
public class DieProgressCalculator {
/**
* 计算进展颗树
* @param waferCount 晶圆片数
* @param diePerWafer 每片晶圆的芯片数
* @param yieldRate 良率
* @return 进展颗树
*/
public int calculateProgressDieCount(int waferCount, int diePerWafer, double yieldRate) {
return (int) (waferCount * diePerWafer * yieldRate);
}
/**
* 按工序计算累计进展颗树
*/
public Map
Map
for (ProcessStep step : batch.getProcessSteps()) {
int inputDieCount = step.getInputDieCount();
double stepYield = step.getYieldRate();
int outputDieCount = (int) (inputDieCount * stepYield);
progressMap.put(step.getProcessName(), outputDieCount);
}
return progressMap;
}
}
应用场景
1. 生产进度监控
-- 查询各工序的进展颗树
SELECT
process_name,
batch_id,
SUM(progress_die_count) as total_die_progress,
AVG(yield_rate) as avg_yield_rate
FROM process_progress
WHERE batch_id = 'BATCH_001'
GROUP BY process_name, batch_id
ORDER BY process_sequence;
2. 良率分析
public class YieldAnalysis {
public double calculateProcessYield(String processName, String batchId) {
int inputDieCount = getInputDieCount(processName, batchId);
int outputDieCount = getProgressDieCount(processName, batchId);
return (double) outputDieCount / inputDieCount;
}
public YieldTrend analyzeYieldTrend(String processName, LocalDate startDate, LocalDate endDate) {
List
return records.stream()
.collect(Collectors.groupingBy(
record -> record.getProcessDate(),
Collectors.averagingDouble(this::calculateYield)
));
}
}
第二种单位:片数(Wafer Count)
什么是”片”?
片数就是指晶圆的片数,就像数纸张一样数晶圆的数量。
通俗解释
继续用饼干的比喻:
如果”颗”是饼干片上的小饼干个数
那么”片”就是饼干片本身的数量
不管这张饼干片上有多少小饼干,它都算”1片”
实际例子
一个生产批次包含:
- 25片晶圆(就是25片)
- 不管每片上有多少颗芯片,都是25片
设备处理能力:
- 某台设备一小时可以处理5片晶圆
- 不管这5片上总共有多少颗芯片
为什么要用”片数”统计?
设备管理:很多设备是按”片”来处理的,比如一次放入5片晶圆
工艺控制:某些工艺参数是按片设定的,比如每片的处理时间
物料追踪:追踪晶圆在各个工序间的流转
简单直观:比颗数更容易理解和管理
数据结构设计
-- 进站记录表
CREATE TABLE wafer_station_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
batch_id VARCHAR(50) NOT NULL,
process_name VARCHAR(100) NOT NULL,
station_id VARCHAR(50) NOT NULL,
wafer_input_count INT NOT NULL,
wafer_output_count INT,
station_time TIMESTAMP NOT NULL,
operator_id VARCHAR(50),
equipment_id VARCHAR(50),
remarks TEXT,
created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_batch_process (batch_id, process_name),
INDEX idx_station_time (station_time)
);
应用场景
1. 设备产能计算
public class EquipmentCapacityCalculator {
/**
* 计算设备小时产能(片/小时)
*/
public double calculateHourlyCapacity(String equipmentId, LocalDate date) {
List
int totalWafers = records.stream()
.mapToInt(StationRecord::getWaferInputCount)
.sum();
double totalHours = calculateTotalProcessingHours(records);
return totalWafers / totalHours;
}
/**
* 预测批次处理时间
*/
public Duration estimateProcessingTime(String equipmentId, int waferCount) {
double hourlyCapacity = getEquipmentHourlyCapacity(equipmentId);
double estimatedHours = waferCount / hourlyCapacity;
return Duration.ofMinutes((long) (estimatedHours * 60));
}
}
2. 批次流转监控
public class BatchFlowMonitor {
/**
* 监控批次在工序间的片数变化
*/
public List
return processSteps.stream()
.map(process -> {
int inputCount = getWaferInputCount(batchId, process);
int outputCount = getWaferOutputCount(batchId, process);
double lossRate = (double) (inputCount - outputCount) / inputCount;
return WaferFlowRecord.builder()
.processName(process)
.inputCount(inputCount)
.outputCount(outputCount)
.lossRate(lossRate)
.build();
})
.collect(Collectors.toList());
}
}
第三种单位:条数(Strip Count)
什么是”条”?
条数是指载具或包装的条数,主要用在封装和测试阶段。
通俗解释
继续用例子来说明:
想象芯片加工完成后,需要放到”托盘”里进行测试
这个”托盘”就叫做载具或者条
每个托盘可以放一定数量的芯片
我们按托盘的数量来统计,就是”条数”
实际例子
封装测试阶段:
- 1000颗芯片封装完成
- 每个测试载具可以放50颗芯片
- 需要 1000 ÷ 50 = 20个载具
- 所以是20条
包装阶段:
- 每条包装带可以装100个芯片
- 1000颗芯片需要10条包装带
不同阶段的”条”含义
封装阶段:引线框架条数(芯片焊接在上面的框架)
测试阶段:测试载具条数(放芯片进行测试的托盘)
包装阶段:包装条数(最终包装的条带)
为什么要用”条数”统计?
载具管理:知道需要多少个托盘或载具
测试安排:测试设备按条来安排测试计划
包装规格:按标准规格进行包装
物流方便:运输和存储都按条来计算
载具管理系统
public class CarrierManagementSystem {
/**
* 载具信息
*/
@Entity
public class Carrier {
private String carrierId;
private CarrierType type;
private int capacity; // 载具容量
private CarrierStatus status;
private String currentLocation;
private LocalDateTime lastUsedTime;
}
/**
* 进站条数记录
*/
@Entity
public class StripStationRecord {
private String batchId;
private String processName;
private int stripInputCount; // 进站条数
private int stripOutputCount; // 出站条数
private List
private LocalDateTime stationTime;
}
/**
* 计算所需载具数量
*/
public int calculateRequiredCarriers(int deviceCount, int carrierCapacity) {
return (int) Math.ceil((double) deviceCount / carrierCapacity);
}
}
应用场景
1. 测试产能规划
public class TestCapacityPlanner {
/**
* 基于条数计算测试产能
*/
public TestCapacity calculateTestCapacity(String testerId, int stripCount) {
TestEquipment equipment = getTestEquipment(testerId);
// 每条测试时间
Duration timePerStrip = equipment.getTimePerStrip();
// 并行测试数量
int parallelCount = equipment.getParallelTestCount();
// 总测试时间
Duration totalTime = timePerStrip.multipliedBy(
(long) Math.ceil((double) stripCount / parallelCount)
);
return TestCapacity.builder()
.stripCount(stripCount)
.estimatedTime(totalTime)
.parallelCount(parallelCount)
.build();
}
}
2. 载具调度优化
public class CarrierScheduler {
/**
* 载具调度算法
*/
public List
List
// 按优先级排序请求
requests.sort(Comparator.comparing(TestRequest::getPriority).reversed());
for (TestRequest request : requests) {
List
request.getRequiredStripCount(),
request.getCarrierType()
);
if (availableCarriers.size() >= request.getRequiredStripCount()) {
assignments.add(createAssignment(request, availableCarriers));
markCarriersAsUsed(availableCarriers);
} else {
// 载具不足,加入等待队列
addToWaitingQueue(request);
}
}
return assignments;
}
}
三种单位的关系和转换
简单理解它们的关系
用一个完整的例子来说明:
一个生产批次:
├── 25片晶圆(片数 = 25)
├── 每片1000颗芯片
├── 总共25000颗芯片(颗数 = 25000)
├── 封装测试时,每50颗装一个载具
└── 需要500个载具(条数 = 500)
转换关系
颗数 ↔ 片数
颗数 = 片数 × 每片芯片数
片数 = 颗数 ÷ 每片芯片数(向上取整)
例子:
- 25片晶圆,每片1000颗 → 25000颗
- 23500颗芯片,每片1000颗 → 24片(向上取整)
颗数 ↔ 条数
条数 = 颗数 ÷ 每条容量(向上取整)
颗数 = 条数 × 每条容量
例子:
- 25000颗芯片,每条50颗 → 500条
- 500条载具,每条50颗 → 25000颗
片数 ↔ 条数
这个转换需要通过颗数来计算:
片数 → 颗数 → 条数
例子:
- 25片晶圆 → 25000颗芯片 → 500条载具
数量关系
public class UnitConverter {
/**
* 单位转换关系
*/
public class ConversionContext {
private int diePerWafer; // 每片芯片数
private int waferPerStrip; // 每条晶圆数
private int devicePerStrip; // 每条器件数
}
/**
* 颗数转片数
*/
public int dieCountToWaferCount(int dieCount, int diePerWafer) {
return (int) Math.ceil((double) dieCount / diePerWafer);
}
/**
* 片数转条数
*/
public int waferCountToStripCount(int waferCount, int waferPerStrip) {
return (int) Math.ceil((double) waferCount / waferPerStrip);
}
/**
* 颗数转条数(封装后)
*/
public int dieCountToStripCount(int dieCount, int devicePerStrip) {
return (int) Math.ceil((double) dieCount / devicePerStrip);
}
}
统计报表应用
-- 综合生产统计报表
SELECT
b.batch_id,
b.product_code,
-- 进展颗树统计
SUM(p.progress_die_count) as total_die_progress,
-- 进站片数统计
SUM(w.wafer_input_count) as total_wafer_input,
-- 进站条数统计
SUM(s.strip_input_count) as total_strip_input,
-- 综合良率
ROUND(SUM(p.progress_die_count) / SUM(p.input_die_count) * 100, 2) as overall_yield_rate
FROM batch b
LEFT JOIN process_progress p ON b.batch_id = p.batch_id
LEFT JOIN wafer_station_log w ON b.batch_id = w.batch_id
LEFT JOIN strip_station_record s ON b.batch_id = s.batch_id
WHERE b.create_date >= '2024-01-01'
GROUP BY b.batch_id, b.product_code
ORDER BY b.create_date DESC;
MES系统中的实现
数据模型设计
/**
* 生产进度实体
*/
@Entity
@Table(name = "production_progress")
public class ProductionProgress {
@Id
private String progressId;
@Column(name = "batch_id")
private String batchId;
@Column(name = "process_name")
private String processName;
// 三种计量单位
@Column(name = "progress_die_count")
private Integer progressDieCount; // 进展颗树
@Column(name = "wafer_input_count")
private Integer waferInputCount; // 进站片数
@Column(name = "strip_input_count")
private Integer stripInputCount; // 进站条数
@Column(name = "process_time")
private LocalDateTime processTime;
@Column(name = "yield_rate")
private Double yieldRate;
// 转换方法
public int calculateWaferCountFromDie(int diePerWafer) {
return (int) Math.ceil((double) progressDieCount / diePerWafer);
}
public int calculateStripCountFromWafer(int waferPerStrip) {
return (int) Math.ceil((double) waferInputCount / waferPerStrip);
}
}
服务层实现
@Service
public class ProductionProgressService {
/**
* 记录生产进度
*/
public void recordProgress(String batchId, String processName,
ProgressData progressData) {
ProductionProgress progress = new ProductionProgress();
progress.setBatchId(batchId);
progress.setProcessName(processName);
progress.setProgressDieCount(progressData.getDieCount());
progress.setWaferInputCount(progressData.getWaferCount());
progress.setStripInputCount(progressData.getStripCount());
progress.setProcessTime(LocalDateTime.now());
// 计算良率
double yieldRate = calculateYieldRate(batchId, processName);
progress.setYieldRate(yieldRate);
progressRepository.save(progress);
// 发送进度更新事件
eventPublisher.publishEvent(new ProgressUpdateEvent(progress));
}
/**
* 获取批次综合进度
*/
public BatchProgressSummary getBatchProgress(String batchId) {
List
progressRepository.findByBatchIdOrderByProcessTime(batchId);
return BatchProgressSummary.builder()
.batchId(batchId)
.totalDieProgress(calculateTotalDieProgress(progressList))
.totalWaferInput(calculateTotalWaferInput(progressList))
.totalStripInput(calculateTotalStripInput(progressList))
.overallYieldRate(calculateOverallYield(progressList))
.processDetails(progressList)
.build();
}
}
质量管理应用
良率分析
public class YieldAnalysisService {
/**
* 按不同单位分析良率
*/
public YieldAnalysisResult analyzeYield(String batchId) {
List
// 按颗粒级良率分析
double dieYieldRate = calculateDieYieldRate(progressList);
// 按晶圆级良率分析
double waferYieldRate = calculateWaferYieldRate(progressList);
// 按条级良率分析
double stripYieldRate = calculateStripYieldRate(progressList);
return YieldAnalysisResult.builder()
.dieYieldRate(dieYieldRate)
.waferYieldRate(waferYieldRate)
.stripYieldRate(stripYieldRate)
.yieldTrend(calculateYieldTrend(progressList))
.build();
}
/**
* 良率预警
*/
public void checkYieldAlert(String batchId, String processName) {
ProductionProgress progress = getCurrentProgress(batchId, processName);
if (progress.getYieldRate() < getYieldThreshold(processName)) {
YieldAlert alert = YieldAlert.builder()
.batchId(batchId)
.processName(processName)
.currentYield(progress.getYieldRate())
.threshold(getYieldThreshold(processName))
.alertTime(LocalDateTime.now())
.build();
alertService.sendYieldAlert(alert);
}
}
}
报表与可视化
Dashboard设计
@RestController
@RequestMapping("/api/dashboard")
public class ProductionDashboardController {
/**
* 生产进度Dashboard数据
*/
@GetMapping("/progress")
public DashboardData getProgressDashboard(
@RequestParam String dateRange,
@RequestParam(required = false) String productCode) {
// 进展颗树统计
Map
progressService.getDieProgressStats(dateRange, productCode);
// 进站片数统计
Map
progressService.getWaferInputStats(dateRange, productCode);
// 进站条数统计
Map
progressService.getStripInputStats(dateRange, productCode);
return DashboardData.builder()
.dieProgressChart(createChart(dieProgressStats))
.waferInputChart(createChart(waferInputStats))
.stripInputChart(createChart(stripInputStats))
.summaryMetrics(calculateSummaryMetrics())
.build();
}
}
图表配置
// 前端图表配置示例
const progressChartConfig = {
title: {
text: '生产进度统计',
subtext: '按不同计量单位统计'
},
legend: {
data: ['进展颗树', '进站片数', '进站条数']
},
xAxis: {
type: 'category',
data: dateLabels
},
yAxis: [
{
type: 'value',
name: '颗数',
position: 'left'
},
{
type: 'value',
name: '片数/条数',
position: 'right'
}
],
series: [
{
name: '进展颗树',
type: 'line',
yAxisIndex: 0,
data: dieProgressData
},
{
name: '进站片数',
type: 'bar',
yAxisIndex: 1,
data: waferInputData
},
{
name: '进站条数',
type: 'bar',
yAxisIndex: 1,
data: stripInputData
}
]
};
测试策略
单元测试
@Test
public class ProductionProgressTest {
@Test
public void testDieCountCalculation() {
// 测试进展颗树计算
int waferCount = 25;
int diePerWafer = 1000;
double yieldRate = 0.95;
int expectedDieCount = (int) (waferCount * diePerWafer * yieldRate);
int actualDieCount = calculator.calculateProgressDieCount(
waferCount, diePerWafer, yieldRate);
assertEquals(expectedDieCount, actualDieCount);
}
@Test
public void testUnitConversion() {
// 测试单位转换
int dieCount = 23750;
int diePerWafer = 1000;
int expectedWaferCount = 24; // 向上取整
int actualWaferCount = converter.dieCountToWaferCount(dieCount, diePerWafer);
assertEquals(expectedWaferCount, actualWaferCount);
}
@Test
public void testYieldCalculation() {
// 测试良率计算
ProductionProgress progress = createTestProgress();
progress.setProgressDieCount(950);
progress.setWaferInputCount(1);
double expectedYield = 0.95; // 950/1000
double actualYield = yieldService.calculateYield(progress);
assertEquals(expectedYield, actualYield, 0.01);
}
}
最佳实践建议
1. 数据一致性保证
事务管理:确保三种计量单位的数据更新在同一事务中
数据校验:建立单位转换的一致性校验机制
审计日志:记录所有数据变更的详细日志
2. 性能优化
索引优化:为批次ID、工序名称等查询字段建立合适索引
数据分区:按时间或产品类型对大表进行分区
缓存策略:对频繁查询的统计数据进行缓存
3. 业务规则管理
配置化管理:将单位转换比例等参数配置化
版本控制:管理不同产品、不同时期的参数变化
权限控制:严格控制数据修改权限
什么时候用哪个单位?
颗数(最精确)
适用场景:
计算良率:投入多少颗,产出多少颗
成本核算:每颗芯片的成本
精确统计:需要知道确切的产品数量
举例:
“这批产品良率95%,投入10000颗,产出9500颗”
“每颗芯片成本0.5元”
片数(设备管理)
适用场景:
设备产能:设备一次处理多少片
工艺时间:每片需要多长时间处理
物料管理:晶圆的流转追踪
举例:
“这台设备一小时可以处理20片晶圆”
“每片晶圆的加工时间是30分钟”
条数(载具和包装)
适用场景:
测试安排:测试设备按条来安排
包装规格:按标准包装规格
物流运输:运输和存储的基本单位
举例:
“今天要测试100条产品”
“每条包装带装100颗芯片”
总结
颗数、片数、条数是半导体制造中三个重要的计量单位:
颗数:数芯片个数,最精确,用于良率和成本计算
片数:数晶圆张数,用于设备管理和工艺控制
条数:数载具个数,用于测试安排和包装物流
记忆口诀
颗数算良率,精确又细致
片数管设备,工艺好控制
条数做测试,包装和物流
理解这三个单位,就像理解”个、箱、车”一样简单。不同的场景用不同的单位,让生产管理更加清晰高效。
本文基于半导体制造实践经验总结,如有疑问欢迎交流讨论。
Previous
MES系统工序跳转测试详解
Next
芯片封测厂载具(Container)详解