5414a4723b65b6d697100e8a89ce58fe4868ff07
[hw/altusmetrum] / packages / footprint.5c
1 /*
2  * Copyright © 2012 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 namespace Footprint {
19
20         import File;
21
22         public int mm2mils100(real mm) = floor (mm / 25.4 * 1000 * 100 + 0.5);
23
24         public real mils1002mm(real mils100) = mils100 * 25.4 / 100 / 1000;
25
26         /* OSH park adprocess requirement */
27         public real process_soldermask = mils1002mm(300);
28         public real process_trace = mils1002mm(600);
29         public real process_space = mils1002mm(600);
30         public real process_drill = mils1002mm(1300);
31         public real process_ring = mils1002mm(700);
32
33         /* default pad options */
34         public string pad_options = "square";
35
36         /* default pin options */
37         public string pin_options = "pin";
38
39         /* default ink thickness */
40         public real line_thickness = mils1002mm(1000);
41
42         file out;
43         file reread;
44
45         string out_name;
46
47         string tmp_name;
48
49         public void element_start(string name) {
50
51                 if (!is_uninit(&out_name)) {
52                         tmp_name = sprintf("_tmp-%d.fp", PID::getpid());
53                         out = open(tmp_name, "w");
54                         reread = open(tmp_name, "r");
55                         unlink(tmp_name);
56                 } else
57                         out = stdout;
58
59                 fprintf(out, "# author: Keith Packard\n");
60                 fprintf(out, "# email: keithp@keithp.com\n");
61                 fprintf(out, "# dist-license: GPL 2\n");
62                 fprintf(out, "# use-license: unlimited\n");
63                 fprintf(out, "Element [\"\" \"%s\" \"\" \"\" 0 0 0 0 0 100 \"\"]\n",
64                         name);
65                 fprintf(out, "(\n");
66         }
67
68         public void element_args() {
69
70                 int other_args;
71
72                 ParseArgs::argdesc argd = {
73                         .args = {
74                                 {
75                                         .var = (ParseArgs::arg_var.arg_string) &out_name,
76                                         .name = "output",
77                                         .abbr = 'o',
78                                         .expr_name = "filename",
79                                         .desc = "Output file name"
80                                 }
81                         },
82                         .unknown = &(int other_args)
83                 };
84
85                 ParseArgs::parseargs(&argd, &argv);
86
87                 if (!is_uninit(&other_args))
88                         argv = (string[dim(argv)-other_args+1]) {
89                                 [i] = (i == 0) ? argv[0] : argv[i + other_args-1]
90                         };
91         }
92
93         element_args();
94
95         public void element_end() {
96                 fprintf(out, ")\n");
97
98                 if (!is_uninit(&out_name)) {
99                         try {
100                                 close(out);
101                                 twixt(file result = open(out_name, "w"); close(result)) {
102                                         while (!end(reread)) {
103                                                 putb(getb(reread), result);
104                                         }
105                                 }
106                         } catch open_error (string message, error_type error, string name) {
107                                 fprintf(stderr, "%s: %s\n", name, message);
108                                 exit(1);
109                         }
110                 }
111         }
112
113         public typedef struct {
114                 real    center_x;       /* center of pad */
115                 real    center_y;
116
117                 real    width;          /* size of pad */
118                 real    height;
119
120                 real    spacing;        /* space between pad and other traces */
121                 real    soldermask;     /* space between pad and solder mask */
122                 string  name;           /* pad name */
123                 string  number;         /* pad number */
124                 string  options;        /* pad options */
125
126                 /* normally computed, but can be provided */
127                 real    x1;             /* start of pad "line" */
128                 real    y1;
129                 real    x2;             /* end of pad "line" */
130                 real    y2;
131                 real    thickness;      /* thickness of pad "line" */
132                 real    clearance;      /* twice the spacing between pad and other traces */
133                 real    mask;           /* thickness of pad and solder mask */
134         } pad_t;
135
136         pad_t fill_pad(pad_t pad) {
137                 if (is_uninit(&pad.x1))
138                         pad.x1 = pad.center_x - max(0, (pad.width - pad.height) / 2);
139                 if (is_uninit(&pad.x2))
140                         pad.x2 = pad.center_x + max(0, (pad.width - pad.height) / 2);
141
142                 if (is_uninit(&pad.y1))
143                         pad.y1 = pad.center_y - max(0, (pad.height - pad.width) / 2);
144                 if (is_uninit(&pad.y2))
145                         pad.y2 = pad.center_y + max(0, (pad.height - pad.width) / 2);
146
147                 if (is_uninit(&pad.spacing))
148                         pad.spacing = process_space;
149                 if (is_uninit(&pad.soldermask))
150                         pad.soldermask = process_soldermask;
151                 if (is_uninit(&pad.number))
152                         pad.number = pad.name;
153                 if (is_uninit(&pad.options))
154                         pad.options = pad_options;
155
156                 if (is_uninit(&pad.thickness))
157                         pad.thickness = min(pad.width, pad.height);
158
159                 if (is_uninit(&pad.clearance))
160                         pad.clearance = pad.spacing * 2;
161
162                 if (is_uninit(&pad.mask))
163                         pad.mask = pad.thickness + pad.soldermask * 2;
164
165 #               fprintf(out, "pad %v\n", pad);
166
167                 return pad;
168         }
169
170         public exception violation(string rule, real min, real val);
171
172         public check(string rule, real min, real val, bool zero_ok) {
173                 if (zero_ok && val <= 0)
174                         return;
175                 if (val < min) {
176                         Debug::trace(Thread::current());
177                         File::fprintf(stderr, "rule %s violated (%fmm %fmils < %fmm %fmils)\n",
178                                       rule,
179                                       val, mm2mils100(val)/100.0,
180                                       min, mm2mils100(min)/100.0);
181                         exit(1);
182                 }
183         }
184
185         public void pad(pad_t pad)
186         {
187                 pad = fill_pad(pad);
188
189                 check("pad trace", process_trace, pad.thickness, false);
190                 check("pad space", process_space, pad.clearance / 2, true);
191                 check("pad mask", process_soldermask, (pad.mask - pad.thickness) / 2, true);
192
193                 fprintf(out, "    Pad[");
194                 fprintf(out, " %6d %6d %6d %6d",
195                         mm2mils100(pad.x1),
196                         mm2mils100(pad.y1),
197                         mm2mils100(pad.x2),
198                         mm2mils100(pad.y2));
199                 fprintf(out, " %6d %6d %6d",
200                         mm2mils100(pad.thickness),
201                         mm2mils100(pad.clearance),
202                         mm2mils100(pad.mask));
203                 fprintf(out, " \"%s\" \"%s\" \"%s\"]\n",
204                         pad.name, pad.number, pad.options);
205         }
206
207         public void pad_mm_space_mask_options(real center_x,
208                                               real center_y,
209                                               real width,
210                                               real height,
211                                               real spacing,
212                                               real soldermask,
213                                               string name,
214                                               string number,
215                                               string options)
216         {
217                 pad((pad_t) {
218                                 .center_x = center_x,
219                                 .center_y = center_y,
220                                 .width = width,
221                                 .height = height,
222                                 .spacing = spacing,
223                                 .soldermask = soldermask,
224                                 .name = name,
225                                 .number = number,
226                                 .options = options
227                                 });
228         }
229
230         public void pad_mm_space_options(real center_x,
231                                          real center_y,
232                                          real width,
233                                          real height,
234                                          real spacing,
235                                          string name,
236                                          string number,
237                                          string options)
238         {
239                 pad((pad_t) {
240                                 .center_x = center_x,
241                                 .center_y = center_y,
242                                 .width = width,
243                                 .height = height,
244                                 .spacing = spacing,
245                                 .name = name,
246                                 .number = number,
247                                 .options = options
248                                 });
249         }
250
251         public void pad_mm_mask_options(real center_x,
252                                          real center_y,
253                                          real width,
254                                          real height,
255                                          real soldermask,
256                                          string name,
257                                          string number,
258                                          string options)
259         {
260                 pad((pad_t) {
261                                 .center_x = center_x,
262                                 .center_y = center_y,
263                                 .width = width,
264                                 .height = height,
265                                 .soldermask = soldermask,
266                                 .name = name,
267                                 .number = number,
268                                 .options = options
269                                 });
270         }
271
272         public void pad_mm_space(real center_x,
273                                  real center_y,
274                                  real width,
275                                  real height,
276                                  real spacing,
277                                  string name,
278                                  string number)
279         {
280                 pad((pad_t) {
281                                 .center_x = center_x,
282                                 .center_y = center_y,
283                                 .width = width,
284                                 .height = height,
285                                 .spacing = spacing,
286                                 .name = name,
287                                 .number = number,
288                                 });
289         }
290
291         public void pad_mm(real center_x,
292                            real center_y,
293                            real width,
294                            real height,
295                            string name,
296                            string number)
297         {
298                 pad((pad_t) {
299                                 .center_x = center_x,
300                                 .center_y = center_y,
301                                 .width = width,
302                                 .height = height,
303                                 .name = name,
304                                 .number = number,
305                                 });
306         }
307
308         public void pad_mm_options(real center_x,
309                                    real center_y,
310                                    real width,
311                                    real height,
312                                    string name,
313                                    string number,
314                                    string options)
315         {
316                 pad((pad_t) {
317                                 .center_x = center_x,
318                                 .center_y = center_y,
319                                 .width = width,
320                                 .height = height,
321                                 .name = name,
322                                 .number = number,
323                                 .options = options
324                                 });
325         }
326
327         /* Pad with partial solder coverage.
328          * Useful for pads under parts which aren't
329          * big enough to use the center function below
330          */
331         public void pad_mm_partial(real center_x,
332                                    real center_y,
333                                    real width,
334                                    real height,
335                                    real partial,
336                                    string name)
337         {
338                 pad((pad_t) {
339                                 .center_x = center_x,
340                                 .center_y = center_y,
341                                 .width = width,
342                                 .height = height,
343                                 .name = name,
344                                 .options = pad_options + ",nopaste"
345                                 });
346
347                 real    ratio = floor(sqrt(partial) * 100.0 + 0.5) / 100;
348
349                 pad((pad_t) {
350                                 .center_x = center_x,
351                                 .center_y = center_y,
352                                 .width = width * ratio,
353                                 .height = height * ratio,
354                                 .name = name,
355                                 });
356         }
357
358         public void pad_mm_arbitrary(   real x1,
359                                         real y1,
360                                         real x2,
361                                         real y2,
362                                         real thickness,
363                                         string name,
364                                         string number,
365                                         string options)
366         {
367                 pad((pad_t) {
368                                 .x1 = x1,
369                                 .y1 = y1,
370                                 .x2 = x2,
371                                 .y2 = y2,
372                                 .thickness = thickness,
373                                 .name = name,
374                                 .number = number,
375                                 .options = options
376                                 });
377         }
378
379         public typedef struct {
380                 real    x;              /* center of pin */
381                 real    y;
382                 real    drill;          /* diameter of drill hole */
383
384                 real    ring;           /* width of annular ring around hole */
385                 real    spacing;        /* space between pin and other traces */
386                 real    soldermask;     /* space between pin and solder mask */
387
388                 string  name;           /* pin name */
389                 string  number;         /* pin number */
390                 string  options;        /* pin options */
391
392                 /* normally computed, but can be provided */
393                 real    thickness;      /* thickness of pin "line" */
394                 real    clearance;      /* twice the spacing between pin and other traces */
395                 real    mask;           /* thickness of pin and solder mask */
396         } pin_t;
397
398         pin_t fill_pin(pin_t pin) {
399
400                 /* Fill in process rules if unset */
401                 if (is_uninit(&pin.spacing))
402                         pin.spacing = process_space;
403                 if (is_uninit(&pin.ring))
404                         pin.ring = process_ring;
405                 if (is_uninit(&pin.soldermask))
406                         pin.soldermask = process_soldermask;
407
408                 if (is_uninit(&pin.number))
409                         pin.number = pin.name;
410                 if (is_uninit(&pin.options))
411                         pin.options = pin_options;
412
413                 if (is_uninit(&pin.thickness))
414                         pin.thickness = pin.drill + pin.ring * 2;
415
416                 if (is_uninit(&pin.mask))
417                         pin.mask = pin.thickness + pin.soldermask * 2;
418
419                 if (is_uninit(&pin.clearance))
420                         pin.clearance = pin.spacing * 2;
421
422                 return pin;
423         }
424
425         public void pin(pin_t pin)
426         {
427                 pin = fill_pin(pin);
428
429                 check("pin drill", process_drill, pin.drill, false);
430                 check("pin ring", process_ring, (pin.thickness - pin.drill) / 2, true);
431                 check("pin space", process_space, pin.clearance / 2, true);
432                 if (String::index(pin.options, "via") < 0)
433                         check("pin mask", process_soldermask, (pin.mask - pin.thickness) / 2, true);
434
435                 fprintf(out, "    Pin[");
436                 fprintf(out, " %6d %6d",
437                         mm2mils100(pin.x),
438                         mm2mils100(pin.y));
439                 fprintf(out, " %6d %6d %6d",
440                         mm2mils100(pin.thickness),
441                         mm2mils100(pin.clearance),
442                         mm2mils100(pin.mask));
443                 fprintf(out," %6d",
444                        mm2mils100(pin.drill));
445                 fprintf(out, " \"%s\" \"%s\" \"%s\"]\n",
446                         pin.name, pin.number, pin.options);
447         }
448
449         public void pin_mm_space_mask_options(real x, real y,
450                                               real drill, real copper, real spacing, real soldermask,
451                                               string name, string number, string options)
452         {
453                 pin ((pin_t) {
454                                 .x = x,
455                                 .y = y,
456                                 .drill = drill,
457                                 .ring = copper,
458                                 .spacing = spacing,
459                                 .soldermask = soldermask,
460                                 .name = name,
461                                 .number = number,
462                                 .options = options });
463         }
464
465         public void pin_mm_space_options(real x, real y, real drill, real copper, real spacing,
466                                          string name,
467                                          string number,
468                                          string options)
469         {
470                 pin ((pin_t) {
471                                 .x = x,
472                                 .y = y,
473                                 .drill = drill,
474                                 .ring = copper,
475                                 .spacing = spacing,
476                                 .name = name,
477                                 .number = number,
478                                 .options = options });
479         }
480
481         public void pin_mm_space_mask(real x, real y,
482                                       real drill, real copper, real spacing, real soldermask,
483                                       string name, string number)
484         {
485                 pin ((pin_t) {
486                                 .x = x,
487                                 .y = y,
488                                 .drill = drill,
489                                 .ring = copper,
490                                 .spacing = spacing,
491                                 .soldermask = soldermask,
492                                 .name = name,
493                                 .number = number });
494         }
495
496         public void pin_mm_space(real x, real y, real drill, real copper, real spacing,
497                         string name,
498                         string number)
499         {
500                 pin ((pin_t) {
501                                 .x = x,
502                                 .y = y,
503                                 .drill = drill,
504                                 .ring = copper,
505                                 .spacing = spacing,
506                                 .name = name,
507                                 .number = number });
508         }
509
510         public void pin_mm(real x, real y, real drill, real copper,
511                         string name,
512                         string number)
513         {
514                 pin ((pin_t) {
515                                 .x = x,
516                                 .y = y,
517                                 .drill = drill,
518                                 .ring = copper,
519                                 .name = name,
520                                 .number = number,});
521         }
522
523         public void pin_mm_options(real x, real y, real drill, real copper,
524                                    string name,
525                                    string number,
526                                    string options)
527         {
528                 pin ((pin_t) {
529                                 .x = x,
530                                 .y = y,
531                                 .drill = drill,
532                                 .ring = copper,
533                                 .name = name,
534                                 .number = number,
535                                 .options = options });
536         }
537
538         public void pin_mm_mask_options(real x, real y, real drill, real copper,
539                                         real soldermask,
540                                         string name,
541                                         string number,
542                                         string options)
543         {
544                 pin ((pin_t) {
545                                 .x = x,
546                                 .y = y,
547                                 .drill = drill,
548                                 .ring = copper,
549                                 .soldermask = soldermask,
550                                 .name = name,
551                                 .number = number,
552                                 .options = options });
553         }
554
555         public void via_mm(real x, real y,
556                            real drill, real copper,
557                            string name)
558         {
559                 pin ((pin_t) {
560                                 .x = x,
561                                 .y = y,
562                                 .drill = drill,
563                                 .ring = copper,
564                                 .mask = 0,
565                                 .name = name,
566                                 .options = "via"
567                         });
568         }
569
570         public void line (real x1, real y1, real x2, real y2)
571         {
572                 fprintf(out, "    ElementLine[");
573                 fprintf(out, " %6d %6d %6d %6d",
574                         mm2mils100(x1),
575                         mm2mils100(y1),
576                         mm2mils100(x2),
577                         mm2mils100(y2));
578                 fprintf(out, " %d]\n", mm2mils100(line_thickness));
579         }
580
581         public void rect (real x, real y, real w, real h)
582         {
583                 line(x,y,x+w,y);
584                 line(x+w,y,x+w,y+h);
585                 line(x+w,y+h,x,y+h);
586                 line(x,y+h,x,y);
587         }
588
589         public void arc (real center_x, real center_y,
590                          real radius_x, real radius_y,
591                          real start_angle, real delta_angle)
592         {
593                 fprintf(out, "    ElementArc[ %6d %6d %6d %6d %3d %3d %d]\n",
594                         mm2mils100(center_x), mm2mils100(center_y),
595                         mm2mils100(radius_x), mm2mils100(radius_y),
596                         start_angle, delta_angle, mm2mils100(line_thickness));
597         }
598
599         public typedef struct {
600                 real    x, y;           /* center */
601                 real    width, height;  /* size */
602
603                 real    via_space_x;
604                 real    via_space_y;
605                 int     via_cols, via_rows;
606                 string  name;
607
608                 real    via_drill;
609                 real    via_copper;
610
611         } center_t;
612
613         public void center_pad(center_t center) {
614
615                 if (is_uninit(&center.via_drill))
616                         center.via_drill = process_drill;
617
618                 if (is_uninit(&center.via_copper))
619                         center.via_copper = process_ring;
620
621                 if (is_uninit(&center.via_space_x)) {
622                         real    side_x = center.via_drill / 2 + center.via_copper;
623                         real    space_x = center.width - 2 * side_x;
624
625                         center.via_space_x = space_x / (center.via_cols - 1);
626                 }
627
628                 if (is_uninit(&center.via_space_y)) {
629                         real    side_y = center.via_drill / 2 + center.via_copper;
630                         real    space_y = center.width - 2 * side_y;
631
632                         center.via_space_y = space_y / (center.via_rows - 1);
633                 }
634
635                 /* whole pad */
636                 pad((pad_t) {
637                                 .center_x = center.x,
638                                 .center_y = center.y,
639                                 .width = center.width,
640                                 .height = center.height,
641                                 .name = center.name,
642                                 .options = "square,nopaste",
643                                 .spacing = process_space,
644                                 .mask = 0});
645
646                 /* vias */
647                 for (int r = 0; r < center.via_rows; r++)
648                         for (int c = 0; c < center.via_cols; c++) {
649                                 real    x = (c - (center.via_cols - 1) / 2) * center.via_space_x;
650                                 real    y = (r - (center.via_rows - 1) / 2) * center.via_space_y;
651
652                                 via_mm(x, y, process_drill, process_ring, center.name);
653
654                                 pad((pad_t) {
655                                                 .center_x = center.x + x,
656                                                 .center_y = center.y + y,
657                                                 .width = center.via_space_x / 2,
658                                                 .height = center.via_space_y / 2,
659                                                 .name = center.name,
660                                                 .options = "square,nopaste",
661                                                 .clearance = 0,
662                                                 .mask = 0,
663                                         });
664                         }
665
666                 for (real r = 0; r < center.via_rows - 0.5; r += 0.5) {
667                         for (real c = 0; c < center.via_cols - 0.5; c += 0.5) {
668
669                                 if (is_int(r) && is_int(c))
670                                         continue;
671
672                                 real    x = (c - (center.via_cols - 1) / 2) * center.via_space_x;
673                                 real    y = (r - (center.via_rows - 1) / 2) * center.via_space_y;
674
675                                 /* exposed copper */
676                                 pad((pad_t) {
677                                                 .center_x = center.x + x,
678                                                 .center_y = center.y + y,
679                                                 .width = center.via_space_x / 2,
680                                                 .height = center.via_space_x / 2,
681                                                 .soldermask = 0,
682                                                 .name = center.name,
683                                                 .options = "square,nopaste",
684                                                         });
685
686                                 /* paste spot */
687                                 pad((pad_t) {
688                                                 .center_x = center.x + x,
689                                                 .center_y = center.y + y,
690                                                 .width = center.via_space_x / 3.5,
691                                                 .height = center.via_space_y / 3.5,
692                                                 .name = center.name,
693                                                 .options = "square",
694                                         });
695                         }
696                 }
697         }
698 }