You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

288 lines
11 KiB

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* 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 2
* 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, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef PCBNEW_LENGTH_CALCULATION_H
#define PCBNEW_LENGTH_CALCULATION_H
#include <board_design_settings.h>
#include <geometry/shape_line_chain.h>
#include <pad.h>
#include <pcb_track.h>
#include <layer_ids.h>
#include <unordered_set>
#include <connectivity/connectivity_data.h>
class BOARD;
/**
* Lightweight class which holds a pad, via, or a routed trace outline. Proxied objects passed by pointer are not
* owned by this container.
*/
class LENGTH_CALCULATION_ITEM
{
public:
/// The type of routing object this item proxies
enum class TYPE
{
UNKNOWN,
PAD,
LINE,
VIA
};
/// Whether this item is UNMERGED, it has been merged and should be used (MERGED_IN_USE), or it has been merged
/// and has been retired from use (MERGED_RETIRED). MERGED_RETIRED essentially means the object has been merged
/// in to a MERGED_IN_USE item.
enum class MERGE_STATUS
{
UNMERGED,
MERGED_IN_USE,
MERGED_RETIRED
};
/// Gets the routing item type
TYPE Type() const { return m_type; };
/// Sets the parent PAD associated with this item
void SetPad( PAD* aPad )
{
m_type = TYPE::PAD;
m_pad = aPad;
}
/// Gets the parent PAD associated with this item
PAD* GetPad() const { return m_pad; }
/// Sets the source SHAPE_LINE_CHAIN of this item
void SetLine( const SHAPE_LINE_CHAIN& aLine )
{
m_type = TYPE::LINE;
m_line = aLine;
}
/// Gets the SHAPE_LINE_CHAIN associated with this item
SHAPE_LINE_CHAIN& GetLine() const { return m_line; }
/// Sets the VIA associated with this item
void SetVia( PCB_VIA* aVia )
{
m_type = TYPE::VIA;
m_via = aVia;
}
/// Gets the VIA associated with this item
PCB_VIA* GetVia() const { return m_via; }
/// Sets the first and last layers associated with this item
void SetLayers( const PCB_LAYER_ID aStart, const PCB_LAYER_ID aEnd = PCB_LAYER_ID::UNDEFINED_LAYER )
{
m_layerStart = aStart;
m_layerEnd = aEnd;
}
/// Sets the MERGE_STATUS of this item. MERGED_RETIRED essentially means the object has been merged
/// in to a MERGED_IN_USE item.
void SetMergeStatus( const MERGE_STATUS aStatus ) { m_mergeStatus = aStatus; }
/// Gets the MERGE_STATUS of this item
MERGE_STATUS GetMergeStatus() const { return m_mergeStatus; }
/// Gets the upper and lower layers for the proxied item
std::tuple<PCB_LAYER_ID, PCB_LAYER_ID> GetLayers() const { return { m_layerStart, m_layerEnd }; }
/// Gets the start board layer for the proxied item
PCB_LAYER_ID GetStartLayer() const { return m_layerStart; }
/// Gets the end board layer for the proxied item.
PCB_LAYER_ID GetEndLayer() const { return m_layerEnd; }
/// Calculates active via payers for a proxied VIA object
void CalculateViaLayers( const BOARD* aBoard );
protected:
/// A proxied PAD object. Set to nullptr if not proxying a PAD.
PAD* m_pad{ nullptr };
/// A proxied SHAPE_LINE_CHAIN object. Line is empty if not proxying a SHAPE_LINE_CHAIN.
mutable SHAPE_LINE_CHAIN m_line;
/// A proxied PVIAAD object. Set to nullptr if not proxying a VIA.
PCB_VIA* m_via{ nullptr };
/// The start board layer for the proxied object
PCB_LAYER_ID m_layerStart{ PCB_LAYER_ID::UNDEFINED_LAYER };
/// The end board layer for the proxied object
PCB_LAYER_ID m_layerEnd{ PCB_LAYER_ID::UNDEFINED_LAYER };
/// Flags whether this item has already been merged with another
MERGE_STATUS m_mergeStatus{ MERGE_STATUS::UNMERGED };
/// The routing object type of the proxied parent
TYPE m_type{ TYPE::UNKNOWN };
};
/**
* Holds length measurement result details and statistics
*/
struct LENGTH_DETAILS
{
int NumPads{ 0 };
int NumVias{ 0 };
int ViaLength{ 0 };
int64_t TrackLength{ 0 };
int PadToDieLength{ 0 };
std::unique_ptr<std::map<PCB_LAYER_ID, int64_t>> LayerLengths;
int64_t TotalLength() const { return ViaLength + TrackLength + PadToDieLength; }
};
/**
* Struct to control which optimisations the length calculation code runs on
* the given path objects. This is required as some call sites (e.g. PNS) run
* their own path optimisation, whereas others (e.g. Net Inspector) do not.
*/
struct PATH_OPTIMISATIONS
{
/// Optimise via layers for height calculations, ensuring only the distance
/// between routed segments is considered
bool OptimiseViaLayers = false;
/// Merges all contiguous (end-to-end, same layer) tracks
bool MergeTracks = false;
/// Optimises the electrical length of tracks within pads. Note that the track
/// must terminate at the trace anchor point to be considered for
/// optimisation. Will require MergeTracks if used with a non-contiguous item
/// set.
bool OptimiseTracesInPads = false;
/// Determines if there is a via-in-pad present on the board but not in the
/// item set. This condition can arise from the PNS meander placer.
/// TODO (JJ): This can be fixed in the router
bool InferViaInPad = false;
};
/**
* Class which calculates lengths (and associated routing statistics) in a BOARD context
*/
class LENGTH_CALCULATION
{
public:
/// Construct the calculator in the given BOARD context
explicit LENGTH_CALCULATION( BOARD* aBoard ) : m_board( aBoard ) {}
/**
* @brief Calculates the electrical length of the given items
* @param aItems is the vector of items making up the route
* @param aPathType indicates whether this is an ordered route, or an unordered collection
* @param aOptimised indicates whether this has been optimised for electrical length (e.g. clipping within pads)
* @param aStartPad is the starting pad of the route
* @param aEndPad is the ending pad of the route
*/
int64_t CalculateLength( std::vector<LENGTH_CALCULATION_ITEM>& aItems, PATH_OPTIMISATIONS aOptimisations,
const PAD* aStartPad = nullptr, const PAD* aEndPad = nullptr ) const;
/**
* @brief Calculates the electrical length of the given items
* @param aItems is the vector of items making up the route
* @param aPathType indicates whether this is an ordered route, or an unordered collection
* @param aOptimised indicates whether this has been optimised for electrical length (e.g. clipping within pads)
* @param aStartPad is the starting pad of the route
* @param aEndPad is the ending pad of the route
* @param aWithLayerLengths indicates whether the layer length structure should be populated
*/
LENGTH_DETAILS CalculateLengthDetails( std::vector<LENGTH_CALCULATION_ITEM>& aItems,
PATH_OPTIMISATIONS aOptimisations, const PAD* aStartPad = nullptr,
const PAD* aEndPad = nullptr, bool aWithLayerLengths = false ) const;
/// Optimises the given trace / line to minimise the electrical path length within the given pad
static void OptimiseTraceInPad( SHAPE_LINE_CHAIN& aLine, const PAD* aPad, PCB_LAYER_ID aPcbLayer );
/// Return a LENGTH_CALCULATION_ITEM constructed from the given BOARD_CONNECTED_ITEM
LENGTH_CALCULATION_ITEM GetLengthCalculationItem( BOARD_CONNECTED_ITEM* aBoardItem ) const;
protected:
/// The parent board for all items
BOARD* m_board;
/// Enum to describe whether track merging is attempted from the start or end of a track segment
enum class MERGE_POINT
{
START,
END
};
/**
* Returns the stackup distance between the two given layers.
*
* Note: Can return 0 if the board design settings disallow stackup height calculations
*/
int stackupHeight( PCB_LAYER_ID aFirstLayer, PCB_LAYER_ID aSecondLayer ) const;
/**
* Optimises the given set of items to minimise the electrical path length. At the moment
* only optimises lines attached to pads, future work could optimise paths through pads
*
* Assumes that any polylines are only connected at either end, and not at midpoints
*/
static void optimiseTracesInPads( const std::vector<LENGTH_CALCULATION_ITEM*>& aPads,
const std::vector<LENGTH_CALCULATION_ITEM*>& aLines );
/// Clips the given line to the minimal direct electrical length within the pad
static void clipLineToPad( SHAPE_LINE_CHAIN& aLine, const PAD* aPad, PCB_LAYER_ID aLayer, bool aForward = true );
/**
* Optimises the via layers. Ensures that vias that are routed through only on one layer do not count towards total
* length calculations.
*/
static void
optimiseViaLayers( const std::vector<LENGTH_CALCULATION_ITEM*>& aVias,
std::vector<LENGTH_CALCULATION_ITEM*>& aLines,
std::map<VECTOR2I, std::unordered_set<LENGTH_CALCULATION_ITEM*>>& aLinesPositionMap,
const std::map<VECTOR2I, std::unordered_set<LENGTH_CALCULATION_ITEM*>>& aPadsPositionMap );
/**
* Merges any lines (traces) that are contiguous, on one layer, and with no junctions
*/
static void mergeLines( std::vector<LENGTH_CALCULATION_ITEM*>& aLines,
std::map<VECTOR2I, std::unordered_set<LENGTH_CALCULATION_ITEM*>>& aLinesPositionMap );
/**
* Merges two SHAPE_LINE_CHAINs where there is a shared endpoing.
*
* aSecondary is merged in to aPrimary
*/
static void mergeShapeLineChains( SHAPE_LINE_CHAIN& aPrimary, const SHAPE_LINE_CHAIN& aSecondary,
MERGE_POINT aMergePoint );
/**
* Infers if there is a via in the given pad. Adds via details to the length details data structure if found.
*/
void inferViaInPad( const PAD* aPad, const LENGTH_CALCULATION_ITEM& aItem, LENGTH_DETAILS& aDetails ) const;
};
#endif //PCBNEW_LENGTH_CALCULATION_H