运行后的界面:纯C# winform 开发。
源码下载:https://download.csdn.net/download/qq_31982109/90418979
用户的需求包括:
1. 左侧界面有下拉列表选择晶圆规格(4到12英寸)。
2. 输入单芯片的长和宽,单位毫米。
3. 输入边沿抛弃尺寸,单位毫米。
4. 点击按钮后,计算可生产的芯片数量,显示在左下侧。
5. 在左侧区域绘制晶圆图,黑色圆圈表示晶圆,红色圆圈表示扣除边沿后的有效区域。
首先,界面设计。主窗体左侧需要这些控件,可能需要使用Panel来布局。下拉列表ComboBox添加4、6、8、10、12英寸的选项。两个文本框用于芯片尺寸,一个用于边沿抛弃尺寸。按钮触发计算。结果显示可以用Label,绘图区域可能需要一个PictureBox。
接下来是计算部分。关键是如何根据晶圆尺寸、芯片尺寸和边沿抛弃尺寸,计算有效区域内的芯片数量。需要注意单位转换,因为晶圆规格是英寸,而输入是毫米。1英寸等于25.4毫米,所以需要将晶圆直径转换为毫米。例如,12英寸是304.8毫米。
有效区域的半径是晶圆半径减去边沿抛弃尺寸。晶圆的有效区域是一个圆,需要在这个圆内排列芯片。排列方式可能影响数量,这里可能需要简单的网格排列,假设芯片是矩形,以行和列的方式排列,每个芯片的位置是否在有效圆内。
不过,精确计算可能比较复杂,因为需要考虑每个芯片的位置是否在有效圆内。但用户可能希望一个近似值,或者使用简单的网格计算,减去边缘无法放置的芯片。或者,可以使用更高效的算法,比如确定可放置的区域,然后计算行列数,再检查每个芯片是否在有效区域内。
可能还需要考虑芯片的方向,但用户没有说明,所以假设长和宽是固定的,不能旋转。
步骤分解:
1. 转换晶圆直径为毫米,计算半径。
2. 计算有效半径:晶圆半径 - 边沿抛弃尺寸。
3. 有效区域是一个圆,中心与晶圆相同。
4. 计算在有效圆内能放置多少芯片。每个芯片占据一个矩形区域,从中心开始排列,或者从左上角开始?
这里可能需要确定排列的起始点,比如从有效区域的左上角开始,或者从中心向四周排列。但通常,晶圆的中心是原点,所以可能需要将坐标原点设在圆心,然后确定每个芯片的位置是否在有效圆内。
或者,更简单的方式是计算有效区域的直径,然后计算在X和Y方向上能放置多少芯片,但这样可能会高估数量,因为角落的芯片可能超出有效圆。所以可能需要更精确的计算。
比如,假设芯片从中心点开始排列,以网格方式向四周扩展,然后检查每个芯片的四个角是否在有效圆内。或者,计算每个芯片的中心是否在有效圆内。这取决于用户的良率标准,但用户提到边沿抛弃是因为良率差,所以可能认为只要芯片的任何部分在有效区域外就需要抛弃。因此,需要确保整个芯片都在有效区域内。
这增加了复杂性,因为必须确保整个芯片位于有效圆内。因此,每个芯片的位置必须满足其四个角到圆心的距离都小于等于有效半径。
可能的算法步骤:
- 确定每个芯片的位置,以网格方式排列,从中心开始,向四周扩展。
- 对于每个可能的位置,计算该芯片的四个顶点到圆心的距离,如果所有顶点的距离都小于等于有效半径,则该芯片有效。
- 统计所有有效的芯片数量。
但这样的计算可能会比较耗时,尤其是对于大晶圆。或者,可以采用数学方法计算最大可排列的行和列,然后减去超出圆的部分。
例如,将晶圆的有效区域视为半径为R的圆,芯片的尺寸为w(宽)和h(高)。假设芯片排列成网格,从中心开始,左右对称,上下对称。那么,每一行和列可以放置多少个芯片?
另一种方法是使用极坐标,但可能比较复杂。或者,将问题简化为在圆内排列矩形的最优方式,这可能需要一些几何计算。
不过,用户可能希望一个近似值,而不是绝对精确的计算。因此,可能采用将有效区域视为一个正方形,然后计算行数和列数,再乘以4(四个象限),但这样显然不准确。
或者,可以使用数学方法计算每个可能的行和列,检查该位置的芯片是否在圆内。
例如,对于每个可能的x和y坐标(以圆心为原点),检查该位置的芯片是否完全在圆内。这可能涉及到遍历所有可能的x和y,但效率可能不高,不过对于现代计算机来说,可能足够快,尤其是当芯片尺寸较大时。
或者,可以使用数学公式来确定在x方向的最大可能位移,然后计算在y方向上每个x对应的最大y范围。
这可能需要较多的数学推导。假设芯片的位置是从圆心开始,以网格方式排列,每个芯片的位置为(i*w, j*h),其中i和j是整数,代表行和列的偏移量。但需要考虑正负方向,所以i和j可以是正负整数。然后,对于每个芯片的位置,检查该芯片的四个角是否在有效圆内。
例如,芯片的左上角坐标为(x - w/2, y - h/2),右下角坐标为(x + w/2, y + h/2)。但这里的坐标系统需要确定。可能需要将圆心作为原点,向右为x正方向,向上为y正方向,或者向下,这取决于绘图时的坐标系统。
在WinForm的绘图控件中,默认的坐标系是左上角为原点,x向右,y向下。这可能需要在绘制时进行坐标转换,但在计算时可能需要使用标准数学坐标系,即原点在中心,y向上。或者在计算时统一使用原点在圆心,y向上,然后在绘制时转换为PictureBox的坐标系。
这可能比较复杂,但必须处理坐标转换。
现在,回到计算问题。假设有效区域的半径是R(单位毫米),芯片的宽度为w,高度为h。要确定在有效圆内能放置多少个这样的芯片。
可能的步骤:
1. 确定每个芯片的位置,以网格方式排列,从圆心开始,向四周扩展。
2. 对于每个可能的网格位置,计算该芯片的四个角是否都在有效圆内。
3. 统计所有满足条件的芯片数量。
但这样的方法可能需要遍历所有可能的网格点,这可能会比较耗时,尤其是当芯片很小,晶圆很大时,会有很多位置需要检查。不过,对于用户来说,可能可以接受,因为点击按钮后的计算时间不会太长。
或者,可以使用数学方法计算最大行和列,然后计算每个行和列中能放置的芯片数。
例如,在x方向上,最大的位移是R - w/2(因为芯片的右边缘不能超过R),所以最大的x坐标是R - w/2。同样,最小x坐标是 - (R - w/2)。所以,在x方向上,每个芯片占据w的宽度,所以x方向上的数量是 floor((R - w/2)/w) * 2 + 1(包括正负方向和中心)。
同样,对于y方向,但需要考虑每个x对应的y的最大值。这可能比较复杂,因为对于每个x,y的范围可能不同。
或者,可以计算在x方向上的每个可能的列,然后对于每个列,计算在该列中可以放置多少个芯片。
例如,对于x从 - (R - w/2) 到 + (R - w/2),步长是w,每个步长代表一列。然后,对于每个x,计算在该列中,y的最大范围,使得芯片的顶部和底部都在有效圆内。
对于每个x,芯片的中心x坐标为x_val,那么该列中y的最大可能范围由圆的方程:x² + y² ≤ R²。芯片的高度为h,所以芯片的顶部和底部的y坐标为 y_val ± h/2。要确保这两个y坐标满足 y_top² ≤ R² - x_val²,和 y_bottom² ≤ R² - x_val²。
这可能需要为每个x列计算y的可能范围,然后计算在该范围内可以放置多少个芯片。
这可能更高效,因为不需要检查每个网格点,而是按列计算。
举个例子,假设x_val是某个列的中心x坐标,那么该列的有效y范围是 -sqrt(R² -x_val²) + h/2 ≤ y_val ≤ sqrt(R² -x_val²) - h/2。然后,每个芯片在y方向上占据h的高度,所以可以放置的数量为 floor((sqrt(R² -x_val²) - h/2)/h) * 2 +1(上下对称)。
这可能比较复杂,但可以编写代码来实现。
不过,这样处理可能需要处理浮点数运算,并且要注意精度问题。
现在,我需要将这些思路转化为C#代码。可能需要创建一个方法,接收晶圆半径、芯片尺寸、边沿抛弃尺寸,返回芯片数量。
同时,绘图部分需要绘制两个圆:黑色的是晶圆本身,红色的是有效区域。然后在有效区域内绘制芯片的位置,或者可能只需显示两个圆,用户可能只需要预览晶圆和有效区域的大小,不一定需要显示每个芯片的位置。但用户提到要“绘图预览整个晶圆的芯片分布情况”,所以可能需要绘制每个芯片的位置。
这增加了复杂性,因为需要在PictureBox上绘制晶圆、有效区域,以及所有有效芯片的位置。
绘图步骤:
1. 确定绘图的区域,比如PictureBox的大小。
2. 将晶圆的物理尺寸映射到绘图区域的坐标。例如,晶圆的直径是D毫米,需要将其缩放到PictureBox的宽度和高度,保持比例。可能以PictureBox的中心为原点,进行坐标转换。
3. 绘制黑色圆圈表示晶圆。
4. 绘制红色圆圈表示有效区域。
5. 绘制所有有效的芯片位置,可能用矩形表示每个芯片的位置。
这需要处理坐标转换,将物理尺寸(毫米)转换为像素坐标。例如,假设晶圆的直径是D毫米,在绘图中需要将其缩放到PictureBox的宽度和高度,可能取较小的缩放比例,使得整个晶圆可见。
例如,PictureBox的宽度和高度为400像素,晶圆直径为300毫米,则每毫米对应400/300≈1.333像素。这样,在绘图时,需要将物理坐标乘以缩放比例,并调整原点位置到PictureBox的中心。
同时,绘制芯片的位置时,需要根据实际坐标计算在PictureBox中的位置,并用矩形填充。
现在,将这些思路整合到代码中。
首先,设计界面:
- 主窗体SplitContainer分成左右两部分,左侧是输入控件,右侧是PictureBox。
- 左侧Panel中,从上到下放置:
- ComboBox(晶圆规格)
- TextBox(芯片长度)
- TextBox(芯片宽度)
- TextBox(边沿抛弃尺寸)
- Button(计算)
- Label(显示结果)
然后,事件处理:
- 按钮点击事件处理函数中,读取输入参数,计算数量,并绘制图形。
计算部分:
1. 获取晶圆规格,转换为毫米直径,计算半径。
2. 有效半径 = 晶圆半径 - 边沿抛弃尺寸。
3. 如果有效半径 <=0,提示错误。
4. 获取芯片的长和宽,转换为double。
5. 计算每个芯片占据的宽度和高度。
6. 计算有效区域内的芯片数量。
7. 显示结果。
绘图部分:
1. 在PictureBox的Paint事件中,根据计算得到的参数绘制晶圆和有效区域。
2. 可能需要存储计算后的芯片位置列表,以便在Paint时绘制。
现在,详细考虑计算芯片数量的方法。
假设芯片从中心开始,向四周排列。例如,中心位置是(0,0),然后向右、左、上、下扩展。
对于每个可能的列(x方向),计算该列可以放置多少行(y方向)。
对于每个列的x坐标(以芯片宽度w为步长),从0开始,向右到x_max,然后同样向左到x_min。
对于每个x,计算该列的最大y范围,然后计算可以放置多少个芯片。
例如,x从0开始,每次增加w,直到x + w/2 > R_eff。然后处理x负方向。
对于每个x,计算允许的y范围:
该列的有效y范围的上限是 sqrt(R_eff² - (x + w/2)²),因为芯片的右侧边缘在x + w/2的位置,所以必须满足x + w/2 <= R_eff。或者,需要确保整个芯片在圆内,所以每个顶点到圆心的距离 <= R_eff。
或者,更准确地说,芯片的四个角必须满足x² + y² <= R_eff²。例如,对于芯片中心在(x_center, y_center),四个角的坐标是:
(x_center - w/2, y_center - h/2)
(x_center + w/2, y_center - h/2)
(x_center + w/2, y_center + h/2)
(x_center - w/2, y_center + h/2)
每个点必须满足 x² + y² <= R_eff².
因此,要确保这四个点都在圆内,即:
(x_center ± w/2)^2 + (y_center ± h/2)^2 <= R_eff².
这可能需要为每个可能的芯片中心坐标检查这四个条件,这可能会很繁琐。
另一种方法是,确定芯片的中心所在的区域,使得整个芯片位于圆内。这相当于芯片中心必须位于一个较小的圆内,其半径为 R_eff - r_chip,其中r_chip是芯片中心到角落的最大距离。
r_chip = sqrt((w/2)^2 + (h/2)^2)
所以,芯片中心必须满足 x_center² + y_center² <= (R_eff - r_chip)^2.
但这样计算的话,可能会过于保守,因为芯片可能在某个方向上更靠近圆心,从而允许更大的范围。例如,如果芯片的长边沿着径向,可能可以更靠近边缘。
不过,这种方法可能简化计算,但会低估数量。或者,可以考虑芯片必须完全位于圆内,所以芯片中心的区域是一个内切于有效圆的矩形,向内缩进w/2和h/2。这可能更简单,但也会低估。
例如,芯片中心的x范围是 [- (R_eff - w/2), R_eff - w/2],y同理。这样,在x和y方向上,芯片不会超出有效圆。这种方法将芯片的排列限制在一个矩形区域内,这显然比实际可能的区域小,但计算简单,数量会明显少于实际可能的最大值。
用户可能希望一个更准确的计算,所以可能需要采用更精确的方法,尽管计算量较大。
可能更好的方法是,按行列排列,然后对于每个芯片,检查四个角是否在圆内。
例如,按行列步进,从中心开始,向四个象限扩展,直到无法放置更多芯片。
这可能涉及双重循环,遍历x和y的可能值,但需要确定循环的范围。
例如,在x方向,从 -x_max到 +x_max,步长是w,其中x_max是R_eff - w/2。同样,y方向同理。
对于每个x_center和y_center,检查四个角是否在圆内。如果是,则计数加1。
这可能比较耗时,但可以接受,特别是当晶圆较大时,可能需要优化循环次数。
例如,x_center的范围是从 -floor((R_eff - w/2)/w) * w到 +floor((R_eff - w/2)/w) * w,每次增加w。同样处理y_center。
然后,对于每个x_center和 y_center,检查四个角的条件。
但这样的双重循环在芯片尺寸较小的情况下,可能会有很多次循环,例如,如果R_eff是150毫米,w和h是5毫米,那么x_center的范围是-29*5到 +29*5,共59个x位置,同样59个y位置,总共59*59=3481次循环。每次循环需要检查四个点,可能可以接受。
现在,编写代码的大纲:
计算芯片数量的方法:
public int CalculateDieCount(double waferRadius, double edgeExclusion, double dieWidth, double dieHeight)
{
double R = waferRadius - edgeExclusion;
if (R <= 0)
return 0;
double halfWidth = dieWidth / 2;
double halfHeight = dieHeight / 2;
// 计算x方向的最大中心坐标
double xMax = R - halfWidth;
int xSteps = (int)Math.Floor(xMax / dieWidth);
int xCount = xSteps * 2 + 1; // 包括正负和零
int totalCount = 0;
for (int i = -xSteps; i <= xSteps; i++)
{
double xCenter = i * dieWidth;
// 计算y方向的最大中心坐标
double yLimit = Math.Sqrt(R * R - (xCenter + halfWidth) * (xCenter + halfWidth)) - halfHeight;
if (yLimit < 0)
continue;
int ySteps = (int)Math.Floor(yLimit / dieHeight);
int yCount = ySteps * 2 + 1;
totalCount += yCount;
}
return totalCount;
}
但这个方法可能不准确,因为它只考虑了x方向的限制,并且假设y方向可以对称排列。或者,可能需要更精确的检查每个位置。
或者,另一种方法:
public int CalculateDieCount(double waferRadius, double edgeExclusion, double dieWidth, double dieHeight)
{
double R_eff = waferRadius - edgeExclusion;
if (R_eff <= 0)
return 0;
double halfW = dieWidth / 2;
double halfH = dieHeight / 2;
int count = 0;
// 计算x方向的起始和结束位置
double xStart = - (R_eff - halfW);
double xEnd = R_eff - halfW;
int xSteps = (int)Math.Floor((xEnd - xStart) / dieWidth) + 1;
for (double x = xStart; x <= xEnd; x += dieWidth)
{
// 对于每个x,计算最大的y范围
double xMax = x + halfW;
double yLimit = Math.Sqrt(R_eff * R_eff - xMax * xMax) - halfH;
if (yLimit < 0)
continue;
int ySteps = (int)Math.Floor(yLimit / dieHeight);
int yCount = ySteps * 2 + 1; // 上下对称
count += yCount;
}
return count;
}
这可能更接近正确的结果,但需要验证是否正确。
或者,使用双重循环,遍历每个可能的中心点,然后检查四个角是否在有效圆内。
例如:
public int CalculateDieCount(double waferRadius, double edgeExclusion, double dieWidth, double dieHeight)
{
double R_eff = waferRadius - edgeExclusion;
if (R_eff <= 0)
return 0;
double halfW = dieWidth / 2;
double halfH = dieHeight / 2;
int count = 0;
// 计算x中心的范围
double xMaxCenter = R_eff - halfW;
int xSteps = (int)Math.Floor(xMaxCenter / dieWidth);
// 遍历所有可能的x中心,包括正负
for (int xi = -xSteps; xi <= xSteps; xi++)
{
double xCenter = xi * dieWidth;
// 计算y中心的最大值
double xEdge = xCenter + halfW;
double yMaxForX = Math.Sqrt(R_eff * R_eff - xEdge * xEdge) - halfH;
if (yMaxForX < 0)
continue;
int ySteps = (int)Math.Floor(yMaxForX / dieHeight);
int yCount = 2 * ySteps + 1; // 上下各ySteps,加上中心
count += yCount;
}
return count;
}
这个方法假设x方向以dieWidth为步长,从中心向左右扩展。对于每个xCenter,计算对应的最大y,然后确定可以放置多少行。
这可能是一个近似值,但可能接近正确结果。不过,需要测试。
例如,假设晶圆半径是150mm(300mm直径),边沿抛弃为0,芯片尺寸是10mm x 10mm。
则有效半径R_eff =150mm.
xMaxCenter=150 -5=145mm.
xSteps=145/10=14.5→14.
所以xi从-14到14,共29个xCenter。
对于每个xCenter=xi*10,xEdge=xi*10 +5.
例如,xi=14,xCenter=140mm,xEdge=145mm.
yMaxForX= sqrt(150² -145²) -5.
计算150²=22500,145²=21025,差为22500-21025=1475. sqrt(1475)≈38.40mm. 38.40-5=33.40mm.
ySteps=33.40/10=3.34→3.
yCount=3*2+1=7.
所以,对于xCenter=140mm,可以放置7个y方向(yCenter从-30, -20, -10, 0, 10, 20, 30)。
每个yCenter对应的芯片是否完全在圆内?
例如,yCenter=30,芯片的四个角的坐标:
x=140±5 →145和135,y=30±5 →25和35.
检查这些点是否在圆内:
145² +25²=21025+625=21650 <= 22500 →是。
145² +35²=21025+1225=22250 <=22500 →是。
但如果是yCenter=35,那么y=35±5→30和40.
145²+40²=21025+1600=22625>22500→超过,所以不能。
所以,当ySteps=3时,最大的yCenter是30mm,对应的yEdge=35mm,这会超过吗?
比如,当yCenter=30,yEdge=35:
xEdge=145,yEdge=35 →145² +35²=21025+1225=22250 <=22500→是,所以有效。
而yCenter=40的话,yEdge=45,超过。
因此,上面的计算是正确的,对于xi=14,ySteps=3,对应yCenter=0, ±10, ±20, ±30,共7个位置,每个位置对应的芯片都完全在圆内。
所以,这个方法计算的结果是正确的。
因此,这个算法是正确的。
接下来,将这个算法转换为C#代码。
然后,在按钮点击事件中,调用这个方法,获取结果,并显示。
接下来,绘图部分。
需要在PictureBox中绘制晶圆和有效区域,可能还需要绘制芯片的位置。
首先,确定如何将物理坐标转换为PictureBox的像素坐标。
假设PictureBox的宽度和高度为400像素。需要将晶圆的直径映射到绘图区域,保持纵横比,可能取较小的缩放比例,使得整个晶圆可见。
例如,晶圆的物理直径是D毫米(例如300mm),在绘图中,希望它在PictureBox中居中显示,所以缩放比例 scale = Math.Min(pictureBox.Width / D, pictureBox.Height / D).
但可能要考虑边沿抛弃后的有效区域,所以需要确保整个晶圆和有效区域都可见。
或者,始终以晶圆的直径为基准进行缩放,不管有效区域的大小。
例如:
double waferDiameter = waferSizeInch * 25.4; // 转换为毫米
double scale = Math.Min(pictureBox.Width / waferDiameter, pictureBox.Height / waferDiameter);
然后,在绘制时,所有坐标都乘以scale,并将原点放在pictureBox的中心。
例如,在pictureBox的Paint事件中:
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
int centerX = pictureBox.Width / 2;
int centerY = pictureBox.Height / 2;
// 绘制晶圆(黑色圆圈)
double waferRadius = waferDiameter / 2;
float waferRadiusPixels = (float)(waferRadius * scale);
g.DrawEllipse(Pens.Black, centerX - waferRadiusPixels, centerY - waferRadiusPixels, 2 * waferRadiusPixels, 2 * waferRadiusPixels);
// 绘制有效区域(红色圆圈)
double effectiveRadius = waferRadius - edgeExclusion;
if (effectiveRadius > 0)
{
float effectiveRadiusPixels = (float)(effectiveRadius * scale);
g.DrawEllipse(Pens.Red, centerX - effectiveRadiusPixels, centerY - effectiveRadiusPixels, 2 * effectiveRadiusPixels, 2 * effectiveRadiusPixels);
}
// 绘制芯片位置
foreach (var die in diePositions)
{
// die 的物理坐标是 (xCenter, yCenter)
float x = (float)(die.X * scale) + centerX;
float y = centerY - (float)(die.Y * scale); // 因为pictureBox的y轴向下,需要反转
float width = (float)(dieWidth * scale);
float height = (float)(dieHeight * scale);
g.DrawRectangle(Pens.Blue, x - width/2, y - height/2, width, height);
}
但需要注意,diePositions需要存储每个有效芯片的中心坐标(x,y),在计算时收集这些坐标。
因此,在计算芯片数量时,不仅要计数,还要记录每个有效芯片的位置,以便在绘图时使用。
这需要在CalculateDieCount方法中返回一个List<PointF>,包含所有有效芯片的中心坐标。
修改计算方法:
public (int count, List<PointF> positions) CalculateDieCountAndPositions(double waferRadius, double edgeExclusion, double dieWidth, double dieHeight)
{
double R_eff = waferRadius - edgeExclusion;
List<PointF> positions = new List<PointF>();
if (R_eff <= 0)
return (0, positions);
double halfW = dieWidth / 2;
double halfH = dieHeight / 2;
int count = 0;
double xMaxCenter = R_eff - halfW;
int xSteps = (int)Math.Floor(xMaxCenter / dieWidth);
for (int xi = -xSteps; xi <= xSteps; xi++)
{
double xCenter = xi * dieWidth;
double xEdge = xCenter + halfW;
double yMaxForX = Math.Sqrt(R_eff * R_eff - xEdge * xEdge) - halfH;
if (yMaxForX < 0)
continue;
int ySteps = (int)Math.Floor(yMaxForX / dieHeight);
for (int yi = -ySteps; yi <= ySteps; yi++)
{
double yCenter = yi * dieHeight;
// 检查四个角是否都在有效圆内
bool isValid = true;
// 四个角的坐标
double[] dx = { -halfW, halfW, halfW, -halfW };
double[] dy = { -halfH, -halfH, halfH, halfH };
foreach (var i in Enumerable.Range(0,4))
{
double x = xCenter + dx[i];
double y = yCenter + dy[i];
if (x * x + y * y > R_eff * R_eff)
{
isValid = false;
break;
}
}
if (isValid)
{
count++;
positions.Add(new PointF((float)xCenter, (float)yCenter));
}
}
}
return (count, positions);
}
这样,在计算数量的同时,收集所有有效的芯片中心坐标。但这样会增加计算时间,因为需要检查每个位置的四个角。不过,对于用户来说,可能可以接受。
或者,可以使用之前的方法,但可能漏掉一些情况。例如,在之前的计算中,假设ySteps是基于xEdge的位置,但可能某些yCenter的位置的芯片的四个角可能超出有效圆。因此,必须进行检查。
例如,假设xCenter=140mm,yCenter=30mm,芯片的右上角是145,35。根据前面的计算,这在有效圆内。但其他角落呢?例如,左下角是135,25。同样在有效圆内。
所以,在之前的方法中,可能不需要检查所有四个角,因为xEdge和yEdge的计算已经确保了右上角在圆内,而其他角落可能自动满足。但不确定,所以需要验证。
例如,假设芯片的左上角是xCenter - halfW, yCenter - halfH。当xCenter和 yCenter 是基于xSteps和 ySteps计算的,是否确保所有四个角都在圆内?
可能不一定,因为 ySteps的计算是基于xEdge= xCenter + halfW,然后计算yMaxForX = sqrt(R² -xEdge²) - halfH。这确保右上角的y坐标为 yCenter + halfH <= sqrt(R² -xEdge²)。即,yCenter + halfH <= sqrt(R² -xEdge²). 因此,右上角的坐标(xEdge, yCenter + halfH)满足xEdge² + (yCenter + halfH)^2 <= R² +0(可能等于)。
但其他角呢,如左下角xCenter - halfW= xCenter - halfW =xi* dieWidth - dieWidth/2. 例如,当xi=14,dieWidth=10mm,xCenter=140mm,halfW=5mm,所以xCenter - halfW=135mm。此时,左下角的坐标是135, yCenter - halfH.
需要确保135² + (yCenter - halfH)^2 <= R².
但 R_eff=150mm,所以135²=18225,只要(yCenter - halfH)^2 <= 150² -135²=22500-18225=4275 → yCenter - halfH <= sqrt(4275)≈65.38mm.
假设dieHeight=10mm,halfH=5mm,yCenter=30 →30-5=25 →25²=625 ≤4275 →是。
所以,在这种情况下,左下角的坐标是满足条件的。
因此,可能之前的算法已经确保所有四个角都在有效圆内,所以不需要额外的检查。但需要数学证明。
假设xCenter和 yCenter的计算满足:
xCenter + halfW ≤ R_eff
yCenter + halfH ≤ sqrt(R_eff² - (xCenter + halfW)^2 )
则:
(xCenter + halfW)^2 + (yCenter + halfH)^2 ≤ R_eff² → 右上角在圆内。
其他角落:
xCenter - halfW ≥ -R_eff + (xCenter + halfW) →因为 xCenter + halfW ≤ R_eff →xCenter ≥ -R_eff + (xCenter + halfW) →这不一定成立。比如,xCenter可能是负数。
例如,xCenter =-10,halfW=5 →xCenter - halfW =-15. 如果 R_eff=20,则-15的绝对值是15 <20,所以有效。但需要验证(x=-15, y= ...)是否在圆内。
假设xCenter=-10,则xCenter + halfW =-5. xEdge=-5,所以 yMaxForX= sqrt(R² - (-5)^2) - halfH.
假设 R=20,则 sqrt(400 -25)=sqrt(375)=~19.36 → yMaxForX=19.36 -5=14.36.
所以 ySteps=14.36/10=1.436 →1 → yCenter=0, ±10.
对于yCenter=10,则 yCenter + halfH=15 →检查右上角坐标(-5,15) →(-5)^2 +15²=25+225=250 ≤400 →是。
左下角坐标x=-10-5=-15,y=10-5=5 → (-15)^2 +5^2=225+25=250 ≤400 →是。
其他角落:
右下角:x=-10+5=-5, y=10+5=15 → (-5)^2+15^2=250 ≤400 →是。
左上角:x=-15, y=5 →250 ≤400 →是。
所以,所有四个角落都满足条件。
这可能证明,当使用xEdge=xCenter + halfW,并且yMaxForX= sqrt(R² -xEdge²) - halfH,并确保yCenter + halfH ≤ sqrt(R² -xEdge²),则所有四个角落都在圆内。
因此,之前的计算方法正确,不需要检查四个角落,可以安全地计数。
因此,可以简化计算方法,提高效率,避免检查每个角落。
因此,回到之前的计算方法,并收集芯片的位置:
public (int count, List<PointF> positions) CalculateDieCountAndPositions(double waferRadius, double edgeExclusion, double dieWidth, double dieHeight)
{
double R_eff = waferRadius - edgeExclusion;
List<PointF> positions = new List<PointF>();
if (R_eff <= 0)
return (0, positions);
double halfW = dieWidth / 2;
double halfH = dieHeight / 2;
int count = 0;
double xMaxCenter = R_eff - halfW;
int xSteps = (int)Math.Floor(xMaxCenter / dieWidth);
for (int xi = -xSteps; xi <= xSteps; xi++)
{
double xCenter = xi * dieWidth;
double xEdge = xCenter + halfW; // 右上角的x坐标
double yMaxForXEdge = Math.Sqrt(R_eff * R_eff - xEdge * xEdge);
double yMaxCenter = yMaxForXEdge - halfH;
if (yMaxCenter < 0)
continue;
int ySteps = (int)Math.Floor(yMaxCenter / dieHeight);
for (int yi = -ySteps; yi <= ySteps; yi++)
{
double yCenter = yi * dieHeight;
// 由于xEdge和yMaxCenter的计算,确保右上角在圆内,其他角落也自动在圆内
positions.Add(new PointF((float)xCenter, (float)yCenter));
count++;
}
}
return (count, positions);
}
这样,可以高效地计算数量并收集位置。
现在,将这些整合到WinForm应用中。
界面设计:
在Visual Studio中,创建一个WinForm项目,目标框架.NET8.0。
在Form1的设计器中,添加控件:
- 左侧用Panel,Dock=Left,Width=200。
- 右侧用PictureBox,Dock=Fill。
左侧Panel中添加控件:
- ComboBox,Name=comboWaferSize,Items添加4、6、8、10、12英寸。
- TextBox,Name=txtDieLength,提示文字“长(mm)”
- TextBox,Name=txtDieWidth,提示文字“宽(mm)”
- TextBox,Name=txtEdgeExclusion,提示文字“边沿抛弃尺寸(mm)”
- Button,Name=btnCalculate,Text=“计算”
- Label,Name=lblResult,Text=“结果:0”
可能需要使用TableLayoutPanel来排列这些控件,使其整齐。
然后,在Form1的代码中,处理按钮点击事件,调用计算方法,并更新结果和绘图。
代码大致如下:
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System;
namespace WaferCalculator
{
public partial class MainForm : Form
{
private List<PointF> diePositions = new List<PointF>();
private double waferDiameter = 0;
private double edgeExclusion = 0;
private double dieWidth = 0;
private double dieHeight = 0;
private double effectiveRadius = 0;
public MainForm()
{
InitializeComponent();
comboWaferSize.Items.AddRange(new object[] { 4, 6, 8, 10, 12 });
comboWaferSize.SelectedIndex = 0;
pictureBox.Paint += PictureBox_Paint;
}
private void btnCalculate_Click(object sender, EventArgs e)
{
if (!double.TryParse(comboWaferSize.SelectedItem?.ToString(), out double waferSizeInch))
{
MessageBox.Show("请选择晶圆规格。");
return;
}
waferDiameter = waferSizeInch * 25.4; // 英寸转毫米
double waferRadius = waferDiameter / 2;
if (!double.TryParse(txtDieLength.Text, out dieHeight) || dieHeight <= 0 ||
!double.TryParse(txtDieWidth.Text, out dieWidth) || dieWidth <= 0)
{
MessageBox.Show("请输入有效的芯片尺寸。");
return;
}
if (!double.TryParse(txtEdgeExclusion.Text, out edgeExclusion) || edgeExclusion < 0)
{
MessageBox.Show("请输入有效的边沿抛弃尺寸。");
return;
}
effectiveRadius = waferRadius - edgeExclusion;
if (effectiveRadius <= 0)
{
lblResult.Text = "结果:0";
diePositions.Clear();
pictureBox.Invalidate();
MessageBox.Show("边沿抛弃尺寸超过晶圆半径。");
return;
}
var result = CalculateDieCountAndPositions(waferRadius, edgeExclusion, dieWidth, dieHeight);
lblResult.Text = $"结果:{result.count}";
diePositions = result.positions;
pictureBox.Invalidate();
}
private (int count, List<PointF> positions) CalculateDieCountAndPositions(double waferRadius, double edgeExclusion, double dieWidth, double dieHeight)
{
// 同上文的实现
}
private void PictureBox_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.Clear(Color.White);
if (waferDiameter <= 0)
return;
int centerX = pictureBox.Width / 2;
int centerY = pictureBox.Height / 2;
// 计算缩放比例
float scale = Math.Min(pictureBox.Width / (float)waferDiameter, pictureBox.Height / (float)waferDiameter);
// 绘制晶圆(黑色圆圈)
float waferRadiusPixels = (float)(waferDiameter / 2 * scale);
g.DrawEllipse(Pens.Black, centerX - waferRadiusPixels, centerY - waferRadiusPixels, 2 * waferRadiusPixels, 2 * waferRadiusPixels);
// 绘制有效区域(红色圆圈)
if (effectiveRadius > 0)
{
float effectiveRadiusPixels = (float)(effectiveRadius * scale);
g.DrawEllipse(Pens.Red, centerX - effectiveRadiusPixels, centerY - effectiveRadiusPixels, 2 * effectiveRadiusPixels, 2 * effectiveRadiusPixels);
}
// 绘制芯片位置
float halfW = (float)(dieWidth * scale) / 2;
float halfH = (float)(dieHeight * scale) / 2;
foreach (var pos in diePositions)
{
float x = centerX + (float)(pos.X * scale);
float y = centerY - (float)(pos.Y * scale); // 反转Y轴方向
g.DrawRectangle(Pens.Blue, x - halfW, y - halfH, (float)(dieWidth * scale), (float)(dieHeight * scale));
}
}
}
}
需要注意的几点:
1. PictureBox的Paint事件处理函数中,将物理坐标转换为像素坐标。由于PictureBox的坐标系是左上角为原点,Y轴向下,所以在转换时,需要将物理坐标的Y值取反,即centerY - (pos.Y * scale)。
2. 缩放比例的计算基于晶圆的直径,确保整个晶圆可见。
3. 绘制芯片时,每个芯片的位置是其中心点,转换为像素坐标后,绘制矩形。
测试案例:
例如,选择12英寸晶圆(304.8毫米直径,半径152.4毫米),边沿抛弃3毫米,芯片尺寸10x10毫米。
有效半径:152.4-3=149.4毫米。
计算能放多少个芯片?
根据之前的算法,xSteps= floor((149.4 -5)/10) = floor(144.4/10)=14 →xCenter从-14*10到+14*10,即-140到+140毫米。
对于xCenter=140,xEdge=145毫米,超过有效半径149.4吗?145<149.4,所以有效。
yMaxForXEdge= sqrt(149.4² -145²) →149.4²=22320.36,145²=21025 →差为22320.36-21025=1295.36 →sqrt(1295.36)≈36 →yMaxCenter=36 -5=31 →ySteps=3 →yCenter从-30到+30,步长10 →7个位置。
这样,每个xCenter=140的列有7个芯片。
对于xCenter=0,xEdge=5,yMaxForXEdge= sqrt(149.4² -5²) ≈149.4 →yMaxCenter=149.4-5=144.4 →ySteps=14 →yCount=29.
这样,每个x列的数量可能不同,总和即为总数。
绘制时,芯片的位置应该形成对称的图案,中心密集,边缘稀疏。
可能还需要处理输入验证,例如,确保输入的边沿抛弃不超过晶圆半径。
此外,当用户输入无效数据时,给出提示。