├── tests ├── via.pbm ├── small.pbm ├── tiny.pbm └── 2_thick_bar7.pbm ├── examples ├── tiny-mask-90nm.jpg ├── tiny-aerial-90nm.jpg ├── tiny-contours-90nm.jpg ├── tiny-opc-mask-90nm.jpg ├── tiny-opc-aerial-90nm.jpg └── tiny-opc-contours-90nm.jpg ├── helper.h ├── matlab ├── make_jinc.m ├── matt.m ├── small.m └── tiny.m ├── timer.h ├── fltimage.h ├── README.md ├── helper.C ├── LICENSE ├── pbmimage.h ├── Makefile ├── fltimage.C ├── pbmimage.C └── lithosim.C /tests/via.pbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VLSIDA/lithosim/HEAD/tests/via.pbm -------------------------------------------------------------------------------- /tests/small.pbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VLSIDA/lithosim/HEAD/tests/small.pbm -------------------------------------------------------------------------------- /tests/tiny.pbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VLSIDA/lithosim/HEAD/tests/tiny.pbm -------------------------------------------------------------------------------- /tests/2_thick_bar7.pbm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VLSIDA/lithosim/HEAD/tests/2_thick_bar7.pbm -------------------------------------------------------------------------------- /examples/tiny-mask-90nm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VLSIDA/lithosim/HEAD/examples/tiny-mask-90nm.jpg -------------------------------------------------------------------------------- /examples/tiny-aerial-90nm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VLSIDA/lithosim/HEAD/examples/tiny-aerial-90nm.jpg -------------------------------------------------------------------------------- /examples/tiny-contours-90nm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VLSIDA/lithosim/HEAD/examples/tiny-contours-90nm.jpg -------------------------------------------------------------------------------- /examples/tiny-opc-mask-90nm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VLSIDA/lithosim/HEAD/examples/tiny-opc-mask-90nm.jpg -------------------------------------------------------------------------------- /examples/tiny-opc-aerial-90nm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VLSIDA/lithosim/HEAD/examples/tiny-opc-aerial-90nm.jpg -------------------------------------------------------------------------------- /examples/tiny-opc-contours-90nm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VLSIDA/lithosim/HEAD/examples/tiny-opc-contours-90nm.jpg -------------------------------------------------------------------------------- /helper.h: -------------------------------------------------------------------------------- 1 | #include 2 | #ifndef _HELPER_H_ 3 | #define _HELPER_H_ 4 | float bessj1(float x); 5 | int fpeek(FILE *stream); 6 | void eatspace(FILE *in); 7 | #endif 8 | -------------------------------------------------------------------------------- /matlab/make_jinc.m: -------------------------------------------------------------------------------- 1 | %clear all; 2 | function [H] = make_jinc(P, scale) 3 | 4 | x = [-floor(P/2) : 1 : floor(P/2)]; 5 | y = x; 6 | 7 | [xx,yy]=meshgrid(x,y); 8 | 9 | r = sqrt(xx.^2+yy.^2); 10 | 11 | z = ones(size(r)); 12 | k = find(r); 13 | z(k) = besselj(1, 2*pi*r(k)*scale) ./ ( 2*pi*r(k)*scale ); 14 | z(ceil(P/2), ceil(P/2)) = 0.5; 15 | 16 | H = z / sum(sum(z)); 17 | -------------------------------------------------------------------------------- /timer.h: -------------------------------------------------------------------------------- 1 | #include 2 | /*######################################################## 3 | Easy to use timer. 4 | ########################################################*/ 5 | 6 | class my_timer_t { 7 | public: 8 | my_timer_t() { stime = clock(); } 9 | void print_time(char * str) { 10 | etime=clock(); 11 | printf(str); 12 | printf(" TIME: %7.3f sec\n",((double) (etime - stime)) / CLOCKS_PER_SEC); 13 | stime = clock(); /* as a convenience. */ 14 | } 15 | private: 16 | clock_t stime,etime; 17 | }; 18 | 19 | -------------------------------------------------------------------------------- /matlab/matt.m: -------------------------------------------------------------------------------- 1 | Resolution = 10; %nm per pixel in the input image 2 | NA = 0.95; %numerical aperture 3 | lambda = 193; %wavelength 4 | threshold = 0.2; %Resist Threshold 5 | Filter_Size = 100; %Jinc filter size 6 | 7 | I = double(imread('../tests/2_thick_bar7.pbm')); 8 | 9 | H = make_jinc( Filter_Size, (NA/lambda)*Resolution ); 10 | 11 | Field = imfilter ( I, H, 'replicate', 'same' ) ; 12 | Aerial = abs(Field).^2; 13 | 14 | Contour = Aerial > threshold; 15 | 16 | f1=figure; imagesc(I); axis image; colormap gray; title('Original'); 17 | f2=figure; imagesc(Aerial); axis image; title('Aerial Image'); 18 | f3=figure; imagesc(Contour); axis image; colormap gray; title('Contours'); 19 | imwrite(I,'original.tiff','Compression','none') 20 | imwrite(Aerial,'aerial.tiff','Compression','none') 21 | imwrite(Contour,'contours.tiff','Compression','none') 22 | -------------------------------------------------------------------------------- /fltimage.h: -------------------------------------------------------------------------------- 1 | #ifndef FLTIMAGE_H_ 2 | #define FLTIMAGE_H_ 3 | 4 | #include "pbmimage.h" 5 | 6 | class pbm_image_t; 7 | class flt_image_t; 8 | 9 | class flt_image_t { 10 | public: 11 | flt_image_t() {width=height=0;}; 12 | flt_image_t(char *filename); 13 | flt_image_t(int w, int h); 14 | ~flt_image_t(); 15 | 16 | void save(char *filename,float threshold); 17 | void save_pgm(char *filename); 18 | void save_ascii(char *filename,float threshold); 19 | void make_jinc(int P, float scale); 20 | void convolve(pbm_image_t &src,flt_image_t &k); 21 | void pack(float *barray,int n,unsigned char *str,float threshold); 22 | float *unpack(unsigned char *str,int n); 23 | 24 | float get(int x, int y) const; 25 | int get_height() const { return(height); }; 26 | int get_width() const { return(width); }; 27 | 28 | private: 29 | int width,height; 30 | float ** image; 31 | }; 32 | #endif 33 | -------------------------------------------------------------------------------- /matlab/small.m: -------------------------------------------------------------------------------- 1 | Resolution = 15; %nm per pixel in the input image 2 | NA = 0.95; %numerical aperture 3 | lambda = 193; %wavelength 4 | threshold = 0.2; %Resist Threshold 5 | Filter_Size = 100; %Jinc filter size 6 | 7 | I = double(imread('small.bmp')); 8 | 9 | H = make_jinc( Filter_Size, (NA/lambda)*Resolution ); 10 | 11 | Field = imfilter ( I, H, 'replicate', 'same' ) ; 12 | Aerial = abs(Field).^2; 13 | 14 | Contour = Aerial > threshold; 15 | 16 | f1=figure; imagesc(I); axis image; colormap gray; title('Original'); 17 | f2=figure; imagesc(Aerial); axis image; title('Aerial Image'); 18 | f3=figure; imagesc(Contour); axis image; colormap gray; title('Contours'); 19 | %saveas(f1,'small-original.png'); 20 | %saveas(f2,'small-aerial.png'); 21 | %saveas(f3,'small-contours.png'); 22 | imwrite(I,'small-original.tiff','Compression','none') 23 | imwrite(Aerial,'small-aerial.tiff','Compression','none') 24 | imwrite(Contour,'small-contours.tiff','Compression','none') 25 | 26 | -------------------------------------------------------------------------------- /matlab/tiny.m: -------------------------------------------------------------------------------- 1 | Resolution = 15; %nm per pixel in the input image 2 | NA = 0.95; %numerical aperture 3 | lambda = 193; %wavelength 4 | threshold = 0.2; %Resist Threshold 5 | Filter_Size = 310; %Jinc filter size 6 | 7 | I = double(imread('../tests/tiny.pbm')); 8 | 9 | H = make_jinc( Filter_Size, (NA/lambda)*Resolution ); 10 | 11 | Field = imfilter ( I, H, 'replicate', 'same' ) ; 12 | Aerial = abs(Field).^2; 13 | 14 | Contour = Aerial > threshold; 15 | 16 | f1=figure; imagesc(I); axis image; colormap gray; title('Original'); 17 | f2=figure; imagesc(Aerial); axis image; title('Aerial Image'); 18 | f3=figure; imagesc(Contour); axis image; colormap gray; title('Contours'); 19 | %saveas(f1,'tiny-original.png'); 20 | %saveas(f2,'tiny-aerial.png'); 21 | %saveas(f3,'tiny-contours.png'); 22 | imwrite(I,'tiny-original.tiff','Compression','none') 23 | imwrite(Aerial,'tiny-aerial.tiff','Compression','none') 24 | imwrite(Contour,'tiny-contours.tiff','Compression','none') 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lithosim 2 | 3 | This is a very basic lithography simulation and pixel-based OPC tool. 4 | 5 | ## Simulation 6 | 7 | The simulation uses an analytical model similar to [A. Poonawala, 8 | P. Milanfar, “A Pixel-Based Regularization Approach to Inverse 9 | Lithography”,Microelectronic Engineering, 84 (2007) 10 | pp. 2837–2852](https://users.soe.ucsc.edu/~milanfar/publications/journal/Microelectronic_Final.pdf). 11 | 12 | ## OPC 13 | 14 | The OPC just does a simulated annealing algorithm to minimize error 15 | between the target mask and the simulated mask. It does not convert 16 | the pixel-based mask into a manufacturable mask. 17 | 18 | ## Example Results 19 | 20 | ### Simulation 21 | Mask, Aerial Image, Contours 22 | 23 | Mask (target)Aerial ImageContours 24 | 25 | ### OPC 26 | OPC'ed Mask (high cost!), OPC Aerial Image, OPC Contours 27 | 28 | OPC MaskOPC Aerial ImageOPC Contours 29 | -------------------------------------------------------------------------------- /helper.C: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | #include "helper.h" 6 | 7 | /*######################################################## 8 | Auxiliary File I/O Functions 9 | ########################################################*/ 10 | float 11 | bessj1(float x) 12 | { 13 | float ax,z; 14 | double xx,y,ans,ans1,ans2; 15 | 16 | if ((ax=fabs(x)) < 8.0) { 17 | y=x*x; 18 | ans1=x*(72362614232.0+y*(-7895059235.0+y*(242396853.1 19 | +y*(-2972611.439+y*(15704.48260+y*(-30.16036606)))))); 20 | ans2=144725228442.0+y*(2300535178.0+y*(18583304.74 21 | +y*(99447.43394+y*(376.9991397+y*1.0)))); 22 | ans=ans1/ans2; 23 | } else { 24 | z=8.0/ax; 25 | y=z*z; 26 | xx=ax-2.356194491; 27 | ans1=1.0+y*(0.183105e-2+y*(-0.3516396496e-4 28 | +y*(0.2457520174e-5+y*(-0.240337019e-6)))); 29 | ans2=0.04687499995+y*(-0.2002690873e-3 30 | +y*(0.8449199096e-5+y*(-0.88228987e-6 31 | +y*0.105787412e-6))); 32 | ans=sqrt(0.636619772/ax)*(cos(xx)*ans1-z*sin(xx)*ans2); 33 | if (x < 0.0) ans = -ans; 34 | } 35 | return ans; 36 | } 37 | 38 | int fpeek(FILE *stream) 39 | { 40 | int c; 41 | c = fgetc(stream); 42 | ungetc(c,stream); 43 | return c; 44 | } 45 | 46 | void 47 | eatspace(FILE *in) { 48 | char c; 49 | while (isspace(fpeek(in))) { 50 | c = fgetc(in); 51 | /* printf("skipping %d\n",c);*/ 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, VLSI Design & Automation Group 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /pbmimage.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PBMIMAGE_H_ 3 | #define PBMIMAGE_H_ 4 | 5 | #include 6 | 7 | #include "fltimage.h" 8 | class flt_image_t; 9 | class pbm_image_t; 10 | 11 | class pbm_image_t { 12 | public: 13 | pbm_image_t(int w, int h); 14 | pbm_image_t(char *filename); 15 | ~pbm_image_t(); 16 | 17 | void flip(int x, int y); 18 | void flip(int x, int y, int wx, int wy); 19 | void set(int x, int y, int val); 20 | 21 | int diff(const pbm_image_t &p); 22 | 23 | std::set > contour_bits(); 24 | 25 | void pick_adjacent_bit(int *x, int *y) const; 26 | bool is_iso_region(int x, int y, int wx, int wy) const; 27 | void pick_iso_region(int *x, int *y, int xw, int yw) const; 28 | void pick_iso_region(int *x, int *y, int wx, int wy,const pbm_image_t &diff, int range) const; 29 | void pick_on_bit(int *x, int *y) const; 30 | 31 | void invert(); 32 | void single_convolve(const pbm_image_t &src,const flt_image_t &k, int x, int y); 33 | void incremental_convolve(const pbm_image_t &src,const flt_image_t &k, int x, int y, int wx, int wy); 34 | void convolve(const pbm_image_t &src,const flt_image_t &k); 35 | void save(char *filename); 36 | void diff(const pbm_image_t &p1,const pbm_image_t &p2); 37 | 38 | int count() const; 39 | int complexity() const; 40 | int singleton(int x, int y) const; 41 | int neighbors(int x, int y) const; 42 | int get(int x, int y) const; 43 | int get_extended(int x, int y) const; 44 | int get_height() const { return(height); }; 45 | int get_width() const { return(width); }; 46 | 47 | pbm_image_t &operator=(const pbm_image_t& f); 48 | 49 | private: 50 | int width, height; 51 | int widthbytes; 52 | unsigned char ** image; 53 | }; 54 | #endif 55 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2003 - 2007 ACM/SIGDA 2 | # 3 | # Written by Florian Krohm (fkrohm@us.ibm.com) 4 | # 5 | # This Makefile will compile all C (*.c) and C++ (*.C) sources in 6 | # this directory and link them into an executable specified by variable TARGET. 7 | # In order not to have to model the exact dependencies on header files this 8 | # Makefile assumes that all C/C++ files depend on all header files. 9 | # Bison and flex input files, if any, are handled as expected. 10 | # 11 | # make debug - compile and link to produce debuggable executable 12 | # make opt - compile and link to produce optimized executable 13 | # make clean - remove all generated files 14 | # make test - run all testcases 15 | # make submit - copy relevant files to solution directory 16 | # 17 | # You may change the value of SUBMIT_FILES to fit your needs 18 | # You *must not* modify TARGET or SUBMIT_DIR. 19 | # 20 | 21 | TARGET = lithosim 22 | 23 | SUBMIT_DIR = ../solution 24 | SUBMIT_FILES = $(wildcard *.[Cchyl]) Makefile lib.defs 25 | 26 | # 27 | # Tools used 28 | # 29 | CC = gcc -std=c99 30 | CXX = g++ 31 | FLEX = flex 32 | BISON = bison 33 | COMPARE_RESULT = diff-float 34 | 35 | # 36 | # Extra libraries 37 | # 38 | LIBS = -lm 39 | 40 | # 41 | # Define LD_RUN_PATH such that ld.so will find the shared objcets 42 | #export OA_HOME=/mada/software/oa2.2.6 43 | #export LD_RUN_PATH=$(OA_HOME)/lib 44 | #export LD_LIBRARY_PATH = $(OA_HOME)/lib/linux_rhel30_64/opt 45 | 46 | # 47 | # Bison, Flex input if any 48 | # (Only one grammar/scanner spec at this point) 49 | # 50 | Y_FILE := $(wildcard *.y) 51 | Y_SRCS := $(subst .y,.tab.c,$(Y_FILE)) 52 | Y_HDRS := $(subst .y,.tab.h,$(Y_FILE)) 53 | L_FILE := $(wildcard *.l) 54 | L_SRCS := $(subst .l,.yy.c,$(L_FILE)) 55 | 56 | # 57 | # Assemble sources, objects, and headers 58 | # 59 | C_SRCS := $(wildcard *.c) 60 | C_SRCS := $(subst $(L_SRCS),,$(C_SRCS)) 61 | C_SRCS := $(subst $(Y_SRCS),,$(C_SRCS)) 62 | C_SRCS += $(L_SRCS) 63 | C_DOBJS := $(C_SRCS:.c=.o) 64 | C_SRCS += $(Y_SRCS) 65 | C_OBJS := $(C_SRCS:.c=.o) 66 | L_OBJS := $(L_SRCS:.c=.o) 67 | 68 | CXX_SRCS := $(wildcard *.C) 69 | CXX_OBJS := $(CXX_SRCS:.C=.o) 70 | 71 | HDRS := $(wildcard *.h) 72 | HDRS := $(subst $(Y_HDRS),,$(HDRS)) 73 | HDRS += $(Y_HDRS) 74 | 75 | # Choose suitable commandline flags 76 | # 77 | ifeq "$(MAKECMDGOALS)" "opt" 78 | #CFLAGS = -O3 -march=athlon64 79 | CFLAGS = -O3 -mssse3 80 | CXXFLAGS = -O3 -mssse3 81 | else 82 | CFLAGS = -g -W -Wall -pedantic 83 | CXXFLAGS = -g 84 | #-W -Wall -pedantic 85 | #-pg 86 | #-W -Wall -pedantic 87 | endif 88 | 89 | CFLAGS += -I. 90 | CXXFLAGS += -I. 91 | 92 | .PHONY: clean test debug opt submit 93 | 94 | .SECONDARY: $(Y_SRCS) $(L_SRCS) 95 | 96 | debug opt: $(TARGET) 97 | 98 | $(TARGET): $(C_OBJS) $(CXX_OBJS) 99 | $(CXX) $(CXXFLAGS) $(LIBS) -o $(TARGET) $(CXX_OBJS) 100 | 101 | %.yy.c:%.l 102 | $(FLEX) -l -o$(notdir $*).yy.c $< 103 | 104 | %.tab.c %.tab.h:%.y 105 | $(BISON) -d --output=$*.tab.c $< 106 | 107 | %.o:%.c 108 | $(CC) -c $(CFLAGS) $< 109 | 110 | %.o:%.C 111 | $(CXX) -c $(CXXFLAGS) $< 112 | 113 | $(C_DOBJS) $(CXX_OBJS): $(HDRS) 114 | 115 | # 116 | # Flex generates code that causes harmless warnings; suppress those 117 | # 118 | $(L_OBJS): $(L_SRCS) $(HDRS) 119 | $(CC) -c $(CFLAGS) -Wno-unused-function -Wno-unused-label \ 120 | -Wno-implicit-function-declaration $< 121 | 122 | test: bar via tiny small 123 | 124 | opc: baropc viaopc tinyopc smallopc 125 | 126 | bar: $(TARGET) 127 | ./$(TARGET) 8 tests/2_thick_bar7.pbm results/bar-aerial.pnm results/bar-contours.pnm 128 | 129 | baropc: $(TARGET) 130 | ./$(TARGET) 8 tests/2_thick_bar7.pbm results/bar-opc-aerial.pnm results/bar-opc-contours.pnm results/bar-opc.pnm 131 | 132 | via: $(TARGET) 133 | ./$(TARGET) 8 tests/via.pbm results/via-aerial.pnm results/via-contours.pnm 134 | 135 | viaopc: $(TARGET) 136 | ./$(TARGET) 8 tests/via.pbm results/via-opc-aerial.pnm results/via-opc-contours.pnm results/via-opc.pnm 137 | 138 | tiny: $(TARGET) 139 | # ./$(TARGET) 16 tests/tiny.pbm tiny-aerial-180.pnm tiny-contours-180.pnm 140 | # ./$(TARGET) 12 tests/tiny.pbm tiny-aerial-130.pnm tiny-contours-130.pnm 141 | ./$(TARGET) 8 tests/tiny.pbm results/tiny-aerial-90.pnm results/tiny-contours-90.pnm 142 | # ./$(TARGET) 6 tests/tiny.pbm tiny-aerial-65.pnm tiny-contours-65.pnm 143 | # ./$(TARGET) 4 tests/tiny.pbm tiny-aerial-45.pnm tiny-contours-45.pnm 144 | 145 | tinyopc: $(TARGET) 146 | ./$(TARGET) 8 tests/tiny.pbm results/tiny-opc-aerial-90.pnm results/tiny-opc-contours-90.pnm results/tiny-opc-90.pnm 147 | 148 | small: $(TARGET) 149 | ./$(TARGET) 8 tests/small.pbm results/small-aerial.pnm results/small-contours.pnm 150 | 151 | smallopc: $(TARGET) 152 | ./$(TARGET) 8 tests/small.pbm results/small-opc-aerial.pnm results/small-opc-contours.pnm results/bar-opc.pnm 153 | 154 | #medium: $(TARGET) 155 | # ./$(TARGET) tests/medium.pbm results/medium-aerial.pnm results/medium-contours.pnm 156 | 157 | 158 | clean : 159 | rm -f *.o $(TARGET) $(Y_HDRS) $(Y_SRCS) $(L_SRCS) results/* 160 | 161 | submit: 162 | $(HOME)/bin/submit $(SUBMIT_DIR) $(SUBMIT_FILES) 163 | -------------------------------------------------------------------------------- /fltimage.C: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "helper.h" 8 | #include "fltimage.h" 9 | 10 | flt_image_t::flt_image_t(int w, int h) { 11 | height=h; 12 | width=w; 13 | image=(float**)malloc(sizeof(float*)*h); 14 | for(int i=0;imax?image[i][j]:max; 72 | min=image[i][j]threshold)); 103 | } 104 | } 105 | fclose(out); 106 | printf("Saved %d x %d ascii pbm: %s\n",width,height,filename); 107 | } 108 | 109 | 110 | void 111 | flt_image_t::make_jinc(int P, float scale) { 112 | if (P%2==0) P++; // make sure it is odd so there is a center 113 | width=height=P; 114 | image=(float**)malloc(sizeof(float*)*height); 115 | for(int i=0;ithreshold) cur+=128; 192 | if (bthreshold) cur+=64; 193 | if (bthreshold) cur+=32; 194 | if (bthreshold) cur+=16; 195 | if (bthreshold) cur+=8; 196 | if (bthreshold) cur+=4; 197 | if (bthreshold) cur+=2; 198 | if (bthreshold) cur+=1; 199 | str[cnt]=cur; 200 | cnt++; 201 | } 202 | } 203 | 204 | float * 205 | flt_image_t::unpack(unsigned char *str,int n) { 206 | float *barray=(float*)malloc(sizeof(float)*8*n); 207 | for(int b=0;b<8*n;b++) 208 | barray[b]=1.0; 209 | int cnt=0; 210 | unsigned char cur; 211 | for(int b=0;b=width) 243 | realx=width-1; 244 | 245 | int realy=y; 246 | if (y<0) 247 | realy=0; 248 | else if (y>=height) 249 | realy=height-1; 250 | 251 | return(image[realy][realx]); 252 | } 253 | 254 | void 255 | flt_image_t::save(char *filename,float threshold) { 256 | FILE * out = fopen(filename,"w"); 257 | if(!out) { 258 | printf("Error: can't open %s\n",filename); 259 | exit(-1); 260 | } 261 | 262 | fprintf(out,"P4\n"); 263 | fprintf(out, "%d %d\n", width, height); 264 | for(int i=0 ; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "pbmimage.h" 10 | #include "helper.h" 11 | 12 | pbm_image_t::pbm_image_t(int w, int h) { // malloc "image" as 2D array of float. 13 | width=w; 14 | widthbytes=(int)(ceil(width/8.0)); 15 | height=h; 16 | image=(unsigned char**)malloc(sizeof(unsigned char*)*h); 17 | for(int i=0;i > 109 | pbm_image_t::contour_bits() { 110 | std::set > bits; 111 | // a contour bit has at least one white and one black neighbor 112 | int cnt=0; 113 | for(int i=0 ; i0 && cnt<4) 117 | bits.insert(std::pair(j,i)); 118 | } 119 | } 120 | 121 | return(bits); 122 | } 123 | 124 | // pick a bit with random probability depending on neighbors 125 | // out of 8 126 | // 8 = 1/10 127 | // 7 = 2/10 128 | // 6 = 3/10 129 | // 5 = 4/10 130 | // 4 = 5/10 131 | // 3 = 6/10 132 | // 2 = 7/10 133 | // 1 = 8/10 134 | // 0 = 9/10 135 | void 136 | pbm_image_t::pick_adjacent_bit(int *x, int *y) const { 137 | do { 138 | *x=(int)rand()%width; 139 | *y=(int)rand()%height; 140 | 141 | } while (rand()<(9.0-neighbors(*x,*y)*0.1)); 142 | } 143 | 144 | // pick an x,y such that the whole region is 1 or 0 for inverting 145 | bool 146 | pbm_image_t::is_iso_region(int x, int y, int xw, int yw) const { 147 | int val=get(x,y); 148 | for(int xx=x;xx= width) || (*y+deltay >= height) || (*x+deltax<0) || (*y+deltay<0) || !is_iso_region(*x,*y,xw,yw)); 179 | } 180 | 181 | // pick a random region that is 182 | // 1. of size xw * yw 183 | // 2. all set to the same value (on or off) 184 | void 185 | pbm_image_t::pick_iso_region(int *x, int *y, int xw, int yw) const { 186 | do { 187 | *x=(int)(rand()%(width-xw)); 188 | *y=(int)(rand()%(height-yw)); 189 | 190 | } while (!is_iso_region(*x,*y,xw,yw)); 191 | } 192 | 193 | 194 | void 195 | pbm_image_t::invert() { 196 | for(int i=0 ; i 0.2) 222 | set(x,y,1); 223 | else 224 | set(x,y,0); 225 | 226 | 227 | } 228 | 229 | // this updates the entire region affected by this one bit 230 | // also the bits +w in x and y direction 231 | // w should be >= 1 232 | void 233 | pbm_image_t::incremental_convolve(const pbm_image_t &src,const flt_image_t &k, int x, int y, int wx, int wy) { 234 | int xradius=k.get_width()/2; 235 | int yradius=k.get_height()/2; 236 | 237 | int xmin=(x-xradius>0)?x-xradius:0; 238 | int xmax=(x+xradius+wx0)?y-yradius:0; 240 | int ymax=(y+yradius+wy> (7 - (x % 8))) & 1; // get the bit out of the byte. 352 | return(val); 353 | } 354 | 355 | int 356 | pbm_image_t::get_extended(int x, int y) const { 357 | // extends the edge values before smallest index, and beyond largest index. 358 | int realx=x; 359 | if (x<0) realx=0; 360 | else if (x>=width) realx=width-1; 361 | int realy=y; 362 | if (y<0) realy=0; 363 | else if (y>=height) realy=height-1; 364 | unsigned char abyte = image[realy][realx/8]; // get the corresponding byte. 365 | int val=(abyte >> (7 - (realx % 8))) & 1; // get the bit out of the byte. 366 | return(val); 367 | } 368 | 369 | 370 | // flips polarity of a given bit region 371 | // to the opposite of the base bit (x,y) 372 | // note that w should be >= 1 373 | // 1 means a single pixel 374 | void 375 | pbm_image_t::flip(int x, int y, int wx, int wy) { 376 | for (int xx=x;xx xx) && (height > yy)) ) 379 | //if( ((this.width > xx) && (this.height > yy)) ) 380 | flip(xx,yy); 381 | } 382 | 383 | // flips the polarity of a single bit 384 | void 385 | pbm_image_t::flip(int x, int y) { 386 | int bitnum = 7 - (x % 8); 387 | unsigned char bitmask = 1< 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #include "timer.h" 12 | #include "pbmimage.h" 13 | #include "fltimage.h" 14 | 15 | #define INITIAL_TEMP 50000 16 | #define BEGIN_SCALE 0.95 17 | #define MID_SCALE 0.99 18 | #define END_SCALE 0.98 19 | #define FINAL_TEMP 1.0 20 | // how much more fixing an error is vs complexity to mask 21 | #define ERROR_WEIGHT 1 //2.0 22 | #define COMPLEXITY_WEIGHT 3 //1.0 23 | #define CLIMB_WEIGHT 50.0 24 | 25 | // Ian Lee 26 | #define DEBUG 0 27 | 28 | double 29 | schedule(double temp){ 30 | if (temp > 1000) 31 | return(BEGIN_SCALE*temp); 32 | else if (temp > 100) 33 | return(MID_SCALE*temp); 34 | else 35 | return(END_SCALE*temp); 36 | } 37 | 38 | 39 | void 40 | greedy(pbm_image_t &opced_mask, pbm_image_t &target, flt_image_t &kernel) { 41 | pbm_image_t contour(target.get_width(),target.get_height()); 42 | // pbm_image *contour2=create_pbm_image(original->width,original->height); 43 | contour.convolve(opced_mask,kernel); 44 | int new_cost,old_cost=contour.diff(target); 45 | int accept=0,reject=0,counter=0; 46 | // find a list of "edge" contour bits 47 | std::set > bits = opced_mask.contour_bits(); 48 | std::set >::iterator bitit; 49 | int bitnum,x,y; 50 | while (!bits.empty()) { 51 | // pick a random bit, remove it 52 | bitnum=(int)rand()%bits.size(); 53 | int i=0; 54 | for(bitit=bits.begin();ifirst; 56 | y=bitit->second; 57 | bits.erase(bitit); 58 | 59 | // flip the bit 60 | opced_mask.flip(x,y); 61 | contour.incremental_convolve(opced_mask,kernel,x,y,1,1); 62 | new_cost=contour.diff(target); 63 | // if it improves things, keep it 64 | if (new_cost>old_cost) { 65 | reject++; 66 | opced_mask.flip(x,y); 67 | contour.incremental_convolve(opced_mask,kernel,x,y,1,1); 68 | } 69 | else { 70 | // printf("accept %d %d\n",x,y); 71 | 72 | accept++; 73 | old_cost=new_cost; 74 | // subtract the old contour bits if they became contained 75 | int cnt1=opced_mask.singleton(x-1,y); 76 | int cnt2=opced_mask.singleton(x,y-1); 77 | int cnt3=opced_mask.singleton(x+1,y); 78 | int cnt4=opced_mask.singleton(x,y+1); 79 | if (cnt1==0 || cnt1==4) { 80 | bits.erase(std::pair(x-1,y)); 81 | // printf("remove %d %d\n",x-1,y); 82 | } 83 | if (cnt2==0 || cnt2==4) { 84 | bits.erase(std::pair(x,y-1)); 85 | // printf("remove %d %d\n",x,y-1); 86 | } 87 | if (cnt3==0 || cnt3==4) { 88 | bits.erase(std::pair(x+1,y)); 89 | // printf("remove %d %d\n",x+1,y); 90 | } 91 | if (cnt4==0 || cnt4==4) { 92 | bits.erase(std::pair(x,y+1)); 93 | // printf("remove %d %d\n",x,y+1); 94 | } 95 | 96 | 97 | // add its new adjacent bits if they aren't contained 98 | if (x-1>=0 && cnt1>0 && cnt1<4) { 99 | bits.insert(std::pair(x-1,y)); 100 | // printf("add %d %d\n",x-1,y); 101 | } 102 | if (y-1>=0 && cnt2>0 && cnt2<4) { 103 | bits.insert(std::pair(x,y-1)); 104 | // printf("add %d %d\n",x,y-1); 105 | } 106 | if (x+10 && cnt3<4) { 107 | bits.insert(std::pair(x+1,y)); 108 | // printf("add %d %d\n",x+1,y); 109 | } 110 | 111 | if (y+10 && cnt4<4) { 112 | bits.insert(std::pair(x,y+1)); 113 | // printf("add %d %d\n",x,y+1); 114 | } 115 | 116 | 117 | } 118 | 119 | if (counter++==20) { 120 | printf("size: %d\taccept: %d\treject: %d",bits.size(),accept,reject); 121 | printf("\tError: %d\tComplx: %d \tCOST: %d \n", 122 | contour.diff(target),opced_mask.complexity(),old_cost); 123 | // char name[20]; 124 | // sprintf(name,"status-%d.pnm",accept); 125 | // opced_mask.save(name); 126 | 127 | counter=0; 128 | } 129 | } 130 | } 131 | 132 | void 133 | anneal(pbm_image_t &opced_mask, pbm_image_t &target, flt_image_t &kernel,float min_feature) { 134 | 135 | pbm_image_t contour(target.get_width(),target.get_height()); 136 | contour.convolve(opced_mask,kernel); 137 | // instead of unconvolving, just keep a copy of the old contour 138 | pbm_image_t old_contour(target.get_width(),target.get_height()); 139 | old_contour=contour; 140 | pbm_image_t diffmap(target.get_width(),target.get_height()); 141 | diffmap.diff(old_contour,target); 142 | 143 | printf("Performing OPC...\n"); 144 | // initialize temp & placement 145 | float OPTIMAL_COMPLEXITY=opced_mask.complexity(); 146 | float INITIAL_ERROR=contour.diff(target); 147 | printf("Initial complexity %f\n",OPTIMAL_COMPLEXITY); 148 | float old_cost=ERROR_WEIGHT*contour.diff(target)/INITIAL_ERROR + COMPLEXITY_WEIGHT; 149 | float new_cost; 150 | double temperature=INITIAL_TEMP; 151 | int inner_loop=0; 152 | float delta_c=0; 153 | int x,y,xw,yw; 154 | int counter=20; 155 | int accept=0,climb=0,reject=0; 156 | int statcnt=0; 157 | int courseness; 158 | int course_cnt=0; 159 | while (temperature > FINAL_TEMP) { 160 | 161 | if (temperature>5000) 162 | courseness=4; 163 | else if (temperature>1000) 164 | courseness=3; 165 | else if (temperature>100) 166 | courseness=2; 167 | else 168 | courseness=1; 169 | 170 | 171 | if (counter==20) { 172 | printf("\nTEMP: %e\tsize: %2d accept: %5d climb: %5d rej: %5d ", temperature, courseness, accept, climb, reject); 173 | printf("Err: %4d Cmpl: %4d COST: %5.3f \n", old_contour.diff(target), opced_mask.complexity(), old_cost); 174 | counter=0; 175 | accept=climb=reject=0; 176 | //char name[20]; 177 | //sprintf(name,"status-%d.pnm",statcnt++); 178 | //opced_mask.save(name); 179 | } 180 | else 181 | counter++; 182 | 183 | printf("."); 184 | fflush(stdout); 185 | 186 | while (inner_loop < 1000) { 187 | // pick the size of a region we want to flip 188 | // this picks a constant area, but changes the aspect ratio 189 | // xw=(int)(rand()%courseness)+1; 190 | // yw=(int)(courseness / xw); 191 | // find such a region 192 | // opced_mask.pick_iso_region(&x,&y,xw,yw); 193 | // opced_mask.pick_iso_region(&x,&y,xw,yw,diffmap,2*min_feature); 194 | 195 | #if DEBUG 196 | printf("\n\nanneal/inner_loop--%4d--", inner_loop); 197 | printf("x=%d y=%d ", x, y); 198 | #endif 199 | xw=3; 200 | yw=3; 201 | opced_mask.pick_adjacent_bit(&x,&y); 202 | 203 | #if DEBUG 204 | printf("Pick %d %d",x,y); 205 | #endif 206 | 207 | // flip the bits 208 | opced_mask.flip(x,y,xw,yw); 209 | #if DEBUG 210 | printf("Flip %d %d +%d +%d\n",x,y,xw,yw); 211 | #endif 212 | 213 | // only incremental convolve if we can benefit from it 214 | contour.incremental_convolve(opced_mask,kernel,x,y,xw,yw); 215 | 216 | new_cost=ERROR_WEIGHT*contour.diff(target)/INITIAL_ERROR + COMPLEXITY_WEIGHT*opced_mask.complexity()/OPTIMAL_COMPLEXITY; 217 | delta_c = new_cost-old_cost; 218 | 219 | float r=(rand()/(float)RAND_MAX); 220 | if (delta_c < 0) { 221 | accept++; 222 | #if DEBUG 223 | printf("Accept\told %f new %f delta %f\n",old_cost,new_cost,delta_c); 224 | #endif 225 | old_contour=contour; 226 | old_cost=new_cost; 227 | diffmap.diff(old_contour,target); 228 | 229 | } 230 | else if (r < exp(-CLIMB_WEIGHT*INITIAL_TEMP*delta_c/temperature)) { 231 | climb++; 232 | #if DEBUG 233 | printf("Climbed (%f < %f)\n",r,exp(-CLIMB_WEIGHT*INITIAL_TEMP*delta_c/temperature)); 234 | printf("old: %f new %f delta_c %f\n",old_cost,new_cost,delta_c); 235 | #endif 236 | old_contour=contour; 237 | old_cost=new_cost; 238 | diffmap.diff(old_contour,target); 239 | #if DEBUG 240 | printf("GOTCHA - %d x=%d y=%d", inner_loop,x,y); 241 | #endif 242 | } 243 | else { 244 | reject++; 245 | #if DEBUG 246 | opced_mask.save("pre.pnm"); 247 | printf("Reject (%f < %f)\n",r,exp(-CLIMB_WEIGHT*INITIAL_TEMP*delta_c/temperature)); 248 | printf("old: %f new %f delta_c %f\n",old_cost,new_cost,delta_c); 249 | #endif 250 | // flip the bit back if it got worse 251 | opced_mask.flip(x,y,xw,yw); 252 | // opced_mask.save("post.pnm"); 253 | // exit(1); 254 | contour=old_contour; // undo the convolution 255 | 256 | } 257 | inner_loop++; 258 | } 259 | 260 | inner_loop=0; 261 | temperature = schedule(temperature); 262 | } 263 | 264 | printf("\n"); 265 | printf("Final:\n"); 266 | printf("TEMP: %e\tsize: %2d accept: %5d climb: %5d rej: %5d ", temperature, courseness, accept, climb, reject); 267 | printf("Err: %4d Cmpl: %4d COST: %5.3f \n", old_contour.diff(target), opced_mask.complexity(), old_cost); 268 | printf("\n"); 269 | } 270 | 271 | 272 | int 273 | main(int argc, char **argv) { 274 | 275 | srand(2); 276 | 277 | if (argc<3) 278 | { 279 | printf("USAGE: %s [] []\n",argv[0]); 280 | printf("Using 5 arguments will generate aerial image.\n"); 281 | printf("Using 6 arguments will run OPC.\n"); 282 | printf("Suggested nm per pixel (given the tests in test directory):\n"); 283 | printf("16 is ~180nm\n12 is ~130nm\n8 is ~90nm\n6 is ~65nm\n4 is ~45nm\n"); 284 | exit(1); 285 | } 286 | 287 | int Resolution = atoi(argv[1]); // nm per pixel in the input image 288 | float NA = 0.95; // numerical aperture 289 | float lambda = 193; // wavelength 290 | float min_feature=(lambda/NA)/Resolution; 291 | int Filter_Size = (int)(min_feature); // Jinc filter size to see first order assist bars 292 | my_timer_t t; 293 | 294 | 295 | pbm_image_t original(argv[2]); 296 | t.print_time("IMAGE READ"); 297 | 298 | // make the jinc function 299 | flt_image_t kernel; 300 | kernel.make_jinc(Filter_Size,(NA/lambda)*Resolution); 301 | 302 | printf("Convolution kernel of size: %d x %d\n", kernel.get_width(), kernel.get_height()); 303 | kernel.save_pgm("jinc.pnm"); 304 | t.print_time("KERNEL MADE"); 305 | 306 | printf("Creating lithography contour...\n"); 307 | if (argc<=4) { 308 | pbm_image_t contour(original.get_width(),original.get_height()); 309 | contour.convolve(original,kernel); // bit vector based. 310 | t.print_time("CONVOLUTION COMPLETE"); 311 | contour.save(argv[3]); 312 | t.print_time("CONTOUR IMAGE SAVED"); 313 | } 314 | else if (argc<6) { 315 | // only generate the aerial image if we want it saved 316 | printf("Generating aerial image...\n"); 317 | flt_image_t aerial(original.get_width(),original.get_height()); 318 | aerial.convolve(original,kernel); 319 | t.print_time("CONVOLUTION COMPLETE"); 320 | aerial.save_pgm(argv[3]); 321 | t.print_time("AERIAL IMAGE SAVED"); 322 | aerial.save(argv[4],0.2); 323 | t.print_time("CONTOUR IMAGE SAVED"); 324 | } 325 | else if (argc==6) { 326 | 327 | pbm_image_t opced_mask(argv[2]); // start with the target as the mask 328 | anneal(opced_mask,original,kernel,min_feature); 329 | //greedy(opced_mask,original,kernel); 330 | 331 | 332 | opced_mask.save(argv[5]); 333 | t.print_time("OPC'ED MASK GENERATED"); 334 | 335 | printf("Generating aerial image...\n"); 336 | flt_image_t aerial(original.get_width(),original.get_height()); 337 | aerial.convolve(opced_mask,kernel); 338 | aerial.save_pgm(argv[3]); 339 | t.print_time("AERIAL IMAGE SAVED"); 340 | 341 | pbm_image_t contour(original.get_width(),original.get_height()); 342 | contour.convolve(opced_mask,kernel); 343 | printf("Final error: %d\n",contour.diff(original)); 344 | printf("Final count: %d\n",opced_mask.count()); 345 | contour.save(argv[4]); 346 | t.print_time("CONTOUR IMAGE SAVED"); 347 | 348 | } 349 | } 350 | 351 | --------------------------------------------------------------------------------