From 5451a4830f55d487808eb92bbcca88aedf2b29c7 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Mon, 26 Dec 2011 23:05:52 -0800 Subject: [PATCH] Basic support in tgt-pcb for reading footprint files. When a black-box item requests a specific footprint, we need to read a file (.fp) that contains the actual details about that footprint. Support parse of that file and use the loaded Element to generate the footprint for the item. --- .gitignore | 4 + tgt-pcb/Makefile.in | 11 ++- tgt-pcb/footprint.cc | 89 +++++++++++++++++++ tgt-pcb/fp.lex | 89 +++++++++++++++++++ tgt-pcb/fp.y | 200 +++++++++++++++++++++++++++++++++++++++++++ tgt-pcb/fp_api.h | 68 +++++++++++++++ tgt-pcb/pcb.cc | 2 + tgt-pcb/pcb_priv.h | 30 +++++++ tgt-pcb/scope.cc | 10 +++ tgt-pcb/show_pcb.cc | 45 +++++++++- 10 files changed, 543 insertions(+), 5 deletions(-) create mode 100644 tgt-pcb/footprint.cc create mode 100644 tgt-pcb/fp.lex create mode 100644 tgt-pcb/fp.y create mode 100644 tgt-pcb/fp_api.h diff --git a/.gitignore b/.gitignore index ba885fa6d..37682841d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,10 @@ Makefile /_pli_types.h config.h /tgt-pcb/pcb_config.h +/tgt-pcb/fp.cc +/tgt-pcb/fp.h +/tgt-pcb/fp.output +/tgt-pcb/fp_lex.cc /tgt-vvp/vvp_config.h /tgt-vhdl/vhdl_config.h /vpi/vpi_config.h diff --git a/tgt-pcb/Makefile.in b/tgt-pcb/Makefile.in index 9e728681a..652d7ab80 100644 --- a/tgt-pcb/Makefile.in +++ b/tgt-pcb/Makefile.in @@ -34,6 +34,8 @@ CXX = @CXX@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ +LEX = @LEX@ +YACC = @YACC@ ifeq (@srcdir@,.) INCLUDE_PATH = -I. -I.. @@ -45,7 +47,7 @@ CPPFLAGS = $(INCLUDE_PATH) @CPPFLAGS@ @DEFS@ @PICFLAG@ CFLAGS = @WARNING_FLAGS@ @CFLAGS@ LDFLAGS = @LDFLAGS@ -O = pcb.o scope.o show_netlist.o show_pcb.o +O = pcb.o scope.o show_netlist.o show_pcb.o footprint.o fp.o fp_lex.o all: dep pcb.tgt @@ -74,6 +76,13 @@ dep: $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep +fp_lex.cc: $(srcdir)/fp.lex + $(LEX) -s -ofp_lex.cc $(srcdir)/fp.lex + +fp.cc fp.h: $(srcdir)/fp.y + $(YACC) --verbose -t -d -o fp.cc $(srcdir)/fp.y + mv fp.cc.h fp.h 2>/dev/null || mv fp.hh fp.h + ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl TGTDEPLIBS=../libivl.a diff --git a/tgt-pcb/footprint.cc b/tgt-pcb/footprint.cc new file mode 100644 index 000000000..d79530987 --- /dev/null +++ b/tgt-pcb/footprint.cc @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include "version_base.h" +# include "version_tag.h" +# include "pcb_config.h" +# include "pcb_priv.h" +# include "fp_api.h" +# include + +using namespace std; + +map footprints; + +static int check_footprint(element_data_t*elem); + +/* +* Scan the element list and collect footprints needed. +*/ +int load_footprints(void) +{ + for (map::const_iterator cur = element_list.begin() + ; cur != element_list.end() ; ++ cur) { + check_footprint(cur->second); + } + + return 0; +} + +/* + * The fpparse funciton calls back the callback_fp_element function + * for each Element that it parses. The check_footprint function + * stores in the cur_footprint variable the name of the footprint that + * we are trying to find in the file. The callback uses that name to + * store the Element into the footprints map. + */ +static string cur_footprint = ""; +void callback_fp_element(const struct fp_element_t&cur_elem) +{ + assert(cur_footprint != ""); + footprints[cur_footprint] = cur_elem; + cur_footprint = ""; +} + + +static int check_footprint(element_data_t*elem) +{ + if (elem->footprint == "") { + cerr << "No footprint defined for \"" << elem->description << "\"." << endl; + return -1; + } + + map::iterator match = footprints.find(elem->footprint); + if (match != footprints.end()) + return 0; + + string fpname = elem->footprint + ".fp"; + + cur_footprint = elem->footprint; + int rc = parse_fp_file(fpname); + if (rc != 0) { + cerr << "parse_fp_file(" << fpname << ") returns rc=" << rc << endl; + return rc; + } + + match = footprints.find(elem->footprint); + if (match == footprints.end()) { + cerr << "Unable to locate footprint " << elem->footprint << "." << endl; + return -2; + } + + return 0; +} diff --git a/tgt-pcb/fp.lex b/tgt-pcb/fp.lex new file mode 100644 index 000000000..caba03a3e --- /dev/null +++ b/tgt-pcb/fp.lex @@ -0,0 +1,89 @@ +%option prefix="fp" +%option never-interactive +%option noinput +%option nounput +%option noyywrap +%option reentrant + +%{ +/* + * Copyright (C) 2011 Stephen Williams (steve@icarus.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +# include "fp_api.h" +# include "fp.h" + +# define YY_DECL int yylex(YYSTYPE*yylvalp, YYLTYPE*yyllocp, yyscan_t yyscanner) + +%} + +SPACE [ \t\f\r] + +%% + + /* Skip comment lines */ +"#".* { ; } + + + /* Skip white space */ +{SPACE} { ; } +"\n" { yyllocp->first_line += 1; } + +"Element" { return K_ELEMENT; } +"Pad" { return K_PAD; } + +"0x"[0-9a-fA-F]+ { + yylvalp->integer = strtoul(yytext+2,0,10); + return INTEGER; +} + +"0"[0-7]* { + yylvalp->integer = strtoul(yytext,0,8); + return INTEGER; +} + +[1-9][0-9]* { + yylvalp->integer = strtoul(yytext,0,10); + return INTEGER; +} + +"\""[^\"]*"\"" { + size_t len = strlen(yytext)-2; + char*tmp = new char[len+1]; + memcpy(tmp, yytext+1, len); + tmp[len] = 0; + yylvalp->text = tmp; + return STRING; +} + + /* Isolated characters are tokens */ +. { return yytext[0]; } + +%% + +yyscan_t prepare_fp_lexor(FILE*fd) +{ + yyscan_t scanner; + yylex_init(&scanner); + yyrestart(fd, scanner); + return scanner; +} + +void destroy_fp_lexor(yyscan_t scanner) +{ + yylex_destroy(scanner); +} diff --git a/tgt-pcb/fp.y b/tgt-pcb/fp.y new file mode 100644 index 000000000..a4bcfc703 --- /dev/null +++ b/tgt-pcb/fp.y @@ -0,0 +1,200 @@ + +%name-prefix="fp" +%pure-parser +%lex-param {yyscan_t yyscanner} +%parse-param {yyscan_t yyscanner} +%parse-param {const char*file_path} + +%{ +/* + * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include "fp_api.h" +# include "pcb_priv.h" + +using namespace std; + +/* Recent version of bison expect that the user supply a + YYLLOC_DEFAULT macro that makes up a yylloc value from existing + values. I need to supply an explicit version to account for the + text field, that otherwise won't be copied. */ +# define YYLLOC_DEFAULT(Current, Rhs, N) do { \ + (Current).first_line = (Rhs)[1].first_line; \ + } while (0) + +static void yyerror(YYLTYPE*yylloc, yyscan_t scanner, const char*file_path, const char*msg) +{ + fprintf(stderr, "%s\n", msg); +} + +extern yyscan_t prepare_fp_lexor(FILE*fd); +extern void destroy_fp_lexor(yyscan_t scanner); +extern int fplex(union YYSTYPE*yylvalp, YYLTYPE*yylloc, yyscan_t scanner); + +static fp_pad_t cur_pad; +static fp_element_t cur_element; + +%} + +%union { + long integer; + char*text; +}; + +%token STRING +%token INTEGER +%token K_ELEMENT +%token K_PAD + +%type integer + +%% + +file_items : file_items file_item | file_item ; + +file_item : element ; + +element + : K_ELEMENT element_header '(' element_items ')' + { callback_fp_element(cur_element); } + ; + +element_header + : '[' INTEGER STRING STRING STRING integer integer integer integer integer integer STRING ']' + { cur_element.nflags = $2; + cur_element.description = $3; delete[]$3; + cur_element.name = $4; delete[]$4; + cur_element.value = $5; delete[]$5; + cur_element.mx = $6; + cur_element.my = $7; + cur_element.tx = $8; + cur_element.ty = $9; + cur_element.tdir = $10; + cur_element.tscale = $11; + cur_element.tsflags = $12; delete[]$12; + cur_element.pads.clear(); + } + | '[' error ']' + { errormsg(@2, "Error in element header\n"); + yyerrok; + cur_element.nflags = 0; + cur_element.description = ""; + cur_element.name = ""; + cur_element.value = ""; + cur_element.mx = 0; + cur_element.my = 0; + cur_element.tx = 0; + cur_element.ty = 0; + cur_element.tdir = 0; + cur_element.tscale = 0; + cur_element.tsflags = ""; + cur_element.pads.clear(); + } + ; + +element_items + : element_items element_item + | element_item + ; + +element_item + : pad { cur_element.pads[cur_pad.name] = cur_pad; } + ; + +integer + : INTEGER { $$ = $1; } + | '-' INTEGER { $$ = -$2; } + ; + +pad + : K_PAD '[' integer integer integer integer integer integer integer STRING STRING STRING ']' + { cur_pad.rx1 = $3; + cur_pad.ry1 = $4; + cur_pad.rx2 = $5; + cur_pad.ry2 = $6; + cur_pad.thickness = $7; + cur_pad.clearance = $8; + cur_pad.mask = $9; + cur_pad.name = $10; delete[]$10; + cur_pad.number = $11; delete[]$11; + cur_pad.sflags = $12; delete[]$12; + } + + | K_PAD '[' error ']' + { errormsg(@3, "Error in pad header\n"); + yyerrok; + cur_pad.rx1 = 0; + cur_pad.ry1 = 0; + cur_pad.rx2 = 0; + cur_pad.ry2 = 0; + cur_pad.thickness = 0; + cur_pad.clearance = 0; + cur_pad.mask = 0; + cur_pad.name = ""; + cur_pad.number = ""; + cur_pad.sflags = ""; + } + ; + +%% + +static string parse_file_path; +int parse_fp_errors = 0; +int parse_fp_sorrys = 0; + +void errormsg(const YYLTYPE&loc, const char*fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + fprintf(stderr, "%s:%d: error: ", parse_file_path.c_str(), loc.first_line); + vfprintf(stderr, fmt, ap); + va_end(ap); + parse_fp_errors += 1; +} + +void sorrymsg(const YYLTYPE&loc, const char*fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + fprintf(stderr, "%s:%d: sorry: ", parse_file_path.c_str(), loc.first_line); + vfprintf(stderr, fmt, ap); + va_end(ap); + parse_fp_sorrys += 1; +} + +int parse_fp_file(const string&file_path) +{ + FILE*fd = fopen(file_path.c_str(), "r"); + if (fd == 0) { + perror(file_path.c_str()); + return -1; + } + + parse_file_path = file_path; + parse_fp_errors = 0; + parse_fp_sorrys = 0; + yyscan_t scanner = prepare_fp_lexor(fd); + int rc = yyparse(scanner, file_path.c_str()); + fclose(fd); + destroy_fp_lexor(scanner); + + return rc; +} diff --git a/tgt-pcb/fp_api.h b/tgt-pcb/fp_api.h new file mode 100644 index 000000000..b15253d29 --- /dev/null +++ b/tgt-pcb/fp_api.h @@ -0,0 +1,68 @@ +#ifndef __fp_api_H +#define __fp_api_H +/* + * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * + * This source code is free software; you can redistribute it + * and/or modify it in source code form under the terms of the GNU + * General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +# include + +/* + * This is the interface function that the user invokes to parse a + * footprint file. The argument is the path to the element. + */ +extern int parse_fp_file(const std::string&file_path); + +typedef void*yyscan_t; + +/* + * The yyltype supports the passing of detailed source file location + * information between the lexical analyzer and the parser. Defining + * YYLTYPE compels the lexor to use this type and not something other. + */ +struct yyltype { + unsigned first_line; + yyltype() { first_line = 1; } +}; +# define YYLTYPE struct yyltype + +/* + * Use this function during parse to generate error messages. The "loc" + * is the location of the token that triggered the error, and the fmt + * is printf-style format. + */ +extern void errormsg(const YYLTYPE&loc, const char*fmt, ...) __attribute__((format (printf, 2, 3))); + +extern void sorrymsg(const YYLTYPE&loc, const char*fmt, ...) __attribute__((format (printf, 2, 3))); + +extern void callback_fp_element(const struct fp_element_t&); + +/* + * Set this to a non-zero value to enable parser debug output. + */ +//extern int yydebug; + +/* + * The parser counts the errors that is handed in the parse_errors + * variable. For a clean compile, this value should not change. (The + * caller sets its initial value.) The sorrys are the count of + * unsupported constructs that are encountered. + */ +//extern int parse_errors; +extern int parse_fp_sorrys; + +#endif diff --git a/tgt-pcb/pcb.cc b/tgt-pcb/pcb.cc index 547dc3a7b..8d72eba02 100644 --- a/tgt-pcb/pcb.cc +++ b/tgt-pcb/pcb.cc @@ -64,6 +64,8 @@ int target_design(ivl_design_t des) } } + load_footprints(); + assert(pcb_path); show_pcb(pcb_path); diff --git a/tgt-pcb/pcb_priv.h b/tgt-pcb/pcb_priv.h index b98fceef0..ef47c57ee 100644 --- a/tgt-pcb/pcb_priv.h +++ b/tgt-pcb/pcb_priv.h @@ -48,10 +48,40 @@ extern std::list nexus_list; struct element_data_t { std::string description; std::string value; + std::string footprint; }; extern std::map element_list; +extern int load_footprints(void); + +struct fp_pad_t { + long rx1, ry1; + long rx2, ry2; + int thickness; + int clearance; + int mask; + std::string name; + std::string number; + std::string sflags; +}; + +struct fp_element_t { + long nflags; + std::string description; + std::string name; + std::string value; + long mx, my; + long tx, ty; + int tdir; + int tscale; + std::string tsflags; + + std::map pads; +}; + +extern std::map footprints; + extern void show_netlist(const char*net_path); extern void show_pcb(const char*pcb_path); diff --git a/tgt-pcb/scope.cc b/tgt-pcb/scope.cc index 22f8e25d4..545945abd 100644 --- a/tgt-pcb/scope.cc +++ b/tgt-pcb/scope.cc @@ -162,6 +162,16 @@ static void black_box(ivl_scope_t scope, const map&attrs) default: assert(0); } + + } else if (name == "footprint") { + ivl_expr_t exp = ivl_parameter_expr(par); + switch (ivl_expr_type(exp)) { + case IVL_EX_STRING: + elem_data->footprint = ivl_expr_string(exp); + break; + default: + assert(0); + } } } diff --git a/tgt-pcb/show_pcb.cc b/tgt-pcb/show_pcb.cc index 02623bb90..163691640 100644 --- a/tgt-pcb/show_pcb.cc +++ b/tgt-pcb/show_pcb.cc @@ -28,6 +28,7 @@ static void show_pcb_header(FILE*fpcb) fprintf(fpcb, "Grid[100.0 0 0 1]\n"); } +static void show_pcb_element(FILE*fpcb, const string&refdes, element_data_t*elem); void show_pcb(const char*pcb_path) { @@ -44,9 +45,17 @@ void show_pcb(const char*pcb_path) for (map::const_iterator cur = element_list.begin() ; cur != element_list.end() ; ++ cur) { - const string&refdes = cur->first; - const string&descr = cur->second->description; - const string&value = cur->second->value; + show_pcb_element(fpcb, cur->first, cur->second); + } + + fclose(fpcb); +} + +static void show_pcb_element(FILE*fpcb, const string&refdes, element_data_t*elem) +{ + string descr = elem->description; + const string&value = elem->value; + if (elem->footprint == "") { fprintf(fpcb, "Element[\"\" \"%s\" \"%s\" \"%s\"", descr.c_str(), refdes.c_str(), value.c_str()); @@ -60,7 +69,35 @@ void show_pcb(const char*pcb_path) // from a library. fprintf(fpcb, "(\n"); fprintf(fpcb, ")\n"); + return; } - fclose(fpcb); + fp_element_t&foot = footprints[elem->footprint]; + if (descr == "") + descr = foot.description; + fprintf(fpcb, "Element[0x%lx \"%s\" \"%s\" \"%s\"", + foot.nflags, descr.c_str(), refdes.c_str(), value.c_str()); + + fprintf(fpcb, " %ld %ld", foot.mx, foot.my); + fprintf(fpcb, " %ld %ld %d %d \"%s\"", foot.tx, foot.ty, foot.tdir, + foot.tscale, foot.tsflags.c_str()); + + fprintf(fpcb, "]\n(\n"); + + for (map::const_iterator cur = foot.pads.begin() + ; cur != foot.pads.end() ; ++ cur) { + fprintf(fpcb, "Pad[%ld %ld %ld %ld %d %d %d \"%s\" \"%s\" \"%s\"]\n", + cur->second.rx1, + cur->second.ry1, + cur->second.rx2, + cur->second.ry2, + cur->second.thickness, + cur->second.clearance, + cur->second.mask, + cur->second.name.c_str(), + cur->second.number.c_str(), + cur->second.sflags.c_str()); + } + + fprintf(fpcb, ")\n"); }