eu.mihosoft.freerouting.autoroute.BatchOptRoute Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2014 Alfons Wirtz
* website www.freerouting.net
*
* Copyright (C) 2017 Michael Hoffer
* Website www.freerouting.mihosoft.eu
*
* 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 at
* for more details.
*/
package eu.mihosoft.freerouting.autoroute;
import java.util.Iterator;
import java.util.Collection;
import java.util.Set;
import eu.mihosoft.freerouting.datastructures.UndoableObjects;
import eu.mihosoft.freerouting.geometry.planar.FloatPoint;
import eu.mihosoft.freerouting.board.Item;
import eu.mihosoft.freerouting.board.Via;
import eu.mihosoft.freerouting.board.Trace;
import eu.mihosoft.freerouting.board.RoutingBoard;
import eu.mihosoft.freerouting.board.FixedState;
import eu.mihosoft.freerouting.board.TestLevel;
import eu.mihosoft.freerouting.interactive.InteractiveActionThread;
/**
* To optimize the vias and traces after the batch autorouter has completed the eu.mihosoft.freerouting.board.
*
* @author Alfons Wirtz
*/
public class BatchOptRoute
{
/**
* To optimize the route on the eu.mihosoft.freerouting.board after the eu.mihosoft.freerouting.autoroute task is finished.
*/
public BatchOptRoute(InteractiveActionThread p_thread)
{
this.thread = p_thread;
this.routing_board = p_thread.hdlg.get_routing_board();
this.sorted_route_items = null;
}
/**
* Optimize the route on the eu.mihosoft.freerouting.board.
*/
public void optimize_board()
{
if (routing_board.get_test_level() != TestLevel.RELEASE_VERSION)
{
System.out.println("Before optimize: Via count: " + routing_board.get_vias().size() + ", trace length: " + Math.round(routing_board.cumulative_trace_length()));
}
boolean route_improved = true;
int curr_pass_no = 0;
use_increased_ripup_costs = true;
while (route_improved)
{
++curr_pass_no;
boolean with_prefered_directions = (curr_pass_no % 2 != 0); // to create more variations
route_improved = opt_route_pass(curr_pass_no, with_prefered_directions);
}
}
/**
* Pass to reduce the number of vias an to shorten the trace lengthon a completely routed eu.mihosoft.freerouting.board.
* Returns true, if the route was improved.
*/
private boolean opt_route_pass(int p_pass_no, boolean p_with_prefered_directions)
{
boolean route_improved = false;
int via_count_before = this.routing_board.get_vias().size();
double trace_length_before = this.thread.hdlg.coordinate_transform.board_to_user(this.routing_board.cumulative_trace_length());
this.thread.hdlg.screen_messages.set_post_route_info(via_count_before, trace_length_before);
this.sorted_route_items = new ReadSortedRouteItems();
this.min_cumulative_trace_length_before = calc_weighted_trace_length(routing_board);
for (;;)
{
if (this.thread.is_stop_requested())
{
return route_improved;
}
Item curr_item = sorted_route_items.next();
if (curr_item == null)
{
break;
}
if (opt_route_item(curr_item, p_pass_no, p_with_prefered_directions))
{
route_improved = true;
}
}
this.sorted_route_items = null;
if (this.use_increased_ripup_costs && !route_improved)
{
this.use_increased_ripup_costs = false;
route_improved = true; // to keep the optimizer going with lower ripup costs
}
return route_improved;
}
/**
* Trie to improve the route by retouting the connections containing p_item.
*/
private boolean opt_route_item(Item p_item, int p_pass_no, boolean p_with_prefered_directions)
{
java.util.ResourceBundle resources =
java.util.ResourceBundle.getBundle("eu.mihosoft.freerouting.interactive.InteractiveState", this.thread.hdlg.get_locale());
String start_message = resources.getString("batch_optimizer") + " " + resources.getString("stop_message") + " " + resources.getString("pass") + " " + (new Integer(p_pass_no)).toString() + ": ";
this.thread.hdlg.screen_messages.set_status_message(start_message);
this.thread.hdlg.remove_ratsnest();
int incomplete_count_before = this.thread.hdlg.get_ratsnest().incomplete_count();
int via_count_before = this.routing_board.get_vias().size();
Set- ripped_items = new java.util.TreeSet
- ();
ripped_items.add(p_item);
if (p_item instanceof Trace)
{
// add also the fork items, especially because not all fork items may be
// returned by ReadSortedRouteItems because of matching end points.
Trace curr_trace = (Trace) p_item;
Set
- curr_contact_list = curr_trace.get_start_contacts();
for (int i = 0; i < 2; ++i)
{
if (contains_only_unfixed_traces(curr_contact_list))
{
ripped_items.addAll(curr_contact_list);
}
curr_contact_list = curr_trace.get_end_contacts();
}
}
Set
- ripped_connections = new java.util.TreeSet
- ();
for (Item curr_item : ripped_items)
{
ripped_connections.addAll(curr_item.get_connection_items(Item.StopConnectionOption.NONE));
}
for (Item curr_item : ripped_connections)
{
if (curr_item.is_user_fixed())
{
return false;
}
}
routing_board.generate_snapshot();
this.routing_board.remove_items(ripped_connections, false);
for (int i = 0; i < p_item.net_count(); ++i)
{
this.routing_board.combine_traces(p_item.get_net_no(i));
}
int ripup_costs = this.thread.hdlg.get_settings().autoroute_settings.get_start_ripup_costs();
if (this.use_increased_ripup_costs)
{
ripup_costs *= ADDITIONAL_RIPUP_COST_FACTOR_AT_START;
}
if (p_item instanceof Trace)
{
// taking less ripup costs seems to produce better results
ripup_costs = (int) Math.round(0.6 * (double) ripup_costs);
}
BatchAutorouter.autoroute_passes_for_optimizing_item(this.thread, MAX_AUTOROUTE_PASSES,
ripup_costs, p_with_prefered_directions);
this.thread.hdlg.remove_ratsnest();
int incomplete_count_after = this.thread.hdlg.get_ratsnest().incomplete_count();
int via_count_after = this.routing_board.get_vias().size();
double trace_length_after = calc_weighted_trace_length(routing_board);
boolean route_improved = !this.thread.is_stop_requested() && (incomplete_count_after < incomplete_count_before ||
incomplete_count_after == incomplete_count_before &&
(via_count_after < via_count_before ||
via_count_after == via_count_before &&
this.min_cumulative_trace_length_before > trace_length_after));
if (route_improved)
{
if (incomplete_count_after < incomplete_count_before ||
incomplete_count_after == incomplete_count_before && via_count_after < via_count_before)
{
this.min_cumulative_trace_length_before = trace_length_after;
}
else
{
// Only cumulative trace length shortened.
// Catch unexpected increase of cumulative trace length somewhere for examole by removing acid trapsw.
this.min_cumulative_trace_length_before = Math.min(this.min_cumulative_trace_length_before, trace_length_after);
}
routing_board.pop_snapshot();
double new_trace_length = this.thread.hdlg.coordinate_transform.board_to_user(this.routing_board.cumulative_trace_length());
this.thread.hdlg.screen_messages.set_post_route_info(via_count_after, new_trace_length);
}
else
{
routing_board.undo(null);
}
return route_improved;
}
static boolean contains_only_unfixed_traces(Collection
- p_item_list)
{
for (Item curr_item : p_item_list)
{
if (curr_item.is_user_fixed() || !(curr_item instanceof Trace))
{
return false;
}
}
return true;
}
/**
* Calculates the cumulative trace lengths multiplied by the trace radius of all traces
* on the eu.mihosoft.freerouting.board, which are not shove_fixed.
*/
private static double calc_weighted_trace_length(RoutingBoard p_board)
{
double result = 0;
int default_clearance_class = eu.mihosoft.freerouting.rules.BoardRules.default_clearance_class();
Iterator
it = p_board.item_list.start_read_object();
for (;;)
{
UndoableObjects.Storable curr_item = p_board.item_list.read_object(it);
if (curr_item == null)
{
break;
}
if (curr_item instanceof Trace)
{
Trace curr_trace = (Trace) curr_item;
FixedState fixed_state = curr_trace.get_fixed_state();
if (fixed_state == FixedState.UNFIXED || fixed_state == FixedState.SHOVE_FIXED)
{
double weighted_trace_length = curr_trace.get_length() * (curr_trace.get_half_width() + p_board.clearance_value(curr_trace.clearance_class_no(), default_clearance_class, curr_trace.get_layer()));
if (fixed_state == FixedState.SHOVE_FIXED)
{
// to produce less violations with pin exit directions.
weighted_trace_length /= 2;
}
result += weighted_trace_length;
}
}
}
return result;
}
/**
* Returns the current position of the item, which will be rerouted or null, if the optimizer is not active.
*/
public FloatPoint get_current_position()
{
if (sorted_route_items == null)
{
return null;
}
return sorted_route_items.get_current_position();
}
private final InteractiveActionThread thread;
private final RoutingBoard routing_board;
private ReadSortedRouteItems sorted_route_items;
private boolean use_increased_ripup_costs; // in the first passes the ripup costs are icreased for better performance.
private double min_cumulative_trace_length_before = 0;
private static int MAX_AUTOROUTE_PASSES = 6;
private static int ADDITIONAL_RIPUP_COST_FACTOR_AT_START = 10;
/**
* Reads the vias and traces on the eu.mihosoft.freerouting.board in ascending x order.
* Because the vias and traces on the eu.mihosoft.freerouting.board change while optimizing the item list
* of the eu.mihosoft.freerouting.board is read from scratch each time the next route item is returned.
*/
private class ReadSortedRouteItems
{
ReadSortedRouteItems()
{
min_item_coor = new FloatPoint(Integer.MIN_VALUE, Integer.MIN_VALUE);
min_item_layer = -1;
}
Item next()
{
Item result = null;
FloatPoint curr_min_coor = new FloatPoint(Integer.MAX_VALUE, Integer.MAX_VALUE);
int curr_min_layer = Integer.MAX_VALUE;
Iterator it = routing_board.item_list.start_read_object();
for (;;)
{
UndoableObjects.Storable curr_item = routing_board.item_list.read_object(it);
if (curr_item == null)
{
break;
}
if (curr_item instanceof Via)
{
Via curr_via = (Via) curr_item;
if (!curr_via.is_user_fixed())
{
FloatPoint curr_via_center = curr_via.get_center().to_float();
int curr_via_min_layer = curr_via.first_layer();
if (curr_via_center.x > min_item_coor.x ||
curr_via_center.x == min_item_coor.x && (curr_via_center.y > min_item_coor.y || curr_via_center.y == min_item_coor.y && curr_via_min_layer > min_item_layer))
{
if (curr_via_center.x < curr_min_coor.x || curr_via_center.x == curr_min_coor.x && (curr_via_center.y < curr_min_coor.y ||
curr_via_center.y == curr_min_coor.y && curr_via_min_layer < curr_min_layer))
{
curr_min_coor = curr_via_center;
curr_min_layer = curr_via_min_layer;
result = curr_via;
}
}
}
}
}
// Read traces last to prefer vias to traces at the same location
it = routing_board.item_list.start_read_object();
for (;;)
{
UndoableObjects.Storable curr_item = routing_board.item_list.read_object(it);
if (curr_item == null)
{
break;
}
if (curr_item instanceof Trace)
{
Trace curr_trace = (Trace) curr_item;
if (!curr_trace.is_shove_fixed())
{
FloatPoint first_corner = curr_trace.first_corner().to_float();
FloatPoint last_corner = curr_trace.last_corner().to_float();
FloatPoint compare_corner;
if (first_corner.x < last_corner.x ||
first_corner.x == last_corner.x && first_corner.y < last_corner.y)
{
compare_corner = last_corner;
}
else
{
compare_corner = first_corner;
}
int curr_trace_layer = curr_trace.get_layer();
if (compare_corner.x > min_item_coor.x ||
compare_corner.x == min_item_coor.x && (compare_corner.y > min_item_coor.y || compare_corner.y == min_item_coor.y && curr_trace_layer > min_item_layer))
{
if (compare_corner.x < curr_min_coor.x || compare_corner.x == curr_min_coor.x &&
(compare_corner.y < curr_min_coor.y || compare_corner.y == curr_min_coor.y && curr_trace_layer < curr_min_layer))
{
boolean is_connected_to_via = false;
Set- trace_contacts = curr_trace.get_normal_contacts();
for (Item curr_contact : trace_contacts)
{
if (curr_contact instanceof Via && !curr_contact.is_user_fixed())
{
is_connected_to_via = true;
break;
}
}
if (!is_connected_to_via)
{
curr_min_coor = compare_corner;
curr_min_layer = curr_trace_layer;
result = curr_trace;
}
}
}
}
}
}
min_item_coor = curr_min_coor;
min_item_layer = curr_min_layer;
return result;
}
FloatPoint get_current_position()
{
return min_item_coor;
}
private FloatPoint min_item_coor;
private int min_item_layer;
}
}