Skip to content
Merged
62 changes: 41 additions & 21 deletions src/db/db/dbFillTool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,16 @@ class GenericRasterizer
}

// because the rasterizer can't handle overlapping cells we need to multiply the row and columns steps
// with an integer until the effective rasterizer pitch get big enough.
// with an integer until the effective rasterizer pitch gets big enough.
m_row_steps *= (m_dim.x () - 1) / (m_row_steps * m_row_step.x ()) + 1;
m_column_steps *= (m_dim.y () - 1) / (m_column_steps * m_column_step.y ()) + 1;

db::Box fp_bbox = fp.box ();

// compensate for distortion by sheared kernel
fp_bbox.enlarge (db::Vector (db::coord_traits<db::Coord>::rounded (double (fp_bbox.height ()) * std::abs (m_column_step.x ()) / dy), db::coord_traits<db::Coord>::rounded (double (fp_bbox.width ()) * std::abs (m_row_step.y ()) / dx)));
db::Coord ex = std::max (std::abs (db::Coord (m_column_step.x () * m_column_steps)), std::abs (db::Coord (m_row_step.x () * m_row_steps)));
db::Coord ey = std::max (std::abs (db::Coord (m_column_step.y () * m_column_steps)), std::abs (db::Coord (m_row_step.y () * m_row_steps)));
fp_bbox.enlarge (db::Vector (ex, ey));

int columns_per_rows = (int (m_row_steps) * m_row_step.y ()) / dy;
int rows_per_columns = (int (m_column_steps) * m_column_step.x ()) / dx;
Expand Down Expand Up @@ -167,6 +169,11 @@ class GenericRasterizer
return m_area_maps [i];
}

db::AreaMap &area_map (unsigned int i)
{
return m_area_maps [i];
}

private:
std::vector<db::AreaMap> m_area_maps;
db::Vector m_row_step, m_column_step;
Expand Down Expand Up @@ -246,7 +253,7 @@ fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type f

for (unsigned int i = 0; i < am.area_maps (); ++i) {

const db::AreaMap &am1 = am.area_map (i);
db::AreaMap &am1 = am.area_map (i);

size_t nx = am1.nx ();
size_t ny = am1.ny ();
Expand All @@ -263,31 +270,54 @@ fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type f
++jj;
}

ninsts += (jj - j);

db::Vector p0 = (am1.p0 () - db::Point ()) - kernel_origin;
p0 += db::Vector (i * am1.d ().x (), j * am1.d ().y ());

db::CellInstArray array;

if (jj > j + 1) {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0), db::Vector (0, am1.d ().y ()), db::Vector (), (unsigned long) (jj - j), 1);
// try to expand the array in x direction
size_t ii = i + 1;
for ( ; ii < nx; ++ii) {
bool all = true;
for (size_t k = j; k < jj && all; ++k) {
all = am1.get (ii, k) == am1.pixel_area ();
}
if (all) {
for (size_t k = j; k < jj; ++k) {
// disable pixel, so we do not see it again in the following columns
am1.get (ii, k) = 0;
}
} else {
break;
}
}

ninsts += (jj - j) * (ii - i);

if (jj > j + 1 || ii > i + 1) {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0), db::Vector (0, am1.d ().y ()), db::Vector (am1.d ().x (), 0), (unsigned long) (jj - j), (unsigned long) (ii - i));
} else {
array = db::CellInstArray (db::CellInst (fill_cell_index), db::Trans (p0));
}

{
// In case we run this from a tiling processor we need to lock against multithread races
tl::MutexLocker locker (&db::TilingProcessor::output_lock ());
tl_assert (cell->layout () != 0);
tl::MutexLocker locker (&cell->layout ()->lock ());
cell->insert (array);
}

if (remaining_parts) {
if (am1.d ().y () == am1.p ().y ()) {
filled_regions.push_back (db::Polygon (db::Box (db::Point (), db::Point (am1.p ().x (), am1.p ().y () * db::Coord (jj - j))).moved (kernel_origin + p0)));
if (am1.d ().y () == am1.p ().y () && am1.d ().x () == am1.p ().x ()) {
db::Box fill_box (db::Point (), db::Point (am1.p ().x () * db::Coord (ii - i), am1.p ().y () * db::Coord (jj - j)));
filled_regions.push_back (db::Polygon (fill_box.enlarged (fill_margin).moved (kernel_origin + p0)));
} else {
db::Box fill_box (db::Point (), db::Point () + am1.p ());
fill_box.enlarge (fill_margin);
for (size_t k = 0; k < jj - j; ++k) {
filled_regions.push_back (db::Polygon (db::Box (db::Point (), db::Point () + am1.p ()).moved (kernel_origin + p0 + db::Vector (0, am1.d ().y () * db::Coord (k)))));
for (size_t l = 0; l < ii - i; ++l) {
filled_regions.push_back (db::Polygon (fill_box.moved (kernel_origin + p0 + db::Vector (am1.d ().x () * db::Coord (l), am1.d ().y () * db::Coord (k)))));
}
}
}
}
Expand All @@ -314,19 +344,9 @@ fill_polygon_impl (db::Cell *cell, const db::Polygon &fp0, db::cell_index_type f
if (any_fill) {

if (remaining_parts) {

std::vector <db::Polygon> fp1;

if (fill_margin != db::Vector ()) {
ep.size (filled_regions, fill_margin.x (), fill_margin.y (), fp1, 3 /*mode*/, false /*=don't resolve holes*/);
filled_regions.swap (fp1);
fp1.clear ();
}

fp1.push_back (fp0);
ep.boolean (fp1, filled_regions, *remaining_parts, db::BooleanOp::ANotB, false /*=don't resolve holes*/);


}

return true;
Expand Down
2 changes: 1 addition & 1 deletion src/db/db/dbPolygonTools.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1918,7 +1918,7 @@ rasterize_impl (const db::polygon<C> &polygon, db::area_map<C> &am)

area_type aa = a;

if (dx == py) {
if (dx == px) {

box_type cell (x, y, xx, yy);

Expand Down
6 changes: 6 additions & 0 deletions src/db/db/dbRecursiveShapeIterator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -500,15 +500,21 @@ RecursiveShapeIterator::validate (RecursiveShapeReceiver *receiver) const
}

if (mp_shapes) {

// Ensures the trees are built properly - this is important in MT contexts (i.e. TilingProcessor)
// TODO: get rid of that const cast
(const_cast <db::Shapes *> (mp_shapes))->update ();

start_shapes ();

} else if (mp_layout && (! m_has_layers || m_current_layer < m_layers.size ())) {

// Ensures the trees are built properly - this is important in MT contexts (i.e. TilingProcessor)
mp_layout->update ();

new_cell (receiver);
next_shape (receiver);

}

if (mp_layout && ! at_end ()) {
Expand Down
10 changes: 10 additions & 0 deletions src/db/db/dbShapes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1127,10 +1127,20 @@ void Shapes::reset_bbox_dirty ()

void Shapes::update ()
{
std::unique_ptr<tl::MutexLocker> locker;

// If not in a layout context, we should lock here against multiple calls from different threads.
// In a layout context, the Layout object will do that for us.
if (layout () == 0) {
static tl::Mutex lock;
locker.reset (new tl::MutexLocker (&lock));
}

for (tl::vector<LayerBase *>::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) {
(*l)->sort ();
(*l)->update_bbox ();
}

set_dirty (false);
}

Expand Down
32 changes: 32 additions & 0 deletions src/db/unit_tests/dbFillToolTests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,35 @@ TEST(5)
CHECKPOINT();
db::compare_layouts (_this, ly, tl::testdata () + "/algo/fill_tool_au5.oas", db::WriteOAS);
}

// issue #2087
TEST(6)
{
db::Layout ly;
{
std::string fn (tl::testdata ());
fn += "/algo/fill_tool6.gds";
tl::InputStream stream (fn);
db::Reader reader (stream);
reader.read (ly);
}

db::cell_index_type fill_cell = ly.cell_by_name ("FILL_CELL").second;
db::cell_index_type top_cell = ly.cell_by_name ("TOP").second;
unsigned int fill_layer = ly.get_layer (db::LayerProperties (1, 0));

db::Region fill_region (db::RecursiveShapeIterator (ly, ly.cell (top_cell), fill_layer));

db::Region remaining_polygons;

db::Vector rs (2500, 0);
db::Vector cs (650, 2500);
db::Box fc_box = ly.cell (fill_cell).bbox ();
db::fill_region (&ly.cell (top_cell), fill_region, fill_cell, fc_box, rs, cs, db::Point (), false, &remaining_polygons);

unsigned int l100 = ly.insert_layer (db::LayerProperties (100, 0));
remaining_polygons.insert_into (&ly, top_cell, l100);

CHECKPOINT();
db::compare_layouts (_this, ly, tl::testdata () + "/algo/fill_tool_au6.oas", db::WriteOAS);
}
33 changes: 28 additions & 5 deletions src/drc/drc/built-in-macros/_drc_layer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5460,8 +5460,17 @@ def output(*args)
# as the reference point. The reference point will also defined the footprint of the fill cell - more precisely
# the lower left corner. When step vectors are given, the fill cell's footprint is taken to be a rectangle
# having the horizontal and vertical step pitch for width and height respectively. This way the fill cells
# will be arrange seamlessly. However, the cell's dimensions can be changed, so that the fill cells
# will be arrange seamlessly.
#
# However, the cell's dimensions can be changed, so that the fill cells
# can overlap or there is a space between the cells. To change the dimensions use the "dim" method.
# This example will use a fill cell footprint of 1x1 micrometers, regardless of the step pitch:
#
# @code
# p = fill_pattern("FILL_CELL")
# p.shape(1, 0, box(0.0, 0.0, 1.0, 1.0))
# p.dim(1.0, 1.0)
# @/code
#
# The following example specifies a fill cell with an active area of -0.5 .. 1.5 in both directions
# (2 micron width and height). With these dimensions the fill cell's footprint is independent of the
Expand All @@ -5474,6 +5483,18 @@ def output(*args)
# p.dim(2.0, 2.0)
# @/code
#
# Finally, the fill cell can be given a margin: this is a space around the fill cell which needs
# to be inside the fill region. Hence, the margin can be used to implement a distance, the fill
# cells (more precisely: their footprints) will maintain to the outside border of the fill region.
# The following example implements a margin of 200 nm in horizontal and 250 nm in vertical direction:
#
# @code
# p = fill_pattern("FILL_CELL")
# p.shape(1, 0, box(0.0, 0.0, 1.0, 1.0))
# p.dim(1.0, 1.0)
# p.margin(0.2, 0.25)
# @/code
#
# With these ingredients will can use the fill function. The first example fills the polygons
# of "to_fill" with an orthogonal pattern of 1x1 micron rectangles with a pitch of 2 microns:
#
Expand Down Expand Up @@ -5574,6 +5595,7 @@ def _fill(with_left, *args)
fill_cell = pattern.create_cell(@engine._output_layout, @engine)
top_cell = @engine._output_cell
fc_box = dbu_trans * pattern.cell_box(row_step.x, column_step.y)
fill_margin = dbu_trans * pattern.fill_margin
rs = dbu_trans * row_step
cs = dbu_trans * column_step
origin = origin ? dbu_trans * origin : nil
Expand Down Expand Up @@ -5604,6 +5626,7 @@ def _fill(with_left, *args)
tp.var("rs", rs)
tp.var("cs", cs)
tp.var("origin", origin)
tp.var("fill_margin", fill_margin)
tp.var("fc_index", fc_index)
tp.var("repeat", repeat)
tp.var("with_left", with_left)
Expand All @@ -5616,8 +5639,8 @@ def _fill(with_left, *args)
tile_box = tile_box & tc_box;
var left = with_left ? Region.new : nil;
repeat ?
(region & tile_box).fill_multi(top_cell, fc_index, fc_box, rs, cs, Vector.new, left, _tile.bbox) :
(region & tile_box).fill(top_cell, fc_index, fc_box, rs, cs, origin, left, Vector.new, left, _tile.bbox);
(region & tile_box).fill_multi(top_cell, fc_index, fc_box, rs, cs, fill_margin, left, _tile.bbox) :
(region & tile_box).fill(top_cell, fc_index, fc_box, rs, cs, origin, left, fill_margin, left, _tile.bbox);
with_left && _output(#{result_arg}, left)
)
END
Expand All @@ -5639,9 +5662,9 @@ def _fill(with_left, *args)

@engine.run_timed("\"#{m}\" in: #{@engine.src_line}", self.data) do
if repeat
self.data.fill_multi(top_cell, fc_index, fc_box, rs, cs, RBA::Vector::new, result)
self.data.fill_multi(top_cell, fc_index, fc_box, rs, cs, fill_margin, result)
else
self.data.fill(top_cell, fc_index, fc_box, rs, cs, origin, result, RBA::Vector::new, result)
self.data.fill(top_cell, fc_index, fc_box, rs, cs, origin, result, fill_margin, result)
end
end

Expand Down
21 changes: 20 additions & 1 deletion src/drc/drc/built-in-macros/_drc_tags.rb
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ def initialize(name)
@shapes = []
@origin = nil
@dim = nil
@margin = RBA::DVector::new
end

def create_cell(layout, engine)
Expand All @@ -350,7 +351,11 @@ def create_cell(layout, engine)
def cell_box(def_w, def_h)
o = @origin || self._computed_origin
d = @dim || RBA::DVector::new(def_w, def_h)
RBA::DBox::new(o, o + d)
RBA::DBox::new(o, o + d).enlarged(@margin)
end

def fill_margin
-@margin
end

def default_xpitch
Expand Down Expand Up @@ -454,6 +459,20 @@ def dim(w, h)

end

def margin(w, h)

if !w.is_a?(1.class) && !w.is_a?(1.0.class)
raise("w argument not numeric FillCell#dim")
end
if !h.is_a?(1.class) && !h.is_a?(1.0.class)
raise("h argument not numeric FillCell#dim")
end
@margin = RBA::DVector::new(w, h)

self

end

end

# A wrapper for the fill step definition
Expand Down
Loading
Loading