/* C-PCB Copyright (C) 2015 Chris Hinsley chris (dot) hinsley (at) gmail (dot) 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 "router.h" #include "math.h" #include #include #include #include #include struct tree { std::string m_value; std::vector m_branches; }; struct pin { std::string m_name; std::string m_form; double m_x; double m_y; double m_angle; }; struct component { std::string m_name; std::map m_pin_map; }; struct instance { std::string m_name; std::string m_comp; std::string m_side; double m_x; double m_y; double m_angle; }; struct rule { double m_radius; double m_gap; }; struct circuit { std::string m_via; rule m_rule; }; struct padstack { points_2d m_shape; rule m_rule; }; std::vector &split(const std::string &s, char delim, std::vector &elems) { std::stringstream ss(s); std::string item; while (std::getline(ss, item, delim)) { elems.push_back(item); } return elems; } std::vector split(const std::string &s, char delim) { std::vector elems; split(s, delim, elems); return elems; } auto shape_to_cords(const points_2d &shape, double a1, double a2) { auto cords = points_2d{}; auto rads = fmod(a1+a2, 2*M_PI); auto s = sin(rads); auto c = cos(rads); for (auto &p : shape) { auto px = double(c*p.m_x - s*p.m_y); auto py = double(s*p.m_x + c*p.m_y); cords.push_back(point_2d{px, py}); } return cords; } //read input till given byte appears auto read_until(std::istream &in, char c) { char input; while (in.get(input)) { if (input == c) return false; } return true; } //read whitespace auto read_whitespace(std::istream &in) { for (;;) { auto b = in.peek(); if (b != '\t' && b != '\n' && b != '\r' && b != ' ') break; char c; in.get(c); } } auto read_node_name(std::istream &in) { std::string s; for (;;) { auto b = in.peek(); if (b == '\t' || b == '\n' || b == '\r' || b == ' ' || b == ')') break; char c; in.get(c); s.push_back(c); } return s; } auto read_string(std::istream &in) { std::string s; for (;;) { auto b = in.peek(); if (b == '\t' || b == '\n' || b == '\r' || b == ' ' || b == ')') break; char c; in.get(c); s.push_back(c); } return tree{s, {}}; } auto read_quoted_string(std::istream &in) { std::string s; for (;;) { auto b = in.peek(); if (b == '"') break; char c; in.get(c); s.push_back(c); } return tree{s, {}}; } tree read_tree(std::istream &in) { read_until(in, '('); read_whitespace(in); auto t = tree{read_node_name(in), {}}; for (;;) { read_whitespace(in); auto b = in.peek(); char c; if (b == EOF) break; if (b == ')') { in.get(c); break; } if (b == '(') { t.m_branches.push_back(read_tree(in)); continue; } if (b == '"') { in.get(c); t.m_branches.push_back(read_quoted_string(in)); in.get(c); continue; } t.m_branches.push_back(read_string(in)); } return t; } tree *search_tree(tree &t, const char *s) { if (t.m_value == s) return &t; for (auto &ct : t.m_branches) { auto st = search_tree(ct, s); if (st != nullptr) return st; } return nullptr; } void print_tree(const tree &t, int indent) { if (!t.m_value.empty()) { for (auto i = 0; i < indent; ++i) std::cout << " "; std::cout << t.m_value << '\n'; } for (auto &ct : t.m_branches) print_tree(ct, indent+1); } void ss_reset(std::stringstream &ss, const std::string &s) { ss.str(s); ss.clear(); } void get_value(std::stringstream &ss, std::vector::iterator t, int &x) { ss_reset(ss, t->m_value); ss >> x; } void get_value(std::stringstream &ss, std::vector::iterator t, double &x) { ss_reset(ss, t->m_value); ss >> x; } void get_2d(std::stringstream &ss, std::vector::iterator t, double &x, double &y) { get_value(ss, t, x); get_value(ss, t + 1, y); } void get_rect(std::stringstream &ss, std::vector::iterator t, double &x1, double &y1, double &x2, double &y2) { get_2d(ss, t, x1, y1); get_2d(ss, t + 2, x2, y2); } int main(int argc, char *argv[]) { //process comand args auto use_file = false; std::ifstream arg_infile; auto arg_b = 1; std::stringstream ss; for (auto i = 1; i < argc; ++i) { if (argv[i][0] == '-') { //option std::string opt = argv[i]; while (!opt.empty() && opt[0] == '-') opt.erase(0, 1); if (++i >= argc) goto help; ss_reset(ss, argv[i]); if (opt == "b") ss >> arg_b; else { help: std::cout << "c_pcb_dsn [switches] [filename]\neg. c_pcb_dsn -b 6 test1.dsn\n"; std::cout << "reads from stdin if no filename.\n"; std::cout << "-b: border gap, default 1\n"; exit(0); } } else { //filename arg_infile.open(argv[i], std::ifstream::in); use_file = true; } } //reading from stdin or file std::istream &in = use_file ? arg_infile : std::cin; //create tree from input auto tree = read_tree(in); auto structure_root = search_tree(tree, "structure"); auto layer_to_index_map = std::map{}; auto layer_to_keepout_map = std::map{}; const auto units = 1000.0; auto num_layers = 0; auto minx = double(1000000.0); auto miny = double(1000000.0); auto maxx = double(-1000000.0); auto maxy = double(-1000000.0); auto default_rule = rule{0.25, 0.25}; auto default_via = std::string{"Via[0-1]_600:400_um"}; auto track_id = 0; for (auto &&structure_node : structure_root->m_branches) { if (structure_node.m_value == "layer") { num_layers++; auto layer_index = 0; auto layer_name = structure_node.m_branches[0].m_value; for (auto &&layer_node : structure_node.m_branches) { if (layer_node.m_value == "property") { for (auto &&property_node : layer_node.m_branches) { if (property_node.m_value == "index") { get_value(ss, begin(property_node.m_branches), layer_index); } } } } layer_to_index_map[layer_name] = layer_index; } else if (structure_node.m_value == "via") { for (auto &&via_node : structure_node.m_branches) { default_via = via_node.m_value; } } else if (structure_node.m_value == "rule") { for (auto &&rule_node : structure_node.m_branches) { if (rule_node.m_value == "width") { get_value(ss, begin(rule_node.m_branches), default_rule.m_radius); default_rule.m_radius /= (2 * units); } else if ((rule_node.m_value == "clear" || rule_node.m_value == "clearance") && rule_node.m_branches.size() == 1) { get_value(ss, begin(rule_node.m_branches), default_rule.m_gap); default_rule.m_gap /= (2 * units); } } } else if (structure_node.m_value == "boundary") { for (auto &&boundary_node : structure_node.m_branches) { if (boundary_node.m_value == "path") { for (auto cords = begin(boundary_node.m_branches) + 2; cords != end(boundary_node.m_branches); cords += 2) { double px, py; get_2d(ss, cords, px, py); px /= units; py /= -units; minx = std::min(px, minx); maxx = std::max(px, maxx); miny = std::min(py, miny); maxy = std::max(py, maxy); } } else if (boundary_node.m_value == "rect") { double x1, y1, x2, y2; get_rect(ss, begin(boundary_node.m_branches) + 1, x1, y1, x2, y2); x1 /= units; y1 /= -units; x2 /= units; y2 /= -units; minx = std::min(x1, minx); maxx = std::max(x1, maxx); miny = std::min(y1, miny); maxy = std::max(y1, maxy); minx = std::min(x2, minx); maxx = std::max(x2, maxx); miny = std::min(y2, miny); maxy = std::max(y2, maxy); } } } else if (structure_node.m_value == "keepout") { for (auto &&keepout_node : structure_node.m_branches) { if (keepout_node.m_value == "polygon") { auto layer = keepout_node.m_branches[0].m_value; auto p = path{}; for (auto cords = begin(keepout_node.m_branches) + 2; cords != end(keepout_node.m_branches); cords += 2) { double px, py; get_2d(ss, cords, px, py); px /= units; py /= -units; p.emplace_back(point_3d(px, py, 0.0)); minx = std::min(px, minx); maxx = std::max(px, maxx); miny = std::min(py, miny); maxy = std::max(py, maxy); } p.push_back(p.front()); layer_to_keepout_map[layer].push_back(p); } } } } auto library_root = search_tree(tree, "library"); auto name_to_component_map = std::map{}; auto name_to_padstack_map = std::map>{}; for (auto &&library_node : library_root->m_branches) { if (library_node.m_value == "image") { auto component_name = library_node.m_branches[0].m_value; auto the_comp = component{component_name, std::map{}}; for (auto image_node = begin(library_node.m_branches) + 1; image_node != end(library_node.m_branches); ++image_node) { if (image_node->m_value == "pin") { auto the_pin = pin{}; the_pin.m_form = image_node->m_branches[0].m_value; if (image_node->m_branches[1].m_value == "rotate") { get_value(ss, begin(image_node->m_branches[1].m_branches), the_pin.m_angle); the_pin.m_name = image_node->m_branches[2].m_value; get_2d(ss, begin(image_node->m_branches) + 3, the_pin.m_x, the_pin.m_y); the_pin.m_angle *= (M_PI / 180.0); } else { the_pin.m_angle = 0.0; the_pin.m_name = image_node->m_branches[1].m_value; get_2d(ss, begin(image_node->m_branches) + 2, the_pin.m_x, the_pin.m_y); } the_pin.m_x /= units; the_pin.m_y /= -units; the_comp.m_pin_map[the_pin.m_name] = the_pin; } } name_to_component_map[component_name] = the_comp; } else if (library_node.m_value == "padstack") { for (auto padstack_node = begin(library_node.m_branches) + 1; padstack_node != end(library_node.m_branches); ++padstack_node) { if (padstack_node->m_value == "shape") { auto points = points_2d{}; auto the_rule = default_rule; if (padstack_node->m_branches[0].m_value == "circle") { get_value(ss, begin(padstack_node->m_branches[0].m_branches) + 1, the_rule.m_radius); the_rule.m_radius /= (2 * units); } else if (padstack_node->m_branches[0].m_value == "path") { get_value(ss, begin(padstack_node->m_branches[0].m_branches) + 1, the_rule.m_radius); the_rule.m_radius /= (2 * units); double x1, y1, x2, y2; get_rect(ss, begin(padstack_node->m_branches[0].m_branches) + 2, x1, y1, x2, y2); if (x1 != 0.0 || x2 != 0.0 || y1 != 0.0 || y2 != 0.0) { x1 /= units; y1 /= -units; x2 /= units; y2 /= -units; points.push_back(point_2d{x1, y1}); points.push_back(point_2d{x2, y2}); } } else if (padstack_node->m_branches[0].m_value == "rect") { the_rule.m_radius = 0.0; double x1, y1, x2, y2; get_rect(ss, begin(padstack_node->m_branches[0].m_branches) + 1, x1, y1, x2, y2); x1 /= units; y1 /= -units; x2 /= units; y2 /= -units; points.push_back(point_2d{x1, y1}); points.push_back(point_2d{x2, y1}); points.push_back(point_2d{x2, y2}); points.push_back(point_2d{x1, y2}); points.push_back(point_2d{x1, y1}); } else if (padstack_node->m_branches[0].m_value == "polygon") { the_rule.m_radius = 0.0; for (auto poly_node = begin(padstack_node->m_branches[0].m_branches) + 2; poly_node != end(padstack_node->m_branches[0].m_branches); poly_node += 2) { double x1, y1; get_2d(ss, poly_node, x1, y1); x1 /= units; y1 /= -units; points.push_back(point_2d{x1, y1}); } points.push_back(points.front()); } auto layer_index = layer_to_index_map[padstack_node->m_branches[0].m_branches[0].m_value]; name_to_padstack_map[library_node.m_branches[0].m_value][layer_index] = padstack{points, the_rule}; } } } } auto placement_root = search_tree(tree, "placement"); auto name_to_instance_map = std::map{}; for (auto &&placement_node : placement_root->m_branches) { if (placement_node.m_value == "component") { auto component_name = placement_node.m_branches[0].m_value; for (auto component_node = begin(placement_node.m_branches) + 1; component_node != end(placement_node.m_branches); ++component_node) { if (component_node->m_value == "place") { auto the_instance = instance{}; auto instance_name = component_node->m_branches[0].m_value; the_instance.m_name = instance_name; the_instance.m_comp = component_name; get_2d(ss, begin(component_node->m_branches) + 1, the_instance.m_x, the_instance.m_y); the_instance.m_side = component_node->m_branches[3].m_value; get_value(ss, begin(component_node->m_branches) + 4, the_instance.m_angle); the_instance.m_angle *= -(M_PI / 180.0); the_instance.m_x /= units; the_instance.m_y /= -units; name_to_instance_map[instance_name] = the_instance; } } } } auto all_pads = pads{}; for (auto &&inst : name_to_instance_map) { auto instance = inst.second; auto component = name_to_component_map[instance.m_comp]; for (auto &&p : component.m_pin_map) { auto pin = p.second; auto m_x = pin.m_x; auto m_y = pin.m_y; if (instance.m_side != "front") m_x = -m_x; auto s = sin(instance.m_angle); auto c = cos(instance.m_angle); auto px = double((c*m_x - s*m_y) + instance.m_x); auto py = double((s*m_x + c*m_y) + instance.m_y); for (auto &&p : name_to_padstack_map[pin.m_form]) { auto tp = point_3d{px, py, (double)p.first}; auto cords = shape_to_cords(p.second.m_shape, pin.m_angle, instance.m_angle); all_pads.push_back(pad{p.second.m_rule.m_radius, p.second.m_rule.m_gap, tp, cords}); for (auto &&p1 : cords) { auto x = p1.m_x + px; auto y = p1.m_y + py; minx = std::min(x - p.second.m_rule.m_radius - p.second.m_rule.m_gap, minx); maxx = std::max(x + p.second.m_rule.m_radius + p.second.m_rule.m_gap, maxx); miny = std::min(y - p.second.m_rule.m_radius - p.second.m_rule.m_gap, miny); maxy = std::max(y + p.second.m_rule.m_radius + p.second.m_rule.m_gap, maxy); } minx = std::min(px - p.second.m_rule.m_radius - p.second.m_rule.m_gap, minx); maxx = std::max(px + p.second.m_rule.m_radius + p.second.m_rule.m_gap, maxx); miny = std::min(py - p.second.m_rule.m_radius - p.second.m_rule.m_gap, miny); maxy = std::max(py + p.second.m_rule.m_radius + p.second.m_rule.m_gap, maxy); } } } auto network_root = search_tree(tree, "network"); auto name_to_circuit_map = std::map{}; for (auto &&network_node : network_root->m_branches) { if (network_node.m_value == "class") { auto the_circuit = circuit{default_via, default_rule}; for (auto &&class_node : network_node.m_branches) { if (class_node.m_value == "rule") { for (auto &&dims : class_node.m_branches) { if (dims.m_value == "width") { get_value(ss, begin(dims.m_branches), the_circuit.m_rule.m_radius); the_circuit.m_rule.m_radius /= (2 * units); } else if ((dims.m_value == "clearance" || dims.m_value == "clear") && dims.m_branches.size() == 1) { get_value(ss, begin(dims.m_branches), the_circuit.m_rule.m_gap); the_circuit.m_rule.m_gap /= (2 * units); } } } else if (class_node.m_value == "circuit") { for (auto &&circuit_node : class_node.m_branches) { if (circuit_node.m_value == "use_via") { the_circuit.m_via = circuit_node.m_branches[0].m_value; } } } } for (auto &&netname : network_node.m_branches) { if (netname.m_branches.empty()) name_to_circuit_map[netname.m_value] = the_circuit; } } } auto wiring_root = search_tree(tree, "wiring"); auto net_to_wires_map = std::map{}; for (auto &&wiring_node : wiring_root->m_branches) { if (wiring_node.m_value == "wire") { auto z = 0.0; auto radius = 0.0; auto wire = path{}; for (auto &&wire_node : wiring_node.m_branches) { if (wire_node.m_value == "path" || wire_node.m_value == "polyline_path") { z = layer_to_index_map[wire_node.m_branches[0].m_value]; get_value(ss, begin(wire_node.m_branches) + 1, radius); radius /= (2 * units); for (auto poly_node = begin(wire_node.m_branches) + 2; poly_node != end(wire_node.m_branches); poly_node += 2) { double x, y; get_2d(ss, poly_node, x, y); x /= units; y /= -units; wire.push_back(point_3d(x, y, z)); minx = std::min(x, minx); maxx = std::max(x, maxx); miny = std::min(y, miny); maxy = std::max(y, maxy); } } else if (wire_node.m_value == "net") { net_to_wires_map[wire_node.m_branches[0].m_value].emplace_back(std::move(wire)); } } } else if (wiring_node.m_value == "via") { double x, y; get_2d(ss, begin(wiring_node.m_branches) + 1, x, y); x /= units; y /= -units; minx = std::min(x, minx); maxx = std::max(x, maxx); miny = std::min(y, miny); maxy = std::max(y, maxy); for (auto wire_node = begin(wiring_node.m_branches) + 3; wire_node != end(wiring_node.m_branches); ++wire_node) { if (wire_node->m_value == "net") { net_to_wires_map[wire_node->m_branches[0].m_value].emplace_back( path{point_3d{x, y, 0}, point_3d{x, y, double(num_layers - 1)}}); } } } } auto the_tracks = tracks{}; for (auto &&network_node : network_root->m_branches) { if (network_node.m_value == "net") { for (auto &net_node : network_node.m_branches) { if (net_node.m_value == "pins") { auto the_pads = pads{}; for (auto &&p : net_node.m_branches) { auto pin_info = split(p.m_value, '-'); auto instance_name = pin_info[0]; auto pin_name = pin_info[1]; auto instance = name_to_instance_map[instance_name]; auto component = name_to_component_map[instance.m_comp]; auto pin = component.m_pin_map[pin_name]; auto m_x = pin.m_x; auto m_y = pin.m_y; if (instance.m_side != "front") m_x = -m_x; auto s = sin(instance.m_angle); auto c = cos(instance.m_angle); auto px = double((c*m_x - s*m_y) + instance.m_x); auto py = double((s*m_x + c*m_y) + instance.m_y); for (auto &&p : name_to_padstack_map[pin.m_form]) { auto tp = point_3d{px, py, (double)p.first}; auto cords = shape_to_cords(p.second.m_shape, pin.m_angle, instance.m_angle); auto term = pad{p.second.m_rule.m_radius, p.second.m_rule.m_gap, tp, cords}; the_pads.push_back(term); all_pads.erase(std::find(begin(all_pads), end(all_pads), term)); } } auto &&net_name = network_node.m_branches[0].m_value; auto &&net = name_to_circuit_map[net_name]; auto track_rule = net.m_rule; auto via_rule = name_to_padstack_map[net.m_via][0].m_rule; the_tracks.push_back(track{std::to_string(track_id++), track_rule.m_radius, via_rule.m_radius, track_rule.m_gap, the_pads, net_to_wires_map[net_name]}); } } } } //unused pads and the keepouts auto all_keepouts = paths{}; for (auto &&k : layer_to_keepout_map) { if (k.first == "signal") { for (auto &&l : layer_to_index_map) { auto z = (double)l.second; for (auto &&p : k.second) { for (auto &&c : p) c.m_z = z; all_keepouts.push_back(p); } } } else { auto z = (double)layer_to_index_map[k.first]; for (auto &&p : k.second) { for (auto &&c : p) c.m_z = z; all_keepouts.push_back(p); } } } the_tracks.push_back(track{std::to_string(track_id++), 0.0, 0.0, 0.0, all_pads, all_keepouts}); //output pcb format auto border = double(arg_b); std::cout << "[" << maxx - minx + border * 2 << "," << maxy - miny + border * 2 << "," << num_layers << "]\n"; minx -= border; miny -= border; for (auto &&track : the_tracks) { std::cout << "[" << track.m_id << "," << track.m_track_radius << "," << track.m_via_radius << "," << track.m_gap << ",["; for (auto i = 0; i < static_cast(track.m_pads.size()); ++i) { auto &&term = track.m_pads[i]; std::cout << "(" << term.m_radius << "," << term.m_gap << ",(" << term.m_pos.m_x - minx << "," << term.m_pos.m_y - miny << "," << term.m_pos.m_z << "),("; for (auto j = 0; j < static_cast(term.m_shape.size()); ++j) { auto cord = term.m_shape[j]; std::cout << "(" << cord.m_x << "," << cord.m_y << ")"; } std::cout << "])"; } std::cout << "],["; for (auto i = 0; i < static_cast(track.m_paths.size()); ++i) { std::cout << "("; auto &&p = track.m_paths[i]; for (auto j = 0; j < static_cast(p.size()); ++j) { std::cout << "(" << p[j].m_x - minx << "," << p[j].m_y - miny << "," << p[j].m_z << ")"; } std::cout << ")"; } std::cout << "]]\n"; } }