芯片测试之trim详解
文章目录
- 1. trim的概念和目的
- 2. trim方式
- 2.1 OTP(One-Time Programmable)一次性修调
- 2.2 EEPROM(Electrically Erasable PROM)修调
- 3. trim算法
- 3.1 电压trim code table
- 3.2 电压测量函数
- 3.3 公式法验证
- 3.4 线性法验证
- 3.5 二分法验证并对比结果
1. trim的概念和目的
-
概念:trim 是芯片制造后通过改变内部器件连接或工作状态的技术,本质上是对芯片参数的微调校准。它是提升良率和功能灵活性的关键步骤。
-
目的:
2.1 补偿工艺问题:半导体制造存在固有波动(如线宽、掺杂浓度),导致芯片参数(如电压、频率)与设计值产生偏差,通过熔断熔丝(Fuse)、写入寄存器(eFuse/OTP)或激光修调等方式,调整电阻/电容网络或偏置电路,将芯片参数拉回目标范围。
2.2 实现单芯片多功能需求:客户可能需要同一芯片的不同规格(如LDO输出1.8V/2.5V/3.3V),传统方案需设计多款芯片,增加制造成本。Trim 方案:设计一个基础内核(Core),测试时通过Trim改变反馈分压比、电流源大小等,生成不同衍生型号(如Vout可调的LDO)。
2. trim方式
2.1 OTP(One-Time Programmable)一次性修调
可以永久改变电路连接,仅允许单次写入,无法逆转,有如下实现方式:
-
齐纳二极管(Zener Trim):未修调时齐纳二极管开路,修调时施加反向高压将其永久击穿短路,使并联的电阻从电路中被剔除。
-
熔丝(Fuse Trim):在电路中设计金属或多晶硅熔丝,施加 >5V 的高压使其熔断形成开路,从而改变电路参数。
-
激光调阻(Laser Trim):使用激光切割薄膜电阻,调整电阻值精度。
-
EPROM(Erasable Programmable ROM)修调:将修调数据写入浮栅晶体管构成的存储器,通过紫外线照射擦除数据,封装后无法擦除,因为芯片封盖会阻挡紫外线,实际应用中属于类OTP方案。
2.2 EEPROM(Electrically Erasable PROM)修调
通过电信号擦写浮栅晶体管数据,无需物理破坏。
- 狭义EEPROM:可修改任意单字节
- Flash:按区块操作(如1KB),速度快但小数据修改效率低,广泛用于MCU程序存储(如固件),不适用频繁改写的小数据。
EEPROM Trim 的电路结构是 电阻网络 + MOS 开关矩阵 ,每个修调电阻(Trim Resistor)并联一个 MOS 晶体管开关,MOS 开关的通断状态由 EEPROM 存储单元的 0/1 值直接控制(1 = 导通,0 = 关断),对于8 位修调系统 = 8个电阻 + 8个MOS开关 → 可产生 28 = 256 种阻值组合。
3. trim算法
我将使用c++98创建一个class来模拟trim算法的公式法,线性法,二分法,可以通过下面的代码学习trim的原理,因为是模拟,并不包含93k测试机相关的API函数,仅仅是学习使用。
3.1 电压trim code table
首先我们来实现一个电压trim code表,假设电压的范围是1.5V到2.5V,一共有32个code,也就是0~31,电压的步长为:0.03226V。
#include <iostream>
#include <iomanip>
#include <utility> // 用于std::pair
#include <cstdlib> // 用于rand()和srand()
#include <ctime> // 用于time()
#include <cmath> // 用于round()和fabs()class TrimSimulator {
private:// 电压校准表:存储32个(code, voltage)键值对std::pair<int, double> trimTable[32];// 电压范围参数double MIN_VOLTAGE = 1.5;double MAX_VOLTAGE = 2.5;// 电压步进值 = (2.5-1.5)/31 ≈ 0.0323Vdouble VOLTAGE_STEP = (MAX_VOLTAGE - MIN_VOLTAGE) / 31;public:// 构造函数:初始化校准表TrimSimulator() {for (int code = 0; code < 32; ++code) {double voltage = MIN_VOLTAGE + code * VOLTAGE_STEP;trimTable[code] = std::make_pair(code, voltage); // 填充校准表}}
};
3.2 电压测量函数
我们在class中实现一个电压测量函数,以模拟芯片测试机的电压测量动作,这个函数通过code作为输入参数,然后将code减去3(假设这是实际值与理论值的偏差),查找code表中的电压值后,再减去一个随机数小量(假设这是测量误差),然后返回这个值作为电压测量值。
#include <iostream>
#include <iomanip>
#include <utility> // 用于std::pair
#include <cstdlib> // 用于rand()和srand()
#include <ctime> // 用于time()
#include <cmath> // 用于round()和fabs()class TrimSimulator {
private:// 电压校准表:存储32个(code, voltage)键值对std::pair<int, double> trimTable[32];// 电压范围参数double MIN_VOLTAGE = 1.5;double MAX_VOLTAGE = 2.5;// 电压步进值 = (2.5-1.5)/31 ≈ 0.0323Vdouble VOLTAGE_STEP = (MAX_VOLTAGE - MIN_VOLTAGE) / 31;// 测量误差范围 ±10mVdouble MEASUREMENT_ERROR_RANGE = 0.01;// 生成[-0.01, 0.01]范围内的随机测量误差double generateRandomError() const {return (2.0 * rand() / RAND_MAX - 1.0) * MEASUREMENT_ERROR_RANGE;}public:// 构造函数:初始化校准表TrimSimulator() {srand(static_cast<unsigned>(time(0))); // 用当前时间初始化随机种子for (int code = 0; code < 32; ++code) {double voltage = MIN_VOLTAGE + code * VOLTAGE_STEP;trimTable[code] = std::make_pair(code, voltage); // 填充校准表}}// 电压测量函数(含随机误差)double measureVoltage(int code) const {int adjustedCode = code - 3; // 硬件偏差补偿// 边界保护:确保code在[0,31]范围内if (adjustedCode < 0) adjustedCode = 0;if (adjustedCode >= 32) adjustedCode = 31;double baseVoltage = trimTable[adjustedCode].second;double measuredVoltage = baseVoltage + generateRandomError(); // 添加随机误差// 电压钳位:确保测量值在物理范围内if (measuredVoltage < MIN_VOLTAGE) return MIN_VOLTAGE;if (measuredVoltage > MAX_VOLTAGE) return MAX_VOLTAGE;return measuredVoltage;}
};
3.3 公式法验证
假设芯片初始code是5,trim的目标范围是:下限 2.2 - 0.02 V,上限 2.2 + 0.02 V,目标值是 2.2 V。
我们先实现公式法:(目标电压值 - 初始电压值)/ 电压步长 = code,目标电压值和初始电压值作为公式法函数输入参数,code作为返回值。
在main函数中设置一个变量使code = 5,调用测量电压的函数,返回值作为初始电压值,然后调用公式法的函数,返回一个修调后的code,再将这个code,调用测量电压的函数,与上下限比较看看是不是在范围内,在输出pass,不在输出fail。
#include <iostream>
#include <iomanip>
#include <utility> // 用于std::pair
#include <cstdlib> // 用于rand()和srand()
#include <ctime> // 用于time()
#include <cmath> // 用于round()和fabs()class TrimSimulator {
private:// 电压校准表:存储32个(code, voltage)键值对std::pair<int, double> trimTable[32];// 电压范围参数double MIN_VOLTAGE = 1.5;double MAX_VOLTAGE = 2.5;// 电压步进值 = (2.5-1.5)/31 ≈ 0.0323Vdouble VOLTAGE_STEP = (MAX_VOLTAGE - MIN_VOLTAGE) / 31;// 测量误差范围 ±10mVdouble MEASUREMENT_ERROR_RANGE = 0.01;// 生成[-0.01, 0.01]范围内的随机测量误差double generateRandomError() const {return (2.0 * rand() / RAND_MAX - 1.0) * MEASUREMENT_ERROR_RANGE;}public:// 构造函数:初始化校准表TrimSimulator() {srand(static_cast<unsigned>(time(0))); // 用当前时间初始化随机种子for (int code = 0; code < 32; ++code) {double voltage = MIN_VOLTAGE + code * VOLTAGE_STEP;trimTable[code] = std::make_pair(code, voltage); // 填充校准表}}// 电压测量函数(含随机误差)double measureVoltage(int code) const {int adjustedCode = code - 3; // 硬件偏差补偿// 边界保护:确保code在[0,31]范围内if (adjustedCode < 0) adjustedCode = 0;if (adjustedCode >= 32) adjustedCode = 31;double baseVoltage = trimTable[adjustedCode].second;double measuredVoltage = baseVoltage + generateRandomError(); // 添加随机误差// 电压钳位:确保测量值在物理范围内if (measuredVoltage < MIN_VOLTAGE) return MIN_VOLTAGE;if (measuredVoltage > MAX_VOLTAGE) return MAX_VOLTAGE;return measuredVoltage;}// 公式法计算目标Code增量int calculateTrimCode(double targetVoltage, double initialVoltage) const {double deltaVoltage = targetVoltage - initialVoltage;// 根据电压差和步进值计算Code增量int codeDelta = static_cast<int>(round(deltaVoltage / VOLTAGE_STEP));return codeDelta;}};int main() {TrimSimulator tester; // 创建校准模拟器实例// 目标电压参数设置const double TARGET_VOLTAGE = 2.2; // 目标电压2.2Vconst double TOLERANCE = 0.02; // 允许误差±20mVconst double LOWER_BOUND = TARGET_VOLTAGE - TOLERANCE; // 电压下限2.18Vconst double UPPER_BOUND = TARGET_VOLTAGE + TOLERANCE; // 电压上限2.22V// 初始状态设置int initialCode = 5; // 起始Code值double initialVoltage = tester.measureVoltage(initialCode); // 测量初始电压// 公式法计算目标Codeint formulaCode = tester.calculateTrimCode(TARGET_VOLTAGE, initialVoltage);double formulaVoltage = tester.measureVoltage(formulaCode);// 验证公式法结果是否在目标范围内bool isFormulaPass = (formulaVoltage >= LOWER_BOUND) && (formulaVoltage <= UPPER_BOUND);// 输出公式法验证结果std::cout << "===== 公式法验证 =====" << std::endl;std::cout << "初始Code: " << initialCode<< " | 初始电压: " << std::fixed << std::setprecision(4) << initialVoltage << "V\n";std::cout << "目标电压: " << TARGET_VOLTAGE << "V ±" << TOLERANCE * 1000 << "mV\n";std::cout << "公式法目标Code: " << formulaCode<< " | 实测电压: " << formulaVoltage << "V\n";std::cout << "公式法结果: " << (isFormulaPass ? "PASS" : "FAIL")<< " [" << LOWER_BOUND << "V ~ " << UPPER_BOUND << "V]\n";return 0;
}
一般情况下公式法可以作为粗调,先快速与目标值拉近距离,然后在使用其他方法精调。
公式法执行结果:
3.4 线性法验证
因为一般情况下粗调的结果是fail的,而且这次模拟的trim code表是线性递增的,在fail的情况下,我们可以使用线性法。
我们实现一个线性法函数,code和limit作为输入值,若起始电压 < 目标下限 → 需升压 → direction=1 增大Code值,若起始电压 > 目标上限 → 需降压 → direction=-1 减小Code值,直至测量电压进入目标范围,返回此时的code。
#include <iostream>
#include <iomanip>
#include <utility> // 用于std::pair
#include <cstdlib> // 用于rand()和srand()
#include <ctime> // 用于time()
#include <cmath> // 用于round()和fabs()class TrimSimulator {
private:// 电压校准表:存储32个(code, voltage)键值对std::pair<int, double> trimTable[32];// 电压范围参数double MIN_VOLTAGE = 1.5;double MAX_VOLTAGE = 2.5;// 电压步进值 = (2.5-1.5)/31 ≈ 0.0323Vdouble VOLTAGE_STEP = (MAX_VOLTAGE - MIN_VOLTAGE) / 31;// 测量误差范围 ±10mVdouble MEASUREMENT_ERROR_RANGE = 0.01;// 生成[-0.01, 0.01]范围内的随机测量误差double generateRandomError() const {return (2.0 * rand() / RAND_MAX - 1.0) * MEASUREMENT_ERROR_RANGE;}public:// 构造函数:初始化校准表TrimSimulator() {srand(static_cast<unsigned>(time(0))); // 用当前时间初始化随机种子for (int code = 0; code < 32; ++code) {double voltage = MIN_VOLTAGE + code * VOLTAGE_STEP;trimTable[code] = std::make_pair(code, voltage); // 填充校准表}}// 电压测量函数(含随机误差)double measureVoltage(int code) const {int adjustedCode = code - 3; // 硬件偏差补偿// 边界保护:确保code在[0,31]范围内if (adjustedCode < 0) adjustedCode = 0;if (adjustedCode >= 32) adjustedCode = 31;double baseVoltage = trimTable[adjustedCode].second;double measuredVoltage = baseVoltage + generateRandomError(); // 添加随机误差// 电压钳位:确保测量值在物理范围内if (measuredVoltage < MIN_VOLTAGE) return MIN_VOLTAGE;if (measuredVoltage > MAX_VOLTAGE) return MAX_VOLTAGE;return measuredVoltage;}// 公式法计算目标Code增量int calculateTrimCode(double targetVoltage, double initialVoltage) const {double deltaVoltage = targetVoltage - initialVoltage;// 根据电压差和步进值计算Code增量int codeDelta = static_cast<int>(round(deltaVoltage / VOLTAGE_STEP));return codeDelta;}// 线性搜索法调整Codeint linearSearchTrim(int startCode, double lowerBound, double upperBound) const {// 确定调整方向(升压1或降压-1)int direction = (measureVoltage(startCode) < lowerBound) ? 1 : -1;int currentCode = startCode;int stepCount = 0; // 迭代计数器bool found = false; // 目标标记std::cout << "\n===== 线性法执行过程 =====" << std::endl;// 在[0,31]范围内线性搜索while (currentCode >= 0 && currentCode <= 31) {double voltage = measureVoltage(currentCode);stepCount++;// 实时输出搜索状态std::cout << "迭代" << stepCount << ": Code=" << currentCode<< " | 电压=" << std::fixed << std::setprecision(4) << voltage << "V\n";// 检查是否达到目标电压范围if (voltage >= lowerBound && voltage <= upperBound) {std::cout << "√ 找到目标: Code=" << currentCode << " (迭代" << stepCount << "次)\n";found = true;break;}currentCode += direction; // 沿方向调整Code}// 未找到有效解的失败处理if (!found) {std::cout << "× 搜索失败,最终Code=" << currentCode << "\n";}return currentCode;}};int main() {TrimSimulator tester; // 创建校准模拟器实例// 目标电压参数设置const double TARGET_VOLTAGE = 2.2; // 目标电压2.2Vconst double TOLERANCE = 0.02; // 允许误差±20mVconst double LOWER_BOUND = TARGET_VOLTAGE - TOLERANCE; // 电压下限2.18Vconst double UPPER_BOUND = TARGET_VOLTAGE + TOLERANCE; // 电压上限2.22V// 初始状态设置int initialCode = 5; // 起始Code值double initialVoltage = tester.measureVoltage(initialCode); // 测量初始电压// 公式法计算目标Codeint formulaCode = tester.calculateTrimCode(TARGET_VOLTAGE, initialVoltage);double formulaVoltage = tester.measureVoltage(formulaCode);// 验证公式法结果是否在目标范围内bool isFormulaPass = (formulaVoltage >= LOWER_BOUND) && (formulaVoltage <= UPPER_BOUND);// 输出公式法验证结果std::cout << "===== 公式法验证 =====" << std::endl;std::cout << "初始Code: " << initialCode<< " | 初始电压: " << std::fixed << std::setprecision(4) << initialVoltage << "V\n";std::cout << "目标电压: " << TARGET_VOLTAGE << "V ±" << TOLERANCE * 1000 << "mV\n";std::cout << "公式法目标Code: " << formulaCode<< " | 实测电压: " << formulaVoltage << "V\n";std::cout << "公式法结果: " << (isFormulaPass ? "PASS" : "FAIL")<< " [" << LOWER_BOUND << "V ~ " << UPPER_BOUND << "V]\n";// 公式法失败时执行线性法if (!isFormulaPass) {// 线性搜索法执行int linearCode = tester.linearSearchTrim(formulaCode, LOWER_BOUND, UPPER_BOUND);double linearVoltage = tester.measureVoltage(linearCode);bool linearPass = (linearVoltage >= LOWER_BOUND) && (linearVoltage <= UPPER_BOUND);// 输出报告std::cout << "\n=========== 线性法报告 ===========" << std::endl;std::cout << "| 方法 | 最终Code | 最终电压 | 结果 |\n";std::cout << "|--------|----------|------------|-------|\n";std::cout << "| 线性法 | " << std::setw(8) << linearCode<< " | " << std::setw(8) << linearVoltage << "V | "<< (linearPass ? "PASS" : "FAIL") << " |\n";}return 0;
}
线性法的结果:
3.5 二分法验证并对比结果
再实现一个二分法函数,code和limit作为输入值,取中点并测量电压,电压过低需升压 → 右移区间,电压过高需降压 → 左移区间,电压命中目标范围 → 返回当前Code。
#include <iostream>
#include <iomanip>
#include <utility> // 用于std::pair
#include <cstdlib> // 用于rand()和srand()
#include <ctime> // 用于time()
#include <cmath> // 用于round()和fabs()class TrimSimulator {
private:// 电压校准表:存储32个(code, voltage)键值对std::pair<int, double> trimTable[32];// 电压范围参数double MIN_VOLTAGE = 1.5;double MAX_VOLTAGE = 2.5;// 电压步进值 = (2.5-1.5)/31 ≈ 0.0323Vdouble VOLTAGE_STEP = (MAX_VOLTAGE - MIN_VOLTAGE) / 31;// 测量误差范围 ±10mVdouble MEASUREMENT_ERROR_RANGE = 0.01;// 生成[-0.01, 0.01]范围内的随机测量误差double generateRandomError() const {return (2.0 * rand() / RAND_MAX - 1.0) * MEASUREMENT_ERROR_RANGE;}public:// 构造函数:初始化校准表TrimSimulator() {srand(static_cast<unsigned>(time(0))); // 用当前时间初始化随机种子for (int code = 0; code < 32; ++code) {double voltage = MIN_VOLTAGE + code * VOLTAGE_STEP;trimTable[code] = std::make_pair(code, voltage); // 填充校准表}}// 电压测量函数(含随机误差)double measureVoltage(int code) const {int adjustedCode = code - 3; // 硬件偏差补偿// 边界保护:确保code在[0,31]范围内if (adjustedCode < 0) adjustedCode = 0;if (adjustedCode >= 32) adjustedCode = 31;double baseVoltage = trimTable[adjustedCode].second;double measuredVoltage = baseVoltage + generateRandomError(); // 添加随机误差// 电压钳位:确保测量值在物理范围内if (measuredVoltage < MIN_VOLTAGE) return MIN_VOLTAGE;if (measuredVoltage > MAX_VOLTAGE) return MAX_VOLTAGE;return measuredVoltage;}// 公式法计算目标Code增量int calculateTrimCode(double targetVoltage, double initialVoltage) const {double deltaVoltage = targetVoltage - initialVoltage;// 根据电压差和步进值计算Code增量int codeDelta = static_cast<int>(round(deltaVoltage / VOLTAGE_STEP));return codeDelta;}// 线性搜索法调整Codeint linearSearchTrim(int startCode, double lowerBound, double upperBound) const {// 确定调整方向(升压1或降压-1)int direction = (measureVoltage(startCode) < lowerBound) ? 1 : -1;int currentCode = startCode;int stepCount = 0; // 迭代计数器bool found = false; // 目标标记std::cout << "\n===== 线性法执行过程 =====" << std::endl;// 在[0,31]范围内线性搜索while (currentCode >= 0 && currentCode <= 31) {double voltage = measureVoltage(currentCode);stepCount++;// 实时输出搜索状态std::cout << "迭代" << stepCount << ": Code=" << currentCode<< " | 电压=" << std::fixed << std::setprecision(4) << voltage << "V\n";// 检查是否达到目标电压范围if (voltage >= lowerBound && voltage <= upperBound) {std::cout << "√ 找到目标: Code=" << currentCode << " (迭代" << stepCount << "次)\n";found = true;break;}currentCode += direction; // 沿方向调整Code}// 未找到有效解的失败处理if (!found) {std::cout << "× 搜索失败,最终Code=" << currentCode << "\n";}return currentCode;}// 二分法搜索目标Codeint binarySearchTrim(int startCode, double lowerBound, double upperBound) const {int low = 0; // 搜索下界int high = 31; // 搜索上界int bestCode = startCode; // 最佳Code记录int stepCount = 0; // 迭代计数器std::cout << "\n===== 二分法执行过程 =====" << std::endl;// 二分搜索主循环while (low <= high) {int mid = (low + high) / 2; // 计算中点double voltage = measureVoltage(mid);stepCount++;// 输出当前搜索状态std::cout << "迭代" << stepCount << ": Code=" << mid<< " | 电压=" << std::fixed << std::setprecision(4) << voltage << "V"<< " | 区间[" << low << "," << high << "]\n";// 检查是否命中目标范围if (voltage >= lowerBound && voltage <= upperBound) {std::cout << "√ 找到目标: Code=" << mid << " (迭代" << stepCount << "次)\n";return mid;}// 电压不足时向右半区搜索else if (voltage < lowerBound) {low = mid + 1;}// 电压超限时向左半区搜索else {high = mid - 1;}}// 未找到精确解时返回最佳近似std::cout << "× 未找到精确解,返回最佳Code=" << bestCode << "\n";return bestCode;}
};int main() {TrimSimulator tester; // 创建校准模拟器实例// 目标电压参数设置const double TARGET_VOLTAGE = 2.2; // 目标电压2.2Vconst double TOLERANCE = 0.02; // 允许误差±20mVconst double LOWER_BOUND = TARGET_VOLTAGE - TOLERANCE; // 电压下限2.18Vconst double UPPER_BOUND = TARGET_VOLTAGE + TOLERANCE; // 电压上限2.22V// 初始状态设置int initialCode = 5; // 起始Code值double initialVoltage = tester.measureVoltage(initialCode); // 测量初始电压// 公式法计算目标Codeint formulaCode = tester.calculateTrimCode(TARGET_VOLTAGE, initialVoltage);double formulaVoltage = tester.measureVoltage(formulaCode);// 验证公式法结果是否在目标范围内bool isFormulaPass = (formulaVoltage >= LOWER_BOUND) && (formulaVoltage <= UPPER_BOUND);// 输出公式法验证结果std::cout << "===== 公式法验证 =====" << std::endl;std::cout << "初始Code: " << initialCode<< " | 初始电压: " << std::fixed << std::setprecision(4) << initialVoltage << "V\n";std::cout << "目标电压: " << TARGET_VOLTAGE << "V ±" << TOLERANCE * 1000 << "mV\n";std::cout << "公式法目标Code: " << formulaCode<< " | 实测电压: " << formulaVoltage << "V\n";std::cout << "公式法结果: " << (isFormulaPass ? "PASS" : "FAIL")<< " [" << LOWER_BOUND << "V ~ " << UPPER_BOUND << "V]\n";// 公式法失败时执行线性法和二分法if (!isFormulaPass) {// 线性搜索法执行int linearCode = tester.linearSearchTrim(formulaCode, LOWER_BOUND, UPPER_BOUND);double linearVoltage = tester.measureVoltage(linearCode);bool linearPass = (linearVoltage >= LOWER_BOUND) && (linearVoltage <= UPPER_BOUND);// 二分搜索法执行int binaryCode = tester.binarySearchTrim(formulaCode, LOWER_BOUND, UPPER_BOUND);double binaryVoltage = tester.measureVoltage(binaryCode);bool binaryPass = (binaryVoltage >= LOWER_BOUND) && (binaryVoltage <= UPPER_BOUND);// 输出对比报告std::cout << "\n===== 方法对比报告 =====" << std::endl;std::cout << "| 方法 | 最终Code | 最终电压 | 结果 |\n";std::cout << "|--------|----------|------------|-------|\n";std::cout << "| 线性法 | " << std::setw(8) << linearCode<< " | " << std::setw(8) << linearVoltage << "V | "<< (linearPass ? "PASS" : "FAIL") << " |\n";std::cout << "| 二分法 | " << std::setw(8) << binaryCode<< " | " << std::setw(8) << binaryVoltage << "V | "<< (binaryPass ? "PASS" : "FAIL") << " |\n";}return 0;
}
对比线性法和二分法的结果: