/*
* hyp2mat - convert hyperlynx files to matlab scripts
* Copyright 2012 Koen De Vleeschauwer.
*
* This file is part of hyp2mat.
*
* 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 3 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, see .
*/
#include
#include
#include
#include "hypfile.h"
using namespace std;
using namespace HypFile;
/*
* Hyperlynx 'STACKUP' record.
* Defines pcb layers.
*/
/*
* Hyperlynx 'OPTIONS' subrecord of STACKUP record.
* Defines dielectric constant and loss tangent of metal layers.
*/
bool HypFile::Hyp::exec_options(parse_param& h)
{
if (trace_hyp) cerr << "options: use_die_for_metal = " << h.use_die_for_metal << endl;
use_die_for_metal = h.use_die_for_metal;
return false;
}
/*
* Hyperlynx 'SIGNAL' subrecord of 'STACKUP' record.
* Defines a pcb signal layer.
*/
bool HypFile::Hyp::exec_signal(parse_param& h)
{
if (trace_hyp)
cerr << "signal:";
trace_layer(h);
add_metal_layer(h);
/*
*The difference between signal and plane layers is
* - signal layers are drawn positive, plane layers are drawn negative
* - signal layers need a name
*/
stackup.back().layer_type = LAYER_SIGNAL;
if (!h.layer_name_set)
error("missing signal layer name" );
calc_layer_position();
calc_layer_epsilon();
return false;
}
/*
* Hyperlynx 'DIELECTRIC' subrecord of 'STACKUP' record.
* Defines a pcb dielectric layer.
*/
bool HypFile::Hyp::exec_dielectric(parse_param& h)
{
if (trace_hyp)
cerr << "dielectric:";
trace_layer(h);
add_dielectric_layer(h);
calc_layer_position();
calc_layer_epsilon();
return false;
}
/*
* Hyperlynx 'PLANE' subrecord of 'STACKUP' record.
* Defines a pcb power or signal plane.
*/
bool HypFile::Hyp::exec_plane(parse_param& h)
{
if (trace_hyp)
cerr << "plane:";
trace_layer(h);
add_metal_layer(h);
stackup.back().layer_type = LAYER_PLANE;
calc_layer_position();
calc_layer_epsilon();
return false;
}
/*
* Debug output for layers
*/
bool HypFile::Hyp::trace_layer(parse_param& h)
{
if (trace_hyp) {
if (h.thickness_set) cerr << " thickness = " << h.thickness;
if (h.plating_thickness_set) cerr << " plating_thickness = " << h.plating_thickness;
if (h.bulk_resistivity_set) cerr << " bulk_resistivity = " << h.bulk_resistivity;
if (h.temperature_coefficient_set) cerr << " temperature_coefficient = " << h.temperature_coefficient;
if (h.epsilon_r_set) cerr << " epsilon_r = " << h.epsilon_r;
if (h.loss_tangent_set) cerr << " loss_tangent = " << h.loss_tangent;
if (h.conformal_set) cerr << " conformal = " << h.conformal;
if (h.prepreg_set) cerr << " prepreg = " << h.prepreg;
if (h.layer_name_set) cerr << " layer_name = " << h.layer_name;
if (h.material_name_set) cerr << " material_name = " << h.material_name;
if (h.plane_separation_set) cerr << " plane_separation = " << h.plane_separation;
cerr << endl;
}
return true;
}
/*
* Add a signal or plane layer.
*/
bool HypFile::Hyp::add_metal_layer(parse_param& h)
{
Layer l;
l.layer_type = LAYER_SIGNAL;
if (h.thickness_set)
l.thickness = h.thickness * metal_thickness_unit;
else {
error ("thickness missing");
return true;
}
if (h.plating_thickness_set)
l.plating_thickness = h.plating_thickness * metal_thickness_unit;
else
l.plating_thickness = 0;
if (h.bulk_resistivity_set)
l.bulk_resistivity = h.bulk_resistivity;
else
l.bulk_resistivity = copper_bulk_resistivity;
if (h.temperature_coefficient_set)
l.temperature_coefficient = h.temperature_coefficient;
else
l.temperature_coefficient = copper_temperature_coefficient;
if (h.conformal_set) {
error("only dielectric layers conformal");
return true;
}
else
l.conformal = false;
if (h.epsilon_r_set)
l.epsilon_r = h.epsilon_r;
else
l.epsilon_r = fr4_epsilon_r;
if (h.loss_tangent_set)
l.loss_tangent = h.loss_tangent;
else
l.loss_tangent = fr4_loss_tangent;
// use use_die_for_metal ? XXX
if (h.prepreg_set) {
error("only dielectric layers prepreg");
return true;
}
else
l.prepreg = false;
if (h.layer_name_set)
l.layer_name = h.layer_name;
else {
error("missing signal layer name" );
l.layer_name = ""; // Continue anyhow
}
if (h.material_name_set)
l.material_name = h.material_name;
else
l.material_name = "";
if (h.plane_separation_set)
l.plane_separation = h.plane_separation * unit;
else
l.plane_separation = board.plane_separation;
// Add layer to stackup
stackup.push_back(l);
return false;
}
/*
* add a substrate layer
*/
bool HypFile::Hyp::add_dielectric_layer(parse_param& h)
{
Layer l;
l.layer_type = LAYER_DIELECTRIC;
if (h.thickness_set)
l.thickness = h.thickness * unit;
else {
error ("thickness missing");
return true;
}
if (h.plating_thickness_set) {
error ("dielectric has no plating thickness");
return true;
}
else
l.plating_thickness = 0;
if (h.bulk_resistivity_set) {
error ("dielectric has no bulk resistivity");
return true;
}
else
l.bulk_resistivity = 0;
if (h.temperature_coefficient_set) {
error ("dielectric has no bulk resistivity temperature coefficient");
return true;
}
else
l.temperature_coefficient = 0;
if (h.conformal_set)
l.conformal = h.conformal;
else
l.conformal = false;
if (h.epsilon_r_set)
l.epsilon_r = h.epsilon_r;
else if (l.conformal)
l.epsilon_r = conformal_epsilon_r;
else
l.epsilon_r = fr4_epsilon_r;
if (h.loss_tangent_set)
l.loss_tangent = h.loss_tangent;
else
l.loss_tangent = fr4_loss_tangent;
if (h.prepreg_set)
l.prepreg = h.prepreg;
else
l.prepreg = true;
if (h.layer_name_set)
l.layer_name = h.layer_name;
else
l.layer_name = "";
if (h.material_name_set)
l.material_name = h.material_name;
else
l.material_name = "";
if (h.plane_separation_set) {
error ("dielectric has no plane separation");
return true;
}
else
l.plane_separation = 0;
// Add layer to stackup
stackup.push_back(l);
return false;
}
/*
* Recalculate vertical position of all layers.
* Layers are ordered top first, bottom last.
* Called when a new layer is added.
*/
bool HypFile::Hyp::calc_layer_position()
{
// calculate vertical position, from bottom to top
double z_top = 0; // current vertical position
for (LayerList::reverse_iterator it = stackup.rbegin(); it != stackup.rend(); ++it) {
if (it->layer_type == LAYER_DIELECTRIC) {
/* substrate and prepreg */
it->z0 = z_top;
it->z1 = z_top + it->thickness;
z_top = it->z1;
}
else {
/* metal layers */
it->z0 = z_top;
it->z1 = z_top;
}
}
return true;
}
bool HypFile::Hyp::calc_layer_epsilon()
{
/* no calculation needed if dielectric constant have been specified */
if (use_die_for_metal)
return true;
/* calculate dielectric constants */
for (LayerList::iterator i = stackup.begin(); i != stackup.end(); ++i) {
/* skip dielectric layers */
if (i->layer_type == LAYER_DIELECTRIC)
continue;
/* find first dielectric layer above current metal layer */
bool upper_layer_found = false;
LayerList::iterator upper_layer;
for (LayerList::iterator j = stackup.begin(); j != i; ++j)
if (j->layer_type == LAYER_DIELECTRIC) {
upper_layer_found = true;
upper_layer = j;
};
/* find first dielectric layer below current metal layer */
bool lower_layer_found = false;
LayerList::iterator lower_layer;
for (LayerList::iterator j = i; j != stackup.end(); ++j)
if (j->layer_type == LAYER_DIELECTRIC) {
lower_layer_found = true;
lower_layer = j;
break;
};
if (!upper_layer_found || !lower_layer_found) {
/* assume outer copper layers are in air */
i->epsilon_r = 1.0;
i->loss_tangent = 0.0;
}
else if (upper_layer->prepreg && !lower_layer->prepreg) {
/* upper layer is prepreg. use values of upper layer */
i->epsilon_r = upper_layer->epsilon_r;
i->loss_tangent = upper_layer->loss_tangent;
}
else if (!upper_layer->prepreg && lower_layer->prepreg) {
/* lower layer is prepreg. use values of lower layer */
i->epsilon_r = lower_layer->epsilon_r;
i->loss_tangent = lower_layer->loss_tangent;
}
else {
/* use average of upper and lower layer */
i->epsilon_r = (upper_layer->epsilon_r + lower_layer->epsilon_r)/2;
i->loss_tangent = (upper_layer->loss_tangent + lower_layer->loss_tangent) /2;
}
}
return false;
}
/*
* Flood specified layers with copper
*/
void HypFile::Hyp::flood_layers_with_copper()
{
/* check if we have to flood all plane layers */
bool flood_plane_layers = std::find(flood_layers.begin(), flood_layers.end(), "plane_layers") != flood_layers.end(); /* look for special value "plane_layers" */
for (LayerList::iterator l = stackup.begin(); l != stackup.end(); ++l) {
/* metallic layers only */
if (l->layer_type == LAYER_DIELECTRIC) continue;
/* check if we have to flood this layer */
bool flood_this_layer = std::find(flood_layers.begin(), flood_layers.end(), l->layer_name) != flood_layers.end();
/* flood with copper if we have to flood this layer, or if this is a plane layer and we have to flood all plane layers */
if (!(flood_this_layer || (flood_plane_layers && (l->layer_type == LAYER_PLANE)))) continue;
/* create default copper on plane layer */
Polygon default_copper;
default_copper.vertex = board.edge.front().vertex; /* polygon is board outline */
default_copper.polygon_type = POLYGON_TYPE_PLANE;
default_copper.net_name = l->layer_name; /* flood layer VCC with copper belonging to net VCC, layer GND with copper belonging to net GND */
default_copper.id = -1;
default_copper.positive = true;
default_copper.width = 0.0;
default_copper.plane_separation = -1.0;
default_copper.left_plane_separation = -1.0;
default_copper.layer_name = l->layer_name;
/* Look up net with same name as layer */
NetList::iterator n;
for (n = net.begin(); n != net.end(); ++n)
if (n->net_name == l->layer_name) {
add_polygon_to_net(*n, default_copper);
break;
}
/* Net with same name as layer not found. Create net */
if (n == net.end()) {
Net plane_net;
plane_net.net_name = l->layer_name;
plane_net.plane_separation = -1.0;
net.push_back(plane_net);
add_polygon_to_net(net.back(), default_copper);
}
}
return;
}
/* not truncated */