public int HandlePreRoutedAsm(ComponentAssembly obj, int pkg_idx) { if (CodeGenerator.preRouted.ContainsKey(obj)) { var layoutBox = (obj.Impl.Impl as GME.MGA.MgaFCO).RegistryValue["layoutBox"]; // format x1,y1,w1,h1; x2,y2,w2,h2; x3,y3,w3,h3 // NOTE: if there are multiple bounding boxes // - we expect the first one to be at the origin of the reference frame // - this minor limitation would be easy to address later by adding code to identify // - the bounding box at origin even if its not the first one var bBoxs = layoutBox.Split(';'); int bbidx = 0; int origPkgIdx = 0; foreach (var bbox in bBoxs) { Package pkg = new Package(); pkg.pkg_idx = pkg_idx++; pkg.name = obj.Name; pkg.package = "__spaceClaim__"; pkg.ComponentID = (obj.Impl.Impl as GME.MGA.MgaFCO) .RegistryValue["Elaborator/InstanceGUID_Chain"]; var pts = bbox.Split(','); if (pts.Length >= 4) { double d = Double.NaN; if (Double.TryParse(pts[0], out d)) pkg.x = d; if (Double.TryParse(pts[1], out d)) pkg.y = d; if (Double.TryParse(pts[2], out d)) pkg.width = d; if (Double.TryParse(pts[3], out d)) pkg.height = d; } if (bbidx == 0) { preroutedPkgMap.Add(obj, pkg); origPkgIdx = (int)pkg.pkg_idx; } else { pkg.ComponentID += "." + bbidx.ToString(); // add constraint relative to first bounding box var pcons = new RelativeConstraint(); pcons.type = "relative-pkg"; pcons.x = pkg.x; pcons.y = pkg.y; pcons.pkg_idx = origPkgIdx; if (pkg.constraints == null) pkg.constraints = new List<Constraint>(); pkg.constraints.Add(pcons); } boardLayout.packages.Add(pkg); bbidx++; } // TBD SKN figure out relative constraint between multiple bboxes } foreach (var ca in obj.ComponentAssemblyInstances) { pkg_idx = HandlePreRoutedAsm(ca, pkg_idx); } return pkg_idx; }
public LayoutGenerator(Eagle.schematic sch_obj, TestBench tb_obj, GMELogger inLogger) { this.Logger = inLogger; this.pkgPartMap = new Dictionary<Package, Eagle.part>(); this.partPkgMap = new Dictionary<Eagle.part, Package>(); this.preroutedPkgMap = new Dictionary<ComponentAssembly, Package>(); boardLayout = new LayoutJson.Layout(); var bw = tb_obj.Parameters.Where(p => p.Name.Equals("boardWidth")).FirstOrDefault(); var bh = tb_obj.Parameters.Where(p => p.Name.Equals("boardHeight")).FirstOrDefault(); try { boardLayout.boardWidth = (bw != null) ? Convert.ToDouble(bw.Value) : 40.0; boardLayout.boardHeight = (bh != null) ? Convert.ToDouble(bh.Value) : 40.0; } catch (FormatException ex) { Logger.WriteWarning("Exception while reading board dimensions: {0}", ex.Message); } boardLayout.numLayers = 2; { // Look to see if there is a PCB component in the top level assembly var compImpl = tb_obj.ComponentAssemblies.SelectMany(c => c.ComponentInstances).Select(i => i.Impl) .Where(j => (j as Tonka.Component).Attributes.Classifications .Contains("pcb_board")).FirstOrDefault(); // Look to see if it has a resource labeled 'BoardTemplate' var comp = (compImpl != null) ? compImpl as Tonka.Component : null; var btRes = (comp != null) ? comp.Children.ResourceCollection.Where(r => r.Name.ToUpper().Contains("BOARDTEMPLATE")).FirstOrDefault() : null; if (btRes != null) { var btPath = Path.Combine(comp.Attributes.Path, btRes.Attributes.Path); boardLayout.boardTemplate = Path.GetFileName(btPath); } } // the prior code looks up for a boardTemplate file associated with a PCB component // the users can override it with a boardTemplate file specified as a testbench parameter { var bt = tb_obj.Parameters.Where(p => p.Name.Equals("boardTemplate")).FirstOrDefault(); // if its a file path - we only want to pass the file name to board synthesis if (bt != null) try { boardLayout.boardTemplate = Path.GetFileName(bt.Value); } catch (System.ArgumentException ex) { CodeGenerator.Logger.WriteError("Error extracting boardTemplate filename: {0}", ex.Message); } } // the users also can specify a designRule file as a testbench parameter { var dr = tb_obj.Parameters.Where(p => p.Name.Equals("designRules")).FirstOrDefault(); if (dr != null) try { boardLayout.designRules = Path.GetFileName(dr.Value); } catch (System.ArgumentException ex) { CodeGenerator.Logger.WriteError("Error extracting designRules filename: {0}", ex.Message); } } boardLayout.packages = new List<Package>(); int pkg_idx = 0; // we want to handle prerouted assemblies as follows // 1) all parts that are part of the pre-routed assembly are tagged with a 'virtual' spaceClaim part // 2) we add their absolute location (in the subckt frame of refernece) in the output json // 3) we create virtual spaceClaim parts for pre-routed subcircuits // 4) all nets belonging to the pre-routed subcircuit are tagged with the spaceClaim part // 5) these nets are added to json with absolute location (same as parts above) foreach (var ca in tb_obj.ComponentAssemblies) { pkg_idx = HandlePreRoutedAsm(ca, pkg_idx); } // compute part dimensions from foreach (var part in sch_obj.parts.part) { var dev = sch_obj.libraries.library.Where(l => l.name.Equals(part.library)). SelectMany(l => l.devicesets.deviceset).Where(ds => ds.name.Equals(part.deviceset)). SelectMany(ds => ds.devices.device).Where(d => d.name.Equals(part.device)).FirstOrDefault(); var spkg = (dev != null) ? sch_obj.libraries.library.Where(l => l.name.Equals(part.library)) .SelectMany(l => l.packages.package).Where(p => p.name.Equals(dev.package)) .FirstOrDefault() : null; Package pkg = new Package(); pkg.name = part.name; pkg.pkg_idx = pkg_idx++; if (dev == null || spkg == null) { // emit warning Logger.WriteWarning("Unable to get package size for part - layout/chipfit results may be inaccurate: {0}", part.name); } else { pkg.package = spkg.name; // note that the eagle package information may be incomplete - should really have curated version from CyPhy #region ComputePackageSize var minX = Double.MaxValue; var minY = Double.MaxValue; var maxX = Double.MinValue; var maxY = Double.MinValue; foreach (Eagle.wire wire in spkg.Items.Where(s => s is Eagle.wire)) { if (wire == null) continue; if (!LayersForBoundingBoxComputation.Any(wire.layer.Contains)) continue; var x1 = Convert.ToDouble(wire.x1); var x2 = Convert.ToDouble(wire.x2); var y1 = Convert.ToDouble(wire.y1); var y2 = Convert.ToDouble(wire.y2); if (x1 < minX) minX = x1; if (x2 < minX) minX = x2; if (x1 > maxX) maxX = x1; if (x2 > maxX) maxX = x2; if (y1 < minY) minY = y1; if (y2 < minY) minY = y2; if (y1 > maxY) maxY = y1; if (y2 > maxY) maxY = y2; } foreach (Eagle.pad pad in spkg.Items.Where(s => s is Eagle.pad)) { if (pad == null) continue; var pad_num = pad.name; var x = Convert.ToDouble(pad.x); var y = Convert.ToDouble(pad.y); var drill = Convert.ToDouble(pad.drill); var dia = Convert.ToDouble(pad.diameter); var shape = pad.shape; // enum padShape {round, octagon, @long, offset} var rot = pad.rot; // { R90, R180, R270, ...} var r = 0.0; if (dia == 0.0) // JS: Workaround for no diameter present, estimate dia to be 2x drill size { dia = drill * 2.0; } if (shape == Eagle.padShape.@long) { // TODO: consider PAD rotation; for now, consider long pads are 2x diameter. dia *= 2.0; // diameter value from package desc is for the "short" side, for max calc, consider long side is 2x (by inspection) } r = dia / 2.0; if ((x - r) < minX) minX = x - r; if ((x + r) > maxX) maxX = x + r; if ((y - r) < minY) minY = y - r; if ((y + r) > maxY) maxY = y + r; } foreach (Eagle.circle circle in spkg.Items.Where(s => s is Eagle.circle)) { if (circle == null) continue; var x = Convert.ToDouble(circle.x); var y = Convert.ToDouble(circle.y); var r = Convert.ToDouble(circle.radius); if ((x - r) < minX) minX = x - r; if ((x + r) > maxX) maxX = x + r; if ((y - r) < minY) minY = y - r; if ((y + r) > maxY) maxY = y + r; } foreach (Eagle.smd smd in spkg.Items.Where(s => s is Eagle.smd)) { if (smd == null) continue; // SKN: after intense research on eagle, it seems that the way SMD pads are placed is as follows // dx - dy tells the length of the pad, while the x is the center point of the SMD pad var x = Convert.ToDouble(smd.x); var y = Convert.ToDouble(smd.y); var ddx = Convert.ToDouble(smd.dx); var ddy = Convert.ToDouble(smd.dy); var dx = ddx; // should be /2?? var dy = ddy; if (smd.rot.Equals("R90") || smd.rot.Equals("R270")) { // flip dx and dy if there is rotation dx = ddy; dy = ddx; } var x1 = x - dx / 2.0; var x2 = x + dx / 2.0; var y1 = y - dy / 2.0; var y2 = y + dy / 2.0; if (x1 < minX) minX = x1; if (x2 > maxX) maxX = x2; if (y1 < minY) minY = y1; if (y2 > maxY) maxY = y2; } pkg.width = maxX - minX; pkg.height = maxY - minY; pkg.originX = Math.Floor(10.0 * (maxX + minX) / 2.0) / 10.0; pkg.originY = Math.Floor(10.0 * (maxY + minY) / 2.0) / 10.0; #endregion // emit component ID for locating components in CAD assembly if (CodeGenerator.partComponentMap.ContainsKey(part)) { var comp_obj = CodeGenerator.partComponentMap[part]; var comp = comp_obj.Impl as Tonka.Component; // emit component ID for locating components in CAD assembly pkg.ComponentID = comp.Attributes.InstanceGUID; Boolean isMultiLayer = comp.Children.EDAModelCollection. Any(e => e.Attributes.HasMultiLayerFootprint); if (isMultiLayer) pkg.multiLayer = true; #region ExactConstraints // exact constraints related to this part var exactCons = from conn in comp.SrcConnections.ApplyExactLayoutConstraintCollection select conn.SrcEnds.ExactLayoutConstraint; foreach (var c in exactCons) { var pcons = new ExactConstraint(); pcons.type = "exact"; if (!c.Attributes.X.Equals("")) pcons.x = Convert.ToDouble(c.Attributes.X); if (!c.Attributes.Y.Equals("")) pcons.y = Convert.ToDouble(c.Attributes.Y); if (!c.Attributes.Layer.Equals("")) pcons.layer = Convert.ToInt32(c.Attributes.Layer); if (!c.Attributes.Rotation.Equals("")) pcons.rotation = Convert.ToInt32(c.Attributes.Rotation); if (pkg.constraints == null) pkg.constraints = new List<Constraint>(); pkg.constraints.Add(pcons); } #endregion #region RangeConstraints // range constraints related to this part var rangeCons = from conn in comp.SrcConnections.ApplyRangeLayoutConstraintCollection select conn.SrcEnds.RangeLayoutConstraint; foreach (var c in rangeCons) { var pcons = new RangeConstraint(); pcons.type = "range"; if (!c.Attributes.XRange.Equals("")) pcons.x = c.Attributes.XRange; if (!c.Attributes.YRange.Equals("")) pcons.y = c.Attributes.YRange; if (!c.Attributes.LayerRange.Equals("")) pcons.layer = c.Attributes.LayerRange; if (pkg.constraints == null) pkg.constraints = new List<Constraint>(); pkg.constraints.Add(pcons); } #endregion #region PreRoutedAssemblyPart // now handle if this component is part of a pre-routed sub-ckt if (CodeGenerator.preRouted.ContainsKey(comp_obj.Parent)) { var layoutParser = CodeGenerator.preRouted[comp_obj.Parent]; var prePkg = layoutParser.componentPackageMap[comp_obj]; pkg.x = prePkg.x; pkg.y = prePkg.y; pkg.rotation = prePkg.rotation; pkg.RelComponentID = (comp_obj.Parent.Impl.Impl as GME.MGA.MgaFCO) .RegistryValue["Elaborator/InstanceGUID_Chain"]; pkg.doNotPlace = true; } #endregion } } pkgPartMap.Add(pkg, part); // add to map partPkgMap.Add(part, pkg); boardLayout.packages.Add(pkg); } #region AddSignalsToBoardLayout boardLayout.signals = new List<Signal>(); foreach (var net in sch_obj.sheets.sheet.FirstOrDefault().nets.net) { // dump pre-routed signals to board file var sig = new Signal(); sig.name = net.name; sig.pins = new List<Pin>(); Signal preRouted = null; string preRoutedAsmID = null; // ID of the parent pre-routed assembly string preRoutedAsm = null; // name the parent pre-routed assembly bool onlyOnePreroute = true; foreach (var seg in net.segment.SelectMany(s => s.Items).Where(s => s is Eagle.pinref)) { bool isPrerouted = false; var pr = seg as Eagle.pinref; var pin = new Pin(); pin.name = pr.pin; pin.gate = pr.gate; pin.package = pr.part.ToUpper(); // find package and pad var part = sch_obj.parts.part.Where(p => p.name.Equals(pr.part)).FirstOrDefault(); var dev = (part != null) ? sch_obj.libraries.library.Where(l => l.name.Equals(part.library)). SelectMany(l => l.devicesets.deviceset).Where(ds => ds.name.Equals(part.deviceset)). SelectMany(ds => ds.devices.device).Where(d => d.name.Equals(part.device)).FirstOrDefault() : null; var pad = (dev != null) ? dev.connects.connect.Where(c => c.gate.Equals(pr.gate) && c.pin.Equals(pr.pin)).Select(c => c.pad).FirstOrDefault() : null; if (pad != null) pin.pad = pad; // check for preroutes - all pins in this signal should be in the prerouted net, otherwise reject it if (part != null && CodeGenerator.partComponentMap.ContainsKey(part)) { var comp_obj = CodeGenerator.partComponentMap[part]; if (CodeGenerator.preRouted.ContainsKey(comp_obj.Parent)) // is the parent assembly prerouted { Logger.WriteDebug("Net {0} in Prerouted Asm {1}", net.name, comp_obj.Parent.Name); var layoutParser = CodeGenerator.preRouted[comp_obj.Parent]; // find the name/gate matching port in schematic domain model var sch = comp_obj.Impl.Children.SchematicModelCollection.FirstOrDefault(); var port = (sch != null) ? sch.Children.SchematicModelPortCollection. Where(p => p.Attributes.EDAGate == pin.gate && p.Name == pin.name). FirstOrDefault() : null; // find the buildPort var buildPort = port != null && CyPhyBuildVisitor.Ports.ContainsKey(port.ID) ? CyPhyBuildVisitor.Ports[port.ID] : null; if (buildPort != null && layoutParser.portTraceMap.ContainsKey(buildPort)) { isPrerouted = true; Logger.WriteDebug("Found build Port and Associated Trace"); if (preRouted == null) { preRouted = layoutParser.portTraceMap[buildPort]; preRoutedAsmID = (comp_obj.Parent.Impl.Impl as GME.MGA.MgaFCO) .RegistryValue["Elaborator/InstanceGUID_Chain"]; preRoutedAsm = comp_obj.Parent.Name; } else if (preRouted != layoutParser.portTraceMap[buildPort]) isPrerouted = false; } } } onlyOnePreroute = onlyOnePreroute && isPrerouted; // add pin to list of pins for this signal sig.pins.Add(pin); } // if pre-routed then copy wires if (onlyOnePreroute && preRouted != null) { Logger.WriteInfo("Prerouted net {0}, from assembly {1}, originally as {2}", net.name, preRoutedAsm, preRouted.name); sig.wires = new List<Wire>(); sig.vias = new List<Via>(); sig.RelComponentID = preRoutedAsmID; foreach (var w in preRouted.wires) sig.wires.Add(w); foreach (var v in preRouted.vias) sig.vias.Add(v); } boardLayout.signals.Add(sig); } #endregion #region AddRelativeConstraintsToBoardLayout // now process relative constraints - they require that all parts be mapped to packages already for (int i = 0; i < boardLayout.packages.Count; i++) { var pkg = boardLayout.packages[i]; if (!pkgPartMap.ContainsKey(pkg)) continue; var part = pkgPartMap[pkg]; var comp = CodeGenerator.partComponentMap.ContainsKey(part) ? CodeGenerator.partComponentMap[part] : null; var impl = comp != null ? comp.Impl as Tonka.Component : null; var relCons = from conn in impl.SrcConnections.ApplyRelativeLayoutConstraintCollection select conn.SrcEnds.RelativeLayoutConstraint; foreach (var c in relCons) { var pcons = new RelativeConstraint(); pcons.type = "relative-pkg"; if (!c.Attributes.XOffset.Equals("")) pcons.x = Convert.ToDouble(c.Attributes.XOffset); if (!c.Attributes.YOffset.Equals("")) pcons.y = Convert.ToDouble(c.Attributes.YOffset); // find origin comp var origCompImpl = (from conn in c.SrcConnections.RelativeLayoutConstraintOriginCollection select conn.SrcEnds.Component).FirstOrDefault(); var origComp = ((origCompImpl != null) && CyPhyBuildVisitor.Components.ContainsKey(origCompImpl.ID)) ? CyPhyBuildVisitor.Components[origCompImpl.ID] : null; var origPart = ((origComp != null) && CodeGenerator.componentPartMap.ContainsKey(origComp)) ? CodeGenerator.componentPartMap[origComp] : null; var origPkg = ((origPart != null) && partPkgMap.ContainsKey(origPart)) ? partPkgMap[origPart] : null; pcons.pkg_idx = origPkg.pkg_idx.Value; if (pkg.constraints == null) pkg.constraints = new List<Constraint>(); pkg.constraints.Add(pcons); boardLayout.packages[i] = pkg; } } #endregion }
static void RotateAndTranslate(Package refPkg, double x, double y, out double rx, out double ry) { // point-wise translate the center of the part // corresponding to frame rotation double theta = (double)refPkg.rotation * Math.PI / 2.0; rx = x * Math.Cos(theta) - y * Math.Sin(theta); ry = x * Math.Sin(theta) + y * Math.Cos(theta); if (refPkg.rotation == 1) rx += refPkg.height; else if (refPkg.rotation == 2) { rx += refPkg.width; ry += refPkg.height; } else if (refPkg.rotation == 3) ry += refPkg.width; // now translate rx += (double)refPkg.x; ry += (double)refPkg.y; }