Skip to content

Commit 57007a0

Browse files
board: add blind and buried vias
changelog: New Features/Board Editor: add support for blind and buried vias
1 parent 7d05a8c commit 57007a0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+1228
-370
lines changed

Diff for: Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ SRC_COMMON = \
5050
src/pool/padstack.cpp\
5151
src/common/polygon.cpp\
5252
src/util/polygon_arc_removal_proxy.cpp\
53+
src/util/layer_range.cpp\
5354
src/common/hole.cpp\
5455
src/common/shape.cpp\
5556
src/common/patch_type_names.cpp\

Diff for: scripts/app_versions.yml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ versions:
1818
16: support relative paths for including boards
1919
17: save plane fragements to a separate file
2020
18: increase maximum number of inner layers to 8
21+
19: add blind and buried vias
2122
schematic:
2223
1: add custom values on symbols
2324
2: add hierarchy

Diff for: src/board/board.cpp

+37-15
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "util/bbox_accumulator.hpp"
1313
#include "util/polygon_arc_removal_proxy.hpp"
1414
#include <filesystem>
15+
#include <range/v3/view.hpp>
1516

1617
namespace horizon {
1718

@@ -42,7 +43,7 @@ const LutEnumStr<Board::OutputFormat> Board::output_format_lut = {
4243
{"odb", Board::OutputFormat::ODB},
4344
};
4445

45-
static const unsigned int app_version = 18;
46+
static const unsigned int app_version = 19;
4647

4748
unsigned int Board::get_app_version()
4849
{
@@ -465,19 +466,25 @@ void Board::update_junction_connections()
465466
it.connected_vias.clear();
466467
it.connected_net_ties.clear();
467468
it.has_via = false;
468-
it.needs_via = false;
469+
it.required_span = {};
469470
}
470471
for (auto &[uu, it] : tracks) {
471472
for (const auto &it_ft : {it.from, it.to}) {
472473
if (it_ft.is_junc()) {
473474
auto &ju = *it_ft.junc;
474475
ju.connected_tracks.push_back(uu);
476+
ju.required_span.merge(it.layer);
475477
ju.layer.merge(it.layer);
476-
if (ju.layer.is_multilayer())
477-
ju.needs_via = true;
478478
}
479479
}
480480
}
481+
for (auto &[uu, it] : net_ties) {
482+
for (auto &it_ft : {&it.from, &it.to}) {
483+
(*it_ft)->connected_net_ties.push_back(uu);
484+
(*it_ft)->required_span.merge(it.layer);
485+
(*it_ft)->layer.merge(it.layer);
486+
}
487+
}
481488

482489
JunctionUtil::update(lines);
483490
JunctionUtil::update(arcs);
@@ -490,15 +497,9 @@ void Board::update_junction_connections()
490497
}
491498
for (auto &[uu, it] : vias) {
492499
it.junction->has_via = true;
493-
it.junction->layer = LayerRange(BoardLayers::TOP_COPPER, BoardLayers::BOTTOM_COPPER);
500+
it.junction->layer = it.span;
494501
it.junction->connected_vias.push_back(uu);
495502
}
496-
for (auto &[uu, it] : net_ties) {
497-
for (auto &it_ft : {&it.from, &it.to}) {
498-
(*it_ft)->connected_net_ties.push_back(uu);
499-
(*it_ft)->layer.merge(it.layer);
500-
}
501-
}
502503
}
503504

504505
unsigned int Board::get_n_inner_layers() const
@@ -739,7 +740,7 @@ void Board::expand_some()
739740
ParameterSet ps_hole = it.second.parameter_set;
740741
ps_hole.emplace(ParameterID::HOLE_SOLDER_MASK_EXPANSION, params.hole_solder_mask_expansion);
741742
it.second.padstack.apply_parameter_set(ps_hole);
742-
it.second.padstack.expand_inner(n_inner_layers);
743+
it.second.padstack.expand_inner(n_inner_layers, BoardLayers::layer_range_through);
743744
if (it.second.padstack.type == Padstack::Type::HOLE && it.second.net == nullptr) {
744745
warnings.emplace_back(it.second.placement.shift, "PTH hole without net");
745746
}
@@ -753,9 +754,21 @@ void Board::expand_some()
753754
}
754755
}
755756

756-
for (const auto &it : junctions) {
757-
if (it.second.needs_via && !it.second.has_via) {
758-
warnings.emplace_back(it.second.position, "Junction needs via");
757+
for (const auto &[uu, it] : junctions) {
758+
if (it.required_span.is_multilayer() && !it.has_via) {
759+
warnings.emplace_back(it.position, "Junction needs via");
760+
}
761+
}
762+
for (const auto &[uu, it] : vias) {
763+
// check that via's span is sufficient for required span
764+
765+
// merge required span and actual span
766+
auto sp = it.junction->required_span;
767+
sp.merge(it.span);
768+
769+
// if the resulting span is not the via's span it must have gotten extended by the required span
770+
if (sp != it.span) {
771+
warnings.emplace_back(it.junction->position, "Junction's via has insufficient span");
759772
}
760773
}
761774

@@ -1486,4 +1499,13 @@ Board::Outline Board::get_outline_and_errors() const
14861499
return get_outline(true);
14871500
}
14881501

1502+
std::set<LayerRange> Board::get_drill_spans() const
1503+
{
1504+
// only vias can add non-through spans
1505+
auto spans = vias | ranges::views::values | ranges::views::transform([](const auto &x) { return x.span; })
1506+
| ranges::to<std::set>();
1507+
spans.insert(BoardLayers::layer_range_through);
1508+
return spans;
1509+
}
1510+
14891511
} // namespace horizon

Diff for: src/board/board.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,8 @@ class Board : public ObjectProvider, public LayerProvider {
184184
Outline get_outline() const;
185185
Outline get_outline_and_errors() const;
186186

187+
std::set<LayerRange> get_drill_spans() const;
188+
187189
std::string board_directory; // for resolving relative paths in included boards
188190

189191
private:

Diff for: src/board/board_junction.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class BoardJunction : public Junction {
88
using Junction::Junction;
99

1010
uuid_ptr<class Net> net = nullptr;
11-
bool needs_via = false;
11+
LayerRange required_span;
1212
bool has_via = false;
1313
std::vector<UUID> connected_vias;
1414
std::vector<UUID> connected_tracks;

Diff for: src/board/board_layers.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,6 @@ const std::vector<int> &BoardLayers::get_layers()
9090

9191
const unsigned int BoardLayers::max_inner_layers = 8;
9292

93+
const LayerRange BoardLayers::layer_range_through{BoardLayers::TOP_COPPER, BoardLayers::BOTTOM_COPPER};
94+
9395
} // namespace horizon

Diff for: src/board/board_layers.hpp

+8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22
#include <string>
33
#include <vector>
4+
#include "util/layer_range.hpp"
45

56
namespace horizon {
67
class BoardLayers {
@@ -34,11 +35,18 @@ class BoardLayers {
3435
BOTTOM_NOTES = -200
3536
};
3637

38+
static const LayerRange layer_range_through;
39+
3740
static bool is_copper(int l)
3841
{
3942
return l <= TOP_COPPER && l >= BOTTOM_COPPER;
4043
}
4144

45+
static bool is_copper(const LayerRange &l)
46+
{
47+
return is_copper(l.start()) || is_copper(l.end());
48+
}
49+
4250
static bool is_silkscreen(int l)
4351
{
4452
return l == TOP_SILKSCREEN || l == BOTTOM_SILKSCREEN;

Diff for: src/board/board_package.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "pool/ipool.hpp"
66
#include "board.hpp"
77
#include "util/util.hpp"
8+
#include "board_layers.hpp"
89

910
namespace horizon {
1011

@@ -138,7 +139,7 @@ bool BoardPackage::update_package(const Board &brd)
138139

139140
placement.mirror = flip;
140141
for (auto &it2 : package.pads) {
141-
it2.second.padstack.expand_inner(brd.get_n_inner_layers());
142+
it2.second.padstack.expand_inner(brd.get_n_inner_layers(), BoardLayers::layer_range_through);
142143
}
143144

144145
if (flip) {

Diff for: src/board/board_rules_check.cpp

+61-68
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#include "util/util.hpp"
1414
#include "board_rules_check_util.hpp"
1515
#include <iostream>
16-
#include "board_rules_check_util.hpp"
1716

1817
namespace horizon {
1918

@@ -363,13 +362,7 @@ RulesCheckResult BoardRules::check_clearance_copper_keepout(const Board &brd, Ru
363362
status_cb("Getting patches");
364363
auto rules = get_rules_sorted<RuleClearanceCopperKeepout>();
365364
auto &c = cache.get_cache<RulesCheckCacheBoardImage>();
366-
std::set<int> layers;
367365
const auto &patches = c.get_canvas().get_patches();
368-
for (const auto &it : patches) { // collect copper layers
369-
if (brd.get_layers().count(it.first.layer) && brd.get_layers().at(it.first.layer).copper) {
370-
layers.emplace(it.first.layer);
371-
}
372-
}
373366
auto keepout_contours = brd.get_keepout_contours();
374367
auto n_patches = patches.size();
375368
auto n_keepouts = keepout_contours.size();
@@ -390,7 +383,7 @@ RulesCheckResult BoardRules::check_clearance_copper_keepout(const Board &brd, Ru
390383
status_cb(ss.str());
391384
}
392385
if (BoardLayers::is_copper(it.first.layer)
393-
&& (it.first.layer == keepout->polygon->layer || keepout->all_cu_layers)
386+
&& (it.first.layer.overlaps(keepout->polygon->layer) || keepout->all_cu_layers)
394387
&& keepout->patch_types_cu.count(it.first.type)) {
395388

396389
const Net *net = it.first.net ? &brd.block->nets.at(it.first.net) : nullptr;
@@ -434,9 +427,9 @@ RulesCheckResult BoardRules::check_clearance_copper_keepout(const Board &brd, Ru
434427
}
435428
e.location = acc.get();
436429
e.comment = patch_type_names.at(it.first.type) + "(" + (net ? net->name : "") + ") on layer"
437-
+ brd.get_layers().at(it.first.layer).name + " near keepout";
430+
+ brd.get_layer_name(it.first.layer) + " near keepout";
438431
e.error_polygons = {ite};
439-
e.layers.insert(it.first.layer);
432+
e.add_layer_range(brd, it.first.layer);
440433
}
441434
}
442435
}
@@ -454,77 +447,77 @@ RulesCheckResult BoardRules::check_clearance_same_net(const Board &brd, RulesChe
454447
status_cb("Getting patches");
455448
auto rules = get_rules_sorted<RuleClearanceSameNet>();
456449
auto &c = cache.get_cache<RulesCheckCacheBoardImage>();
457-
std::set<int> layers;
458450
const auto &patches = c.get_canvas().get_patches();
459-
for (const auto &it : patches) { // collect copper layers
460-
if (brd.get_layers().count(it.first.layer) && brd.get_layers().at(it.first.layer).copper) {
461-
layers.emplace(it.first.layer);
462-
}
463-
}
451+
464452
if (r.check_cancelled(cancel))
465453
return r;
466454
status_cb("Building patch pairs");
467455

468456
static const std::set<PatchType> patch_types = {PatchType::PAD, PatchType::PAD_TH, PatchType::VIA,
469457
PatchType::HOLE_NPTH};
470-
471-
for (const auto layer : layers) { // check each layer individually
472-
// assemble a list of patch pairs we'll need to check
473-
std::set<std::pair<CanvasPatch::PatchKey, CanvasPatch::PatchKey>> patch_pairs;
474-
for (const auto &it : patches) {
475-
for (const auto &it_other : patches) {
476-
if (layer == it.first.layer && it.first.layer == it_other.first.layer
477-
&& patch_types.count(it.first.type) && patch_types.count(it_other.first.type)
478-
&& it.first.type != it_other.first.type && it.first.net == it_other.first.net) {
479-
std::pair<CanvasPatch::PatchKey, CanvasPatch::PatchKey> k = {it.first, it_other.first};
480-
auto k2 = k;
481-
std::swap(k2.first, k2.second);
482-
if (patch_pairs.count(k) == 0 && patch_pairs.count(k2) == 0) {
483-
patch_pairs.emplace(k);
484-
}
458+
std::set<std::pair<CanvasPatch::PatchKey, CanvasPatch::PatchKey>> patch_pairs;
459+
460+
461+
// assemble a list of patch pairs we'll need to check
462+
for (const auto &it : patches) {
463+
for (const auto &it_other : patches) {
464+
if (it.first.layer.overlaps(it_other.first.layer) && patch_types.count(it.first.type)
465+
&& patch_types.count(it_other.first.type) && it.first.type != it_other.first.type
466+
&& it.first.net == it_other.first.net) {
467+
std::pair<CanvasPatch::PatchKey, CanvasPatch::PatchKey> k = {it.first, it_other.first};
468+
auto k2 = k;
469+
std::swap(k2.first, k2.second);
470+
if (patch_pairs.count(k) == 0 && patch_pairs.count(k2) == 0) {
471+
patch_pairs.emplace(k);
485472
}
486473
}
487-
if (r.check_cancelled(cancel))
488-
return r;
489474
}
475+
if (r.check_cancelled(cancel))
476+
return r;
477+
}
490478

491-
for (const auto &[p1, p2] : patch_pairs) {
492-
if (r.check_cancelled(cancel))
493-
return r;
494-
const Net *net = p1.net ? &brd.block->nets.at(p1.net) : nullptr;
495-
const auto &rule = get_clearance_same_net(net, layer);
496-
auto clearance = rule.get_clearance(p1.type, p2.type);
497-
if (clearance >= 0) {
498-
// expand p1 patch by clearance
499-
ClipperLib::ClipperOffset ofs;
500-
ofs.ArcTolerance = 10e3;
501-
ofs.AddPaths(patches.at(p1), ClipperLib::jtRound, ClipperLib::etClosedPolygon);
502-
ClipperLib::Paths paths_ofs;
503-
ofs.Execute(paths_ofs, clearance);
504-
505-
// intersect expanded and this patch
506-
ClipperLib::Clipper clipper;
507-
clipper.AddPaths(paths_ofs, ClipperLib::ptClip, true);
508-
clipper.AddPaths(patches.at(p2), ClipperLib::ptSubject, true);
509-
ClipperLib::Paths errors;
510-
clipper.Execute(ClipperLib::ctIntersection, errors, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
479+
for (const auto &[p1, p2] : patch_pairs) {
480+
if (r.check_cancelled(cancel))
481+
return r;
511482

512-
// no intersection: no clearance violation
513-
if (errors.size() > 0) {
514-
for (const auto &ite : errors) {
515-
r.errors.emplace_back(RulesCheckErrorLevel::FAIL);
516-
auto &e = r.errors.back();
517-
e.has_location = true;
518-
Accumulator<Coordi> acc;
519-
for (const auto &ite2 : ite) {
520-
acc.accumulate({ite2.X, ite2.Y});
521-
}
522-
e.location = acc.get();
523-
e.comment = patch_type_names.at(p1.type) + " near " + patch_type_names.at(p2.type) + " "
524-
+ get_net_name(net) + " on layer " + brd.get_layers().at(layer).name;
525-
e.error_polygons = {ite};
526-
e.layers.insert(layer);
483+
const Net *net = p1.net ? &brd.block->nets.at(p1.net) : nullptr;
484+
485+
const auto layer_isect = p1.layer.intersection(p2.layer).value();
486+
487+
auto clearance =
488+
find_clearance(*this, &BoardRules::get_clearance_same_net, brd.get_layers_for_range(layer_isect),
489+
std::forward_as_tuple(net), std::forward_as_tuple(p1.type, p2.type));
490+
491+
if (clearance >= 0) {
492+
// expand p1 patch by clearance
493+
ClipperLib::ClipperOffset ofs;
494+
ofs.ArcTolerance = 10e3;
495+
ofs.AddPaths(patches.at(p1), ClipperLib::jtRound, ClipperLib::etClosedPolygon);
496+
ClipperLib::Paths paths_ofs;
497+
ofs.Execute(paths_ofs, clearance);
498+
499+
// intersect expanded and this patch
500+
ClipperLib::Clipper clipper;
501+
clipper.AddPaths(paths_ofs, ClipperLib::ptClip, true);
502+
clipper.AddPaths(patches.at(p2), ClipperLib::ptSubject, true);
503+
ClipperLib::Paths errors;
504+
clipper.Execute(ClipperLib::ctIntersection, errors, ClipperLib::pftNonZero, ClipperLib::pftNonZero);
505+
506+
// no intersection: no clearance violation
507+
if (errors.size() > 0) {
508+
for (const auto &ite : errors) {
509+
r.errors.emplace_back(RulesCheckErrorLevel::FAIL);
510+
auto &e = r.errors.back();
511+
e.has_location = true;
512+
Accumulator<Coordi> acc;
513+
for (const auto &ite2 : ite) {
514+
acc.accumulate({ite2.X, ite2.Y});
527515
}
516+
e.location = acc.get();
517+
e.comment = patch_type_names.at(p1.type) + " near " + patch_type_names.at(p2.type) + " "
518+
+ get_net_name(net) + " on layer " + brd.get_layer_name(layer_isect);
519+
e.error_polygons = {ite};
520+
e.add_layer_range(brd, layer_isect);
528521
}
529522
}
530523
}

0 commit comments

Comments
 (0)