반응형
나는 대학원에서 회로 설계를 하고 있다.
회로 설계를 하다보면 다양한 이유로 deep-n well (DNW) 소자를 사용해야 하는 경우들이 존재한다.
아날로그 회로에서 substrate noise를 걸러주기 위해서 사용할 수도 있고, nmos body에 전압을 인가하기 위해서 사용할 수도 있다.
PDK에 따라 다르겠지만, 내가 사용하는 TSMC PDK에서는 LVS 시 특정 옵션을 켜주면 기생 다이오드들을 추출해서 LVS를 수행할 수 있게 해준다.
이 때 문제가 기생 다이오드의 총 면적만 알려주고, 다이오드의 L/W를 어떻게 하라고 전혀 알려주지 않는다.
매 번 LVS 돌릴 때 마다 찾을라니까 너무 귀찮고 시간이 오래걸리기 때문에 결국 이를 해결하기 위해 python으로 프로그램을 결국 만들게 되었다.
요즘은 ChatGPT가 잘 되어 있어서 내 목적을 설명하면 코드를 잘 짜주는데 이걸 잘 이용하면 귀찮은 코드 작업이 자동화 되어 아주 좋은 세상이다.
코드의 전체적인 흐름은 다음과 같다.
Layout의 DRC grid 및 기생 다이오드의 면적 입력
→ 기생 다이오드 면적을 unit grid area로 나누기
→ 나눈 값을 소인수 분해 후, 해당 소인수들로 구성할 수 있는 가장 큰 두 개의 수 출력
Fig. 1은 실제 코드를 실행시킨 결과이다.
단위는 기본적으로 um 단위이고, L/W = {결과값}으로 출력되는 두 가지 쌍을 기생 다이오드의 L와 W로 각각 입력하면 된다.
전체 동작 코드는 아래에 첨부하였다. Python 3이상이면 아마 큰 문제 없이 구동될 것이다.
import math
import random
from typing import List
# 에라토스테네스의 체를 사용한 소수 리스트 생성
def sieve_of_eratosthenes(limit: int) -> List[int]:
sieve = [True] * (limit + 1)
sieve[0] = sieve[1] = False
for start in range(2, int(math.sqrt(limit)) + 1):
if sieve[start]:
for i in range(start*start, limit + 1, start):
sieve[i] = False
return [num for num, is_prime in enumerate(sieve) if is_prime]
# 유클리드 호제법을 사용한 최대공약수 계산
def gcd(a: int, b: int) -> int:
while b:
a, b = b, a % b
return a
# Pollard's Rho 알고리즘
def pollards_rho(n: int) -> int:
if n % 2 == 0:
return 2
x = random.randint(2, n - 1)
y = x
c = random.randint(1, n - 1)
d = 1
while d == 1:
x = (pow(x, 2, n) + c) % n
y = (pow(y, 2, n) + c) % n
y = (pow(y, 2, n) + c) % n
d = gcd(abs(x - y), n)
if d == n:
return pollards_rho(n)
return d
# 소인수 분해 함수
def prime_factors(n: int) -> List[int]:
factors = []
# 에라토스테네스의 체로 작은 소수들을 이용한 분해
limit = int(n**0.5) + 1
primes = sieve_of_eratosthenes(limit)
for prime in primes:
while n % prime == 0:
factors.append(prime)
n //= prime
# Pollard's Rho 알고리즘을 이용한 나머지 소인수 분해
while n > 1:
if is_prime(n):
factors.append(n)
break
factor = pollards_rho(n)
while n % factor == 0:
factors.append(factor)
n //= factor
return factors
# 소수 여부 판별 함수 (빠른 소수 판별)
def is_prime(n: int) -> bool:
if n <= 1:
return False
if n <= 3:
return True
if n % 2 == 0 or n % 3 == 0:
return False
i = 5
while i * i <= n:
if n % i == 0 or n % (i + 2) == 0:
return False
i += 6
return True
def find_two_largest_numbers(factors: list) -> list:
# 소인수들을 내림차순으로 정렬
factors.sort(reverse=True)
# 두 그룹으로 나누기 위한 변수
group1 = []
group2 = []
# 소인수들을 번갈아 가면서 두 그룹에 배분
for factor in factors:
if math.prod(group1) < math.prod(group2):
group1.append(factor)
else:
group2.append(factor)
# 각각의 그룹에서 곱을 계산
num1 = math.prod(group1)
num2 = math.prod(group2)
return [num1, num2]
if __name__ == "__main__" :
grid_size = float(input("Grid Size: "))
unit_area = grid_size*grid_size
print(f"Unit Area: {unit_area}")
area = float(input("Target Area: "))
number_of_unit_area = int(round(area / unit_area))
#print(f"Number of Unit areas: {number_of_unit_area}")
factors = prime_factors(number_of_unit_area)
numbers = find_two_largest_numbers(factors)
#print(numbers)
for number in numbers :
print(f"L/W = {number * grid_size} um")
반응형
댓글