diff --git a/doc/src/api/vpr/index.rst b/doc/src/api/vpr/index.rst index 53c6b35bc36..37410b72b9c 100644 --- a/doc/src/api/vpr/index.rst +++ b/doc/src/api/vpr/index.rst @@ -7,6 +7,7 @@ VPR API :maxdepth: 1 contexts - netlist mapping + netlist + route_tree rr_graph diff --git a/doc/src/api/vpr/route_tree.rst b/doc/src/api/vpr/route_tree.rst new file mode 100644 index 00000000000..7be12dda86a --- /dev/null +++ b/doc/src/api/vpr/route_tree.rst @@ -0,0 +1,22 @@ +======== +Route Tree +======== + +RouteTree +------- + +.. doxygenfile:: route_tree.h + :project: vpr + :sections: detaileddescription + + +.. doxygenclass:: RouteTree + :project: vpr + :members: + +RouteTreeNode +------------- + +.. doxygenclass:: RouteTreeNode + :project: vpr + :members: diff --git a/libs/libvtrutil/src/tl_optional.hpp b/libs/libvtrutil/src/tl_optional.hpp new file mode 100644 index 00000000000..294755b69a1 --- /dev/null +++ b/libs/libvtrutil/src/tl_optional.hpp @@ -0,0 +1,2072 @@ +/* TartanLlama's optional.hpp. + * It is wrapped in vtr namespace in vtr_optional.h. + * Original repo: https://github.com/TartanLlama/optional + */ + +/// +// optional - An implementation of std::optional with extensions +// Written in 2017 by Sy Brand (tartanllama@gmail.com, @TartanLlama) +// +// Documentation available at https://tl.tartanllama.xyz/ +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// . +/// + +#ifndef TL_OPTIONAL_HPP +#define TL_OPTIONAL_HPP + +#define TL_OPTIONAL_VERSION_MAJOR 1 +#define TL_OPTIONAL_VERSION_MINOR 0 +#define TL_OPTIONAL_VERSION_PATCH 0 + +#include +#include +#include +#include +#include + +#if (defined(_MSC_VER) && _MSC_VER == 1900) +# define TL_OPTIONAL_MSVC2015 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__)) +# define TL_OPTIONAL_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && !defined(__clang__)) +# define TL_OPTIONAL_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && !defined(__clang__)) +# define TL_OPTIONAL_GCC55 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions +# define TL_OPTIONAL_NO_CONSTRR + +// GCC < 5 doesn't support some standard C++11 type traits +# define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor::value +# define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign::value + +// This one will be different for GCC 5.7 if it's ever supported +# define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value + +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector +// for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) +# ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +# define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +namespace tl { +namespace detail { +template +struct is_trivially_copy_constructible : std::is_trivially_copy_constructible {}; +# ifdef _GLIBCXX_VECTOR +template +struct is_trivially_copy_constructible> + : std::is_trivially_copy_constructible {}; +# endif +} // namespace detail +} // namespace tl +# endif + +# define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible::value +# define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable::value +# define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value +#else +# define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible::value +# define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable::value +# define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible::value +#endif + +#if __cplusplus > 201103L +# define TL_OPTIONAL_CXX14 +#endif + +// constexpr implies const in C++11, not C++14 +#if (__cplusplus == 201103L || defined(TL_OPTIONAL_MSVC2015) || defined(TL_OPTIONAL_GCC49)) +# define TL_OPTIONAL_11_CONSTEXPR +#else +# define TL_OPTIONAL_11_CONSTEXPR constexpr +#endif + +namespace tl { +#ifndef TL_MONOSTATE_INPLACE_MUTEX +# define TL_MONOSTATE_INPLACE_MUTEX +/// Used to represent an optional with no data; essentially a bool +class monostate {}; + +/// A tag type to tell optional to construct its value in-place +struct in_place_t { + explicit in_place_t() = default; +}; +/// A tag to tell optional to construct its value in-place +static constexpr in_place_t in_place{}; +#endif + +template +class optional; + +namespace detail { +#ifndef TL_TRAITS_MUTEX +# define TL_TRAITS_MUTEX +// C++14-style aliases for brevity +template +using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template +using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; + +// std::conjunction from C++17 +template +struct conjunction : std::true_type {}; +template +struct conjunction : B {}; +template +struct conjunction + : std::conditional, B>::type {}; + +# if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +# define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +# endif + +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +# ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +template +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func : std::true_type {}; +template +struct is_pointer_to_non_const_member_func : std::true_type {}; +template +struct is_pointer_to_non_const_member_func : std::true_type {}; +template +struct is_pointer_to_non_const_member_func : std::true_type {}; +template +struct is_pointer_to_non_const_member_func : std::true_type {}; +template +struct is_pointer_to_non_const_member_func : std::true_type {}; + +template +struct is_const_or_const_ref : std::false_type {}; +template +struct is_const_or_const_ref : std::true_type {}; +template +struct is_const_or_const_ref : std::true_type {}; +# endif + +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template::value && is_const_or_const_ref::value)>, +# endif + typename = enable_if_t>::value>, + int = 0> +constexpr auto invoke(Fn&& f, Args&&... args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} + +template>::value>> +constexpr auto invoke(Fn&& f, Args&&... args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template +struct invoke_result_impl; + +template +struct invoke_result_impl< + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = decltype(detail::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; + +# if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template +struct is_swappable : std::true_type {}; + +template +struct is_nothrow_swappable : std::true_type {}; +# else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { +// if swap ADL finds this then it would call std::swap otherwise (same +// signature) +struct tag {}; + +template +tag swap(T&, T&); +template +tag swap(T (&a)[N], T (&b)[N]); + +// helper functions to test if an unqualified swap is possible, and if it +// becomes std::swap +template +std::false_type can_swap(...) noexcept(false); +template(), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + +template +std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); + +template +struct is_std_swap_noexcept + : std::integral_constant::value && std::is_nothrow_move_assignable::value> {}; + +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; + +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && (!decltype(detail::swap_adl_tests::uses_std(0))::value || (std::is_move_assignable::value && std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && (!decltype(detail::swap_adl_tests::uses_std(0))::value || is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && ((decltype(detail::swap_adl_tests::uses_std(0))::value && detail::swap_adl_tests::is_std_swap_noexcept::value) || (!decltype(detail::swap_adl_tests::uses_std(0))::value && detail::swap_adl_tests::is_adl_swap_noexcept::value))> { +}; +# endif +#endif + +// std::void_t from C++17 +template +struct voider { using type = void; }; +template +using void_t = typename voider::type; + +// Trait for checking if a type is a tl::optional +template +struct is_optional_impl : std::false_type {}; +template +struct is_optional_impl> : std::true_type {}; +template +using is_optional = is_optional_impl>; + +// Change void to tl::monostate +template +using fixup_void = conditional_t::value, monostate, U>; + +template> +using get_map_return = optional>>; + +// Check if invoking F for some Us returns void +template +struct returns_void_impl; +template +struct returns_void_impl>, U...> + : std::is_void> {}; +template +using returns_void = returns_void_impl; + +template +using enable_if_ret_void = enable_if_t::value>; + +template +using disable_if_ret_void = enable_if_t::value>; + +template +using enable_forward_value = detail::enable_if_t::value && !std::is_same, in_place_t>::value && !std::is_same, detail::decay_t>::value>; + +template +using enable_from_other = detail::enable_if_t< + std::is_constructible::value && !std::is_constructible&>::value && !std::is_constructible&&>::value && !std::is_constructible&>::value && !std::is_constructible&&>::value && !std::is_convertible&, T>::value && !std::is_convertible&&, T>::value && !std::is_convertible&, T>::value && !std::is_convertible&&, T>::value>; + +template +using enable_assign_forward = detail::enable_if_t< + !std::is_same, detail::decay_t>::value && !detail::conjunction, std::is_same>>::value && std::is_constructible::value && std::is_assignable::value>; + +template +using enable_assign_from_other = detail::enable_if_t< + std::is_constructible::value && std::is_assignable::value && !std::is_constructible&>::value && !std::is_constructible&&>::value && !std::is_constructible&>::value && !std::is_constructible&&>::value && !std::is_convertible&, T>::value && !std::is_convertible&&, T>::value && !std::is_convertible&, T>::value && !std::is_convertible&&, T>::value && !std::is_assignable&>::value && !std::is_assignable&&>::value && !std::is_assignable&>::value && !std::is_assignable&&>::value>; + +// The storage base manages the actual storage, and correctly propagates +// trivial destruction from T. This case is for when T is not trivially +// destructible. +template::value> +struct optional_storage_base { + TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept + : m_dummy() + , m_has_value(false) {} + + template + TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U&&... u) + : m_value(std::forward(u)...) + , m_has_value(true) {} + + ~optional_storage_base() { + if (m_has_value) { + m_value.~T(); + m_has_value = false; + } + } + + struct dummy {}; + union { + dummy m_dummy; + T m_value; + }; + + bool m_has_value; +}; + +// This case is for when T is trivially destructible. +template +struct optional_storage_base { + TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept + : m_dummy() + , m_has_value(false) {} + + template + TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U&&... u) + : m_value(std::forward(u)...) + , m_has_value(true) {} + + // No destructor, so this class is trivially destructible + + struct dummy {}; + union { + dummy m_dummy; + T m_value; + }; + + bool m_has_value = false; +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template +struct optional_operations_base : optional_storage_base { + using optional_storage_base::optional_storage_base; + + void hard_reset() noexcept { + get().~T(); + this->m_has_value = false; + } + + template + void construct(Args&&... args) noexcept { + new (std::addressof(this->m_value)) T(std::forward(args)...); + this->m_has_value = true; + } + + template + void assign(Opt&& rhs) { + if (this->has_value()) { + if (rhs.has_value()) { + this->m_value = std::forward(rhs).get(); + } else { + this->m_value.~T(); + this->m_has_value = false; + } + } + + else if (rhs.has_value()) { + construct(std::forward(rhs).get()); + } + } + + bool has_value() const { return this->m_has_value; } + + TL_OPTIONAL_11_CONSTEXPR T& get() & { return this->m_value; } + TL_OPTIONAL_11_CONSTEXPR const T& get() const& { return this->m_value; } + TL_OPTIONAL_11_CONSTEXPR T&& get() && { return std::move(this->m_value); } +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr const T&& get() const&& { return std::move(this->m_value); } +#endif +}; + +// This class manages conditionally having a trivial copy constructor +// This specialization is for when T is trivially copy constructible +template +struct optional_copy_base : optional_operations_base { + using optional_operations_base::optional_operations_base; +}; + +// This specialization is for when T is not trivially copy constructible +template +struct optional_copy_base : optional_operations_base { + using optional_operations_base::optional_operations_base; + + optional_copy_base() = default; + optional_copy_base(const optional_copy_base& rhs) + : optional_operations_base() { + if (rhs.has_value()) { + this->construct(rhs.get()); + } else { + this->m_has_value = false; + } + } + + optional_copy_base(optional_copy_base&& rhs) = default; + optional_copy_base& operator=(const optional_copy_base& rhs) = default; + optional_copy_base& operator=(optional_copy_base&& rhs) = default; +}; + +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef TL_OPTIONAL_GCC49 +template::value> +struct optional_move_base : optional_copy_base { + using optional_copy_base::optional_copy_base; +}; +#else +template +struct optional_move_base; +#endif +template +struct optional_move_base : optional_copy_base { + using optional_copy_base::optional_copy_base; + + optional_move_base() = default; + optional_move_base(const optional_move_base& rhs) = default; + + optional_move_base(optional_move_base&& rhs) noexcept( + std::is_nothrow_move_constructible::value) { + if (rhs.has_value()) { + this->construct(std::move(rhs.get())); + } else { + this->m_has_value = false; + } + } + optional_move_base& operator=(const optional_move_base& rhs) = default; + optional_move_base& operator=(optional_move_base&& rhs) = default; +}; + +// This class manages conditionally having a trivial copy assignment operator +template +struct optional_copy_assign_base : optional_move_base { + using optional_move_base::optional_move_base; +}; + +template +struct optional_copy_assign_base : optional_move_base { + using optional_move_base::optional_move_base; + + optional_copy_assign_base() = default; + optional_copy_assign_base(const optional_copy_assign_base& rhs) = default; + + optional_copy_assign_base(optional_copy_assign_base&& rhs) = default; + optional_copy_assign_base& operator=(const optional_copy_assign_base& rhs) { + this->assign(rhs); + return *this; + } + optional_copy_assign_base& + operator=(optional_copy_assign_base&& rhs) + = default; +}; + +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef TL_OPTIONAL_GCC49 +template::value&& std::is_trivially_move_constructible::value&& std::is_trivially_move_assignable::value> +struct optional_move_assign_base : optional_copy_assign_base { + using optional_copy_assign_base::optional_copy_assign_base; +}; +#else +template +struct optional_move_assign_base; +#endif + +template +struct optional_move_assign_base : optional_copy_assign_base { + using optional_copy_assign_base::optional_copy_assign_base; + + optional_move_assign_base() = default; + optional_move_assign_base(const optional_move_assign_base& rhs) = default; + + optional_move_assign_base(optional_move_assign_base&& rhs) = default; + + optional_move_assign_base& + operator=(const optional_move_assign_base& rhs) + = default; + + optional_move_assign_base& + operator=(optional_move_assign_base&& rhs) noexcept( + std::is_nothrow_move_constructible::value&& std::is_nothrow_move_assignable::value) { + this->assign(std::move(rhs)); + return *this; + } +}; + +// optional_delete_ctor_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible +template::value, bool EnableMove = std::is_move_constructible::value> +struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base&) = default; + optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = default; + optional_delete_ctor_base& + operator=(const optional_delete_ctor_base&) + = default; + optional_delete_ctor_base& + operator=(optional_delete_ctor_base&&) noexcept = default; +}; + +template +struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base&) = default; + optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = delete; + optional_delete_ctor_base& + operator=(const optional_delete_ctor_base&) + = default; + optional_delete_ctor_base& + operator=(optional_delete_ctor_base&&) noexcept = default; +}; + +template +struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base&) = delete; + optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = default; + optional_delete_ctor_base& + operator=(const optional_delete_ctor_base&) + = default; + optional_delete_ctor_base& + operator=(optional_delete_ctor_base&&) noexcept = default; +}; + +template +struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base&) = delete; + optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = delete; + optional_delete_ctor_base& + operator=(const optional_delete_ctor_base&) + = default; + optional_delete_ctor_base& + operator=(optional_delete_ctor_base&&) noexcept = default; +}; + +// optional_delete_assign_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible + assignable +template::value && std::is_copy_assignable::value), + bool EnableMove = (std::is_move_constructible::value && std::is_move_assignable::value)> +struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base&) = default; + optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default; + optional_delete_assign_base& + operator=(const optional_delete_assign_base&) + = default; + optional_delete_assign_base& + operator=(optional_delete_assign_base&&) noexcept = default; +}; + +template +struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base&) = default; + optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default; + optional_delete_assign_base& + operator=(const optional_delete_assign_base&) + = default; + optional_delete_assign_base& + operator=(optional_delete_assign_base&&) noexcept = delete; +}; + +template +struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base&) = default; + optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default; + optional_delete_assign_base& + operator=(const optional_delete_assign_base&) + = delete; + optional_delete_assign_base& + operator=(optional_delete_assign_base&&) noexcept = default; +}; + +template +struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base&) = default; + optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default; + optional_delete_assign_base& + operator=(const optional_delete_assign_base&) + = delete; + optional_delete_assign_base& + operator=(optional_delete_assign_base&&) noexcept = delete; +}; + +} // namespace detail + +/// A tag type to represent an empty optional +struct nullopt_t { + struct do_not_use {}; + constexpr explicit nullopt_t(do_not_use, do_not_use) noexcept {} +}; +/// Represents an empty optional +static constexpr nullopt_t nullopt{nullopt_t::do_not_use{}, + nullopt_t::do_not_use{}}; + +class bad_optional_access : public std::exception { + public: + bad_optional_access() = default; + const char* what() const noexcept { return "Optional has no value"; } +}; + +/// An optional object is an object that contains the storage for another +/// object and manages the lifetime of this contained object, if any. The +/// contained object may be initialized after the optional object has been +/// initialized, and may be destroyed before the optional object has been +/// destroyed. The initialization state of the contained object is tracked by +/// the optional object. +template +class optional : private detail::optional_move_assign_base, + private detail::optional_delete_ctor_base, + private detail::optional_delete_assign_base { + using base = detail::optional_move_assign_base; + + static_assert(!std::is_same::value, + "instantiation of optional with in_place_t is ill-formed"); + static_assert(!std::is_same, nullopt_t>::value, + "instantiation of optional with nullopt_t is ill-formed"); + + public: +// The different versions for C++14 and 11 are needed because deduced return +// types are not SFINAE-safe. This provides better support for things like +// generic lambdas. C.f. +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// Carries out some operation which returns an optional on the stored + /// object if there is one. + template + TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + template + TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : result(nullopt); + } + + template + constexpr auto and_then(F&& f) const& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + +# ifndef TL_OPTIONAL_NO_CONSTRR + template + constexpr auto and_then(F&& f) const&& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : result(nullopt); + } +# endif +#else + /// Carries out some operation which returns an optional on the stored + /// object if there is one. + template + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F&& f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + template + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F&& f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : result(nullopt); + } + + template + constexpr detail::invoke_result_t and_then(F&& f) const& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + +# ifndef TL_OPTIONAL_NO_CONSTRR + template + constexpr detail::invoke_result_t and_then(F&& f) const&& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : result(nullopt); + } +# endif +#endif + +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// Carries out some operation on the stored object if there is one. + template + TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) & { + return optional_map_impl(*this, std::forward(f)); + } + + template + TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) && { + return optional_map_impl(std::move(*this), std::forward(f)); + } + + template + constexpr auto map(F&& f) const& { + return optional_map_impl(*this, std::forward(f)); + } + + template + constexpr auto map(F&& f) const&& { + return optional_map_impl(std::move(*this), std::forward(f)); + } +#else + /// Carries out some operation on the stored object if there is one. + template + TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), + std::declval())) + map(F&& f) & { + return optional_map_impl(*this, std::forward(f)); + } + + template + TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), + std::declval())) + map(F&& f) && { + return optional_map_impl(std::move(*this), std::forward(f)); + } + + template + constexpr decltype(optional_map_impl(std::declval(), + std::declval())) + map(F&& f) const& { + return optional_map_impl(*this, std::forward(f)); + } + +# ifndef TL_OPTIONAL_NO_CONSTRR + template + constexpr decltype(optional_map_impl(std::declval(), + std::declval())) + map(F&& f) const&& { + return optional_map_impl(std::move(*this), std::forward(f)); + } +# endif +#endif + +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// Carries out some operation on the stored object if there is one. + template + TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) & { + return optional_map_impl(*this, std::forward(f)); + } + + template + TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) && { + return optional_map_impl(std::move(*this), std::forward(f)); + } + + template + constexpr auto transform(F&& f) const& { + return optional_map_impl(*this, std::forward(f)); + } + + template + constexpr auto transform(F&& f) const&& { + return optional_map_impl(std::move(*this), std::forward(f)); + } +#else + /// Carries out some operation on the stored object if there is one. + template + TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), + std::declval())) + transform(F&& f) & { + return optional_map_impl(*this, std::forward(f)); + } + + template + TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval(), + std::declval())) + transform(F&& f) && { + return optional_map_impl(std::move(*this), std::forward(f)); + } + + template + constexpr decltype(optional_map_impl(std::declval(), + std::declval())) + transform(F&& f) const& { + return optional_map_impl(*this, std::forward(f)); + } + +# ifndef TL_OPTIONAL_NO_CONSTRR + template + constexpr decltype(optional_map_impl(std::declval(), + std::declval())) + transform(F&& f) const&& { + return optional_map_impl(std::move(*this), std::forward(f)); + } +# endif +#endif + + /// Calls `f` if the optional is empty + template* = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + template* = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & { + return has_value() ? *this : std::forward(f)(); + } + + template* = nullptr> + optional or_else(F&& f) && { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + template* = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) && { + return has_value() ? std::move(*this) : std::forward(f)(); + } + + template* = nullptr> + optional or_else(F&& f) const& { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + template* = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) const& { + return has_value() ? *this : std::forward(f)(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template* = nullptr> + optional or_else(F&& f) const&& { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + template* = nullptr> + optional or_else(F&& f) const&& { + return has_value() ? std::move(*this) : std::forward(f)(); + } +#endif + + /// Maps the stored value with `f` if there is one, otherwise returns `u`. + template + U map_or(F&& f, U&& u) & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u); + } + + template + U map_or(F&& f, U&& u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u); + } + + template + U map_or(F&& f, U&& u) const& { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template + U map_or(F&& f, U&& u) const&& { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u); + } +#endif + + /// Maps the stored value with `f` if there is one, otherwise calls + /// `u` and returns the result. + template + detail::invoke_result_t map_or_else(F&& f, U&& u) & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u)(); + } + + template + detail::invoke_result_t map_or_else(F&& f, U&& u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u)(); + } + + template + detail::invoke_result_t map_or_else(F&& f, U&& u) const& { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u)(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template + detail::invoke_result_t map_or_else(F&& f, U&& u) const&& { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u)(); + } +#endif + + /// Returns `u` if `*this` has a value, otherwise an empty optional. + template + constexpr optional::type> conjunction(U&& u) const { + using result = optional>; + return has_value() ? result{u} : result{nullopt}; + } + + /// Returns `rhs` if `*this` is empty, otherwise the current value. + TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) & { + return has_value() ? *this : rhs; + } + + constexpr optional disjunction(const optional& rhs) const& { + return has_value() ? *this : rhs; + } + + TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) && { + return has_value() ? std::move(*this) : rhs; + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr optional disjunction(const optional& rhs) const&& { + return has_value() ? std::move(*this) : rhs; + } +#endif + + TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) & { + return has_value() ? *this : std::move(rhs); + } + + constexpr optional disjunction(optional&& rhs) const& { + return has_value() ? *this : std::move(rhs); + } + + TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) && { + return has_value() ? std::move(*this) : std::move(rhs); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr optional disjunction(optional&& rhs) const&& { + return has_value() ? std::move(*this) : std::move(rhs); + } +#endif + + /// Takes the value out of the optional, leaving it empty + optional take() { + optional ret = std::move(*this); + reset(); + return ret; + } + + using value_type = T; + + /// Constructs an optional that does not contain a value. + constexpr optional() noexcept = default; + + constexpr optional(nullopt_t) noexcept {} + + /// Copy constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR optional(const optional& rhs) = default; + + /// Move constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR optional(optional&& rhs) = default; + + /// Constructs the stored value in-place using the given arguments. + template + constexpr explicit optional( + detail::enable_if_t::value, in_place_t>, + Args&&... args) + : base(in_place, std::forward(args)...) {} + + template + TL_OPTIONAL_11_CONSTEXPR explicit optional( + detail::enable_if_t&, Args&&...>::value, + in_place_t>, + std::initializer_list il, + Args&&... args) { + this->construct(il, std::forward(args)...); + } + + /// Constructs the stored value with `u`. + template< + class U = T, + detail::enable_if_t::value>* = nullptr, + detail::enable_forward_value* = nullptr> + constexpr optional(U&& u) + : base(in_place, std::forward(u)) {} + + template< + class U = T, + detail::enable_if_t::value>* = nullptr, + detail::enable_forward_value* = nullptr> + constexpr explicit optional(U&& u) + : base(in_place, std::forward(u)) {} + + /// Converting copy constructor. + template< + class U, + detail::enable_from_other* = nullptr, + detail::enable_if_t::value>* = nullptr> + optional(const optional& rhs) { + if (rhs.has_value()) { + this->construct(*rhs); + } + } + + template* = nullptr, detail::enable_if_t::value>* = nullptr> + explicit optional(const optional& rhs) { + if (rhs.has_value()) { + this->construct(*rhs); + } + } + + /// Converting move constructor. + template< + class U, + detail::enable_from_other* = nullptr, + detail::enable_if_t::value>* = nullptr> + optional(optional&& rhs) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } + } + + template< + class U, + detail::enable_from_other* = nullptr, + detail::enable_if_t::value>* = nullptr> + explicit optional(optional&& rhs) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } + } + + /// Destroys the stored value if there is one. + ~optional() = default; + + /// Assignment to empty. + /// + /// Destroys the current value if there is one. + optional& operator=(nullopt_t) noexcept { + if (has_value()) { + this->m_value.~T(); + this->m_has_value = false; + } + + return *this; + } + + /// Copy assignment. + /// + /// Copies the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + optional& operator=(const optional& rhs) = default; + + /// Move assignment. + /// + /// Moves the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + optional& operator=(optional&& rhs) = default; + + /// Assigns the stored value from `u`, destroying the old value if there was + /// one. + template* = nullptr> + optional& operator=(U&& u) { + if (has_value()) { + this->m_value = std::forward(u); + } else { + this->construct(std::forward(u)); + } + + return *this; + } + + /// Converting copy assignment operator. + /// + /// Copies the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + template* = nullptr> + optional& operator=(const optional& rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = *rhs; + } else { + this->hard_reset(); + } + } + + if (rhs.has_value()) { + this->construct(*rhs); + } + + return *this; + } + + // TODO check exception guarantee + /// Converting move assignment operator. + /// + /// Moves the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + template* = nullptr> + optional& operator=(optional&& rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = std::move(*rhs); + } else { + this->hard_reset(); + } + } + + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } + + return *this; + } + + /// Constructs the value in-place, destroying the current one if there is + /// one. + template + T& emplace(Args&&... args) { + static_assert(std::is_constructible::value, + "T must be constructible with Args"); + + *this = nullopt; + this->construct(std::forward(args)...); + return value(); + } + + template + detail::enable_if_t< + std::is_constructible&, Args&&...>::value, + T&> + emplace(std::initializer_list il, Args&&... args) { + *this = nullopt; + this->construct(il, std::forward(args)...); + return value(); + } + + /// Swaps this optional with the other. + /// + /// If neither optionals have a value, nothing happens. + /// If both have a value, the values are swapped. + /// If one has a value, it is moved to the other and the movee is left + /// valueless. + void + swap(optional& rhs) noexcept(std::is_nothrow_move_constructible::value&& detail::is_nothrow_swappable::value) { + using std::swap; + if (has_value()) { + if (rhs.has_value()) { + swap(**this, *rhs); + } else { + new (std::addressof(rhs.m_value)) T(std::move(this->m_value)); + this->m_value.T::~T(); + } + } else if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); + rhs.m_value.T::~T(); + } + swap(this->m_has_value, rhs.m_has_value); + } + + /// Returns a pointer to the stored value + constexpr const T* operator->() const { + return std::addressof(this->m_value); + } + + TL_OPTIONAL_11_CONSTEXPR T* operator->() { + return std::addressof(this->m_value); + } + + /// Returns the stored value + TL_OPTIONAL_11_CONSTEXPR T& operator*() & { return this->m_value; } + + constexpr const T& operator*() const& { return this->m_value; } + + TL_OPTIONAL_11_CONSTEXPR T&& operator*() && { + return std::move(this->m_value); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr const T&& operator*() const&& { return std::move(this->m_value); } +#endif + + /// Returns whether or not the optional has a value + constexpr bool has_value() const noexcept { return this->m_has_value; } + + constexpr explicit operator bool() const noexcept { + return this->m_has_value; + } + + /// Returns the contained value if there is one, otherwise throws bad_optional_access + TL_OPTIONAL_11_CONSTEXPR T& value() & { + if (has_value()) + return this->m_value; + throw bad_optional_access(); + } + TL_OPTIONAL_11_CONSTEXPR const T& value() const& { + if (has_value()) + return this->m_value; + throw bad_optional_access(); + } + TL_OPTIONAL_11_CONSTEXPR T&& value() && { + if (has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + TL_OPTIONAL_11_CONSTEXPR const T&& value() const&& { + if (has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } +#endif + + /// Returns the stored value if there is one, otherwise returns `u` + template + constexpr T value_or(U&& u) const& { + static_assert(std::is_copy_constructible::value && std::is_convertible::value, + "T must be copy constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + + template + TL_OPTIONAL_11_CONSTEXPR T value_or(U&& u) && { + static_assert(std::is_move_constructible::value && std::is_convertible::value, + "T must be move constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + + /// Destroys the stored value if one exists, making the optional empty + void reset() noexcept { + if (has_value()) { + this->m_value.~T(); + this->m_has_value = false; + } + } +}; // namespace tl + +/// Compares two optional objects +template +inline constexpr bool operator==(const optional& lhs, + const optional& rhs) { + return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || *lhs == *rhs); +} +template +inline constexpr bool operator!=(const optional& lhs, + const optional& rhs) { + return lhs.has_value() != rhs.has_value() || (lhs.has_value() && *lhs != *rhs); +} +template +inline constexpr bool operator<(const optional& lhs, + const optional& rhs) { + return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); +} +template +inline constexpr bool operator>(const optional& lhs, + const optional& rhs) { + return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); +} +template +inline constexpr bool operator<=(const optional& lhs, + const optional& rhs) { + return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); +} +template +inline constexpr bool operator>=(const optional& lhs, + const optional& rhs) { + return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); +} + +/// Compares an optional to a `nullopt` +template +inline constexpr bool operator==(const optional& lhs, nullopt_t) noexcept { + return !lhs.has_value(); +} +template +inline constexpr bool operator==(nullopt_t, const optional& rhs) noexcept { + return !rhs.has_value(); +} +template +inline constexpr bool operator!=(const optional& lhs, nullopt_t) noexcept { + return lhs.has_value(); +} +template +inline constexpr bool operator!=(nullopt_t, const optional& rhs) noexcept { + return rhs.has_value(); +} +template +inline constexpr bool operator<(const optional&, nullopt_t) noexcept { + return false; +} +template +inline constexpr bool operator<(nullopt_t, const optional& rhs) noexcept { + return rhs.has_value(); +} +template +inline constexpr bool operator<=(const optional& lhs, nullopt_t) noexcept { + return !lhs.has_value(); +} +template +inline constexpr bool operator<=(nullopt_t, const optional&) noexcept { + return true; +} +template +inline constexpr bool operator>(const optional& lhs, nullopt_t) noexcept { + return lhs.has_value(); +} +template +inline constexpr bool operator>(nullopt_t, const optional&) noexcept { + return false; +} +template +inline constexpr bool operator>=(const optional&, nullopt_t) noexcept { + return true; +} +template +inline constexpr bool operator>=(nullopt_t, const optional& rhs) noexcept { + return !rhs.has_value(); +} + +/// Compares the optional with a value. +template +inline constexpr bool operator==(const optional& lhs, const U& rhs) { + return lhs.has_value() ? *lhs == rhs : false; +} +template +inline constexpr bool operator==(const U& lhs, const optional& rhs) { + return rhs.has_value() ? lhs == *rhs : false; +} +template +inline constexpr bool operator!=(const optional& lhs, const U& rhs) { + return lhs.has_value() ? *lhs != rhs : true; +} +template +inline constexpr bool operator!=(const U& lhs, const optional& rhs) { + return rhs.has_value() ? lhs != *rhs : true; +} +template +inline constexpr bool operator<(const optional& lhs, const U& rhs) { + return lhs.has_value() ? *lhs < rhs : true; +} +template +inline constexpr bool operator<(const U& lhs, const optional& rhs) { + return rhs.has_value() ? lhs < *rhs : false; +} +template +inline constexpr bool operator<=(const optional& lhs, const U& rhs) { + return lhs.has_value() ? *lhs <= rhs : true; +} +template +inline constexpr bool operator<=(const U& lhs, const optional& rhs) { + return rhs.has_value() ? lhs <= *rhs : false; +} +template +inline constexpr bool operator>(const optional& lhs, const U& rhs) { + return lhs.has_value() ? *lhs > rhs : false; +} +template +inline constexpr bool operator>(const U& lhs, const optional& rhs) { + return rhs.has_value() ? lhs > *rhs : true; +} +template +inline constexpr bool operator>=(const optional& lhs, const U& rhs) { + return lhs.has_value() ? *lhs >= rhs : false; +} +template +inline constexpr bool operator>=(const U& lhs, const optional& rhs) { + return rhs.has_value() ? lhs >= *rhs : true; +} + +template::value>* = nullptr, + detail::enable_if_t::value>* = nullptr> +void swap(optional& lhs, + optional& rhs) noexcept(noexcept(lhs.swap(rhs))) { + return lhs.swap(rhs); +} + +namespace detail { +struct i_am_secret {}; +} // namespace detail + +template::value, detail::decay_t, T>> +inline constexpr optional make_optional(U&& v) { + return optional(std::forward(v)); +} + +template +inline constexpr optional make_optional(Args&&... args) { + return optional(in_place, std::forward(args)...); +} +template +inline constexpr optional make_optional(std::initializer_list il, + Args&&... args) { + return optional(in_place, il, std::forward(args)...); +} + +#if __cplusplus >= 201703L +template +optional(T) -> optional; +#endif + +/// \exclude +namespace detail { +#ifdef TL_OPTIONAL_CXX14 +template(), *std::declval())), detail::enable_if_t::value>* = nullptr> +constexpr auto optional_map_impl(Opt&& opt, F&& f) { + return opt.has_value() + ? detail::invoke(std::forward(f), *std::forward(opt)) + : optional(nullopt); +} + +template(), *std::declval())), detail::enable_if_t::value>* = nullptr> +auto optional_map_impl(Opt&& opt, F&& f) { + if (opt.has_value()) { + detail::invoke(std::forward(f), *std::forward(opt)); + return make_optional(monostate{}); + } + + return optional(nullopt); +} +#else +template(), *std::declval())), detail::enable_if_t::value>* = nullptr> + +constexpr auto optional_map_impl(Opt&& opt, F&& f) -> optional { + return opt.has_value() + ? detail::invoke(std::forward(f), *std::forward(opt)) + : optional(nullopt); +} + +template(), *std::declval())), detail::enable_if_t::value>* = nullptr> + +auto optional_map_impl(Opt&& opt, F&& f) -> optional { + if (opt.has_value()) { + detail::invoke(std::forward(f), *std::forward(opt)); + return monostate{}; + } + + return nullopt; +} +#endif +} // namespace detail + +/// Specialization for when `T` is a reference. `optional` acts similarly +/// to a `T*`, but provides more operations and shows intent more clearly. +template +class optional { + public: +// The different versions for C++14 and 11 are needed because deduced return +// types are not SFINAE-safe. This provides better support for things like +// generic lambdas. C.f. +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + + /// Carries out some operation which returns an optional on the stored + /// object if there is one. + template + TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + template + TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + template + constexpr auto and_then(F&& f) const& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + +# ifndef TL_OPTIONAL_NO_CONSTRR + template + constexpr auto and_then(F&& f) const&& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } +# endif +#else + /// Carries out some operation which returns an optional on the stored + /// object if there is one. + template + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F&& f) & { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + template + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t and_then(F&& f) && { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + + template + constexpr detail::invoke_result_t and_then(F&& f) const& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } + +# ifndef TL_OPTIONAL_NO_CONSTRR + template + constexpr detail::invoke_result_t and_then(F&& f) const&& { + using result = detail::invoke_result_t; + static_assert(detail::is_optional::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward(f), **this) + : result(nullopt); + } +# endif +#endif + +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// Carries out some operation on the stored object if there is one. + template + TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) & { + return detail::optional_map_impl(*this, std::forward(f)); + } + + template + TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) && { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } + + template + constexpr auto map(F&& f) const& { + return detail::optional_map_impl(*this, std::forward(f)); + } + + template + constexpr auto map(F&& f) const&& { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } +#else + /// Carries out some operation on the stored object if there is one. + template + TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), + std::declval())) + map(F&& f) & { + return detail::optional_map_impl(*this, std::forward(f)); + } + + template + TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), + std::declval())) + map(F&& f) && { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } + + template + constexpr decltype(detail::optional_map_impl(std::declval(), + std::declval())) + map(F&& f) const& { + return detail::optional_map_impl(*this, std::forward(f)); + } + +# ifndef TL_OPTIONAL_NO_CONSTRR + template + constexpr decltype(detail::optional_map_impl(std::declval(), + std::declval())) + map(F&& f) const&& { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } +# endif +#endif + +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// Carries out some operation on the stored object if there is one. + template + TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) & { + return detail::optional_map_impl(*this, std::forward(f)); + } + + template + TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) && { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } + + template + constexpr auto transform(F&& f) const& { + return detail::optional_map_impl(*this, std::forward(f)); + } + + template + constexpr auto transform(F&& f) const&& { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } +#else + /// Carries out some operation on the stored object if there is one. + template + TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), + std::declval())) + transform(F&& f) & { + return detail::optional_map_impl(*this, std::forward(f)); + } + + /// \group map + /// \synopsis template auto transform(F &&f) &&; + template + TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval(), + std::declval())) + transform(F&& f) && { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } + + template + constexpr decltype(detail::optional_map_impl(std::declval(), + std::declval())) + transform(F&& f) const& { + return detail::optional_map_impl(*this, std::forward(f)); + } + +# ifndef TL_OPTIONAL_NO_CONSTRR + template + constexpr decltype(detail::optional_map_impl(std::declval(), + std::declval())) + transform(F&& f) const&& { + return detail::optional_map_impl(std::move(*this), std::forward(f)); + } +# endif +#endif + + /// Calls `f` if the optional is empty + template* = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + template* = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & { + return has_value() ? *this : std::forward(f)(); + } + + template* = nullptr> + optional or_else(F&& f) && { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + template* = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) && { + return has_value() ? std::move(*this) : std::forward(f)(); + } + + template* = nullptr> + optional or_else(F&& f) const& { + if (has_value()) + return *this; + + std::forward(f)(); + return nullopt; + } + + template* = nullptr> + optional TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) const& { + return has_value() ? *this : std::forward(f)(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template* = nullptr> + optional or_else(F&& f) const&& { + if (has_value()) + return std::move(*this); + + std::forward(f)(); + return nullopt; + } + + template* = nullptr> + optional or_else(F&& f) const&& { + return has_value() ? std::move(*this) : std::forward(f)(); + } +#endif + + /// Maps the stored value with `f` if there is one, otherwise returns `u` + template + U map_or(F&& f, U&& u) & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u); + } + + template + U map_or(F&& f, U&& u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u); + } + + template + U map_or(F&& f, U&& u) const& { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template + U map_or(F&& f, U&& u) const&& { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u); + } +#endif + + /// Maps the stored value with `f` if there is one, otherwise calls + /// `u` and returns the result. + template + detail::invoke_result_t map_or_else(F&& f, U&& u) & { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u)(); + } + + template + detail::invoke_result_t map_or_else(F&& f, U&& u) && { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u)(); + } + + template + detail::invoke_result_t map_or_else(F&& f, U&& u) const& { + return has_value() ? detail::invoke(std::forward(f), **this) + : std::forward(u)(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template + detail::invoke_result_t map_or_else(F&& f, U&& u) const&& { + return has_value() ? detail::invoke(std::forward(f), std::move(**this)) + : std::forward(u)(); + } +#endif + + /// Returns `u` if `*this` has a value, otherwise an empty optional. + template + constexpr optional::type> conjunction(U&& u) const { + using result = optional>; + return has_value() ? result{u} : result{nullopt}; + } + + /// Returns `rhs` if `*this` is empty, otherwise the current value. + TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) & { + return has_value() ? *this : rhs; + } + + constexpr optional disjunction(const optional& rhs) const& { + return has_value() ? *this : rhs; + } + + TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) && { + return has_value() ? std::move(*this) : rhs; + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr optional disjunction(const optional& rhs) const&& { + return has_value() ? std::move(*this) : rhs; + } +#endif + + TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) & { + return has_value() ? *this : std::move(rhs); + } + + constexpr optional disjunction(optional&& rhs) const& { + return has_value() ? *this : std::move(rhs); + } + + TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) && { + return has_value() ? std::move(*this) : std::move(rhs); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr optional disjunction(optional&& rhs) const&& { + return has_value() ? std::move(*this) : std::move(rhs); + } +#endif + + /// Takes the value out of the optional, leaving it empty + optional take() { + optional ret = std::move(*this); + reset(); + return ret; + } + + using value_type = T&; + + /// Constructs an optional that does not contain a value. + constexpr optional() noexcept + : m_value(nullptr) {} + + constexpr optional(nullopt_t) noexcept + : m_value(nullptr) {} + + /// Copy constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR optional(const optional& rhs) noexcept = default; + + /// Move constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR optional(optional&& rhs) = default; + + /// Constructs the stored value with `u`. + template>::value>* = nullptr> + constexpr optional(U&& u) noexcept + : m_value(std::addressof(u)) { + static_assert(std::is_lvalue_reference::value, "U must be an lvalue"); + } + + template + constexpr explicit optional(const optional& rhs) noexcept + : optional(*rhs) {} + + /// No-op + ~optional() = default; + + /// Assignment to empty. + /// + /// Destroys the current value if there is one. + optional& operator=(nullopt_t) noexcept { + m_value = nullptr; + return *this; + } + + /// Copy assignment. + /// + /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise + /// resets the stored value in `*this`. + optional& operator=(const optional& rhs) = default; + + /// Rebinds this optional to `u`. + template>::value>* = nullptr> + optional& operator=(U&& u) { + static_assert(std::is_lvalue_reference::value, "U must be an lvalue"); + m_value = std::addressof(u); + return *this; + } + + /// Converting copy assignment operator. + /// + /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise + /// resets the stored value in `*this`. + template + optional& operator=(const optional& rhs) noexcept { + m_value = std::addressof(rhs.value()); + return *this; + } + + /// Rebinds this optional to `u`. + template>::value>* = nullptr> + optional& emplace(U&& u) noexcept { + return *this = std::forward(u); + } + + void swap(optional& rhs) noexcept { std::swap(m_value, rhs.m_value); } + + /// Returns a pointer to the stored value + constexpr const T* operator->() const noexcept { return m_value; } + + TL_OPTIONAL_11_CONSTEXPR T* operator->() noexcept { return m_value; } + + /// Returns the stored value + TL_OPTIONAL_11_CONSTEXPR T& operator*() noexcept { return *m_value; } + + constexpr const T& operator*() const noexcept { return *m_value; } + + constexpr bool has_value() const noexcept { return m_value != nullptr; } + + constexpr explicit operator bool() const noexcept { + return m_value != nullptr; + } + + /// Returns the contained value if there is one, otherwise throws bad_optional_access + TL_OPTIONAL_11_CONSTEXPR T& value() { + if (has_value()) + return *m_value; + throw bad_optional_access(); + } + TL_OPTIONAL_11_CONSTEXPR const T& value() const { + if (has_value()) + return *m_value; + throw bad_optional_access(); + } + + /// Returns the stored value if there is one, otherwise returns `u` + template + constexpr T value_or(U&& u) const& noexcept { + static_assert(std::is_copy_constructible::value && std::is_convertible::value, + "T must be copy constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + + /// \group value_or + template + TL_OPTIONAL_11_CONSTEXPR T value_or(U&& u) && noexcept { + static_assert(std::is_move_constructible::value && std::is_convertible::value, + "T must be move constructible and convertible from U"); + return has_value() ? **this : static_cast(std::forward(u)); + } + + /// Destroys the stored value if one exists, making the optional empty + void reset() noexcept { m_value = nullptr; } + + private: + T* m_value; +}; // namespace tl + +} // namespace tl + +namespace std { +// TODO SFINAE +template +struct hash> { + ::std::size_t operator()(const tl::optional& o) const { + if (!o.has_value()) + return 0; + + return std::hash>()(*o); + } +}; +} // namespace std + +#endif diff --git a/libs/libvtrutil/src/vtr_optional.h b/libs/libvtrutil/src/vtr_optional.h new file mode 100644 index 00000000000..7f714c612ff --- /dev/null +++ b/libs/libvtrutil/src/vtr_optional.h @@ -0,0 +1,43 @@ +#pragma once + +/** std::optional-like interface with optional references. + * currently: import TartanLlama's optional into the vtr namespace + * documentation at https://tl.tartanllama.xyz/en/latest/api/optional.html + * there are three main uses of this: + * 1. replace pointers when refactoring legacy code + * optional (reference) is in many ways a pointer, it even has * and -> operators, + * but it can't be allocated or freed. this property is very helpful in refactoring. + * 2. explicit alternative for containers + * optional (non-reference) allows you to put non-empty-initializable + * objects into a container which owns them. it is an alternative to + * unique_ptr in that sense, but with a cleaner interface. + * 3. function return types + * returning an optional gives the caller a clear hint to check the return value. + * + * Q: why not use std::optional? + * A: std::optional doesn't allow optional due to a disagreement about + * what it means to assign to an optional reference. tl::optional permits this, with + * "rebind on assignment" behavior. this means opt acts very similarly to a pointer. + * Q: why do we need opt? there's already T*. + * A: in an ideal world where all pointers are aliases to existing values and nothing + * else, opt wouldn't be that necessary. however VPR is full of legacy code where + * the usual C++ conventions about pointers don't apply. + * when refactoring such code, turning all pointers into opt helps a lot. + * it can't be allocated or freed and doesn't allow pointer arithmetic. + * in that aspect it acts as a "enforced proper C++ ptr". + * that's why I think it's worth keeping around in the codebase. */ + +#include "tl_optional.hpp" + +namespace vtr { +template +using optional = tl::optional; + +using nullopt_t = tl::nullopt_t; +static constexpr nullopt_t nullopt = tl::nullopt; + +using in_place_t = tl::in_place_t; +static constexpr in_place_t in_place = tl::in_place; + +using bad_optional_access = tl::bad_optional_access; +} // namespace vtr diff --git a/utils/fasm/src/fasm.cpp b/utils/fasm/src/fasm.cpp index 50c8fff9bab..925799f22a4 100644 --- a/utils/fasm/src/fasm.cpp +++ b/utils/fasm/src/fasm.cpp @@ -22,7 +22,6 @@ #include "atom_netlist_utils.h" #include "netlist_writer.h" #include "vpr_utils.h" -#include "route_tree_timing.h" #include "fasm_utils.h" @@ -619,18 +618,19 @@ void FasmWriterVisitor::check_for_lut(const t_pb* atom) { } void FasmWriterVisitor::visit_atom_impl(const t_pb* atom) { - check_for_lut(atom); - check_for_param(atom); + check_for_lut(atom); + check_for_param(atom); } -void FasmWriterVisitor::walk_route_tree(const RRGraphBuilder& rr_graph_builder, const t_rt_node *root) { - for (t_linked_rt_edge* edge = root->u.child_list; edge != nullptr; edge = edge->next) { - auto *meta = vpr::rr_edge_metadata(rr_graph_builder, root->inode, edge->child->inode, edge->iswitch, fasm_features); +void FasmWriterVisitor::walk_route_tree(const RRGraphBuilder& rr_graph_builder, const RouteTreeNode& root) { + for(auto& child: root.child_nodes()){ + auto* meta = vpr::rr_edge_metadata(rr_graph_builder, size_t(root.inode), size_t(child.inode), size_t(child.parent_switch), fasm_features); + if(meta != nullptr) { output_fasm_features(meta->as_string().get(strings_), "", ""); } - walk_route_tree(rr_graph_builder, edge->child); + walk_route_tree(rr_graph_builder, child); } } @@ -638,12 +638,9 @@ void FasmWriterVisitor::walk_routing() { auto& route_ctx = g_vpr_ctx.mutable_routing(); const auto& device_ctx = g_vpr_ctx.device(); - for(const auto &trace : route_ctx.trace) { - t_trace *head = trace.head; - if (!head) continue; - t_rt_node* root = traceback_to_route_tree(head, is_flat_); - walk_route_tree(device_ctx.rr_graph_builder, root); - free_route_tree(root); + for(const auto &tree : route_ctx.route_trees) { + if (!tree) continue; + walk_route_tree(device_ctx.rr_graph_builder, tree.value().root()); } } diff --git a/utils/fasm/src/fasm.h b/utils/fasm/src/fasm.h index d8ab8decd9d..4239af18edc 100644 --- a/utils/fasm/src/fasm.h +++ b/utils/fasm/src/fasm.h @@ -22,7 +22,7 @@ #include "netlist_writer.h" #include "lut.h" #include "parameters.h" -#include "route_tree_type.h" +#include "route_tree_fwd.h" namespace fasm { @@ -73,7 +73,7 @@ class FasmWriterVisitor : public NetlistVisitor { void check_for_lut(const t_pb* atom); void output_fasm_mux(std::string fasm_mux, t_interconnect *interconnect, const t_pb_graph_pin *mux_input_pin); void walk_routing(); - void walk_route_tree(const RRGraphBuilder& rr_graph_builder, const t_rt_node *root); + void walk_route_tree(const RRGraphBuilder& rr_graph_builder, const RouteTreeNode& root); std::string build_clb_prefix(const t_pb *pb, const t_pb_graph_node* pb_graph_node, bool* is_parent_pb_null) const; const LutOutputDefinition* find_lut(const t_pb_graph_node* pb_graph_node); void check_for_param(const t_pb *atom); diff --git a/utils/fasm/src/main.cpp b/utils/fasm/src/main.cpp index 1b6eb0e7b1f..61c8a7f44fa 100644 --- a/utils/fasm/src/main.cpp +++ b/utils/fasm/src/main.cpp @@ -120,11 +120,7 @@ int main(int argc, const char **argv) { (float) (entire_flow_end - entire_flow_begin) / CLOCKS_PER_SEC); /* free data structures */ - if(is_flat) { - vpr_free_all((const Netlist<>&) g_vpr_ctx.atom().nlist, Arch, vpr_setup); - } else { - vpr_free_all((const Netlist<>&) g_vpr_ctx.clustering().clb_nlist, Arch, vpr_setup); - } + vpr_free_all(Arch, vpr_setup); } catch (const tatum::Error& tatum_error) { diff --git a/utils/fasm/test/test_fasm.cpp b/utils/fasm/test/test_fasm.cpp index 392ed7d3b63..3632f8fae81 100644 --- a/utils/fasm/test/test_fasm.cpp +++ b/utils/fasm/test/test_fasm.cpp @@ -1,6 +1,7 @@ #include "catch2/catch_test_macros.hpp" #include "catch2/matchers/catch_matchers_all.hpp" +#include "route_common.h" #include "vpr_api.h" #include "vtr_util.h" #include "rr_metadata.h" @@ -23,7 +24,6 @@ namespace { // ============================================================================ -using Catch::Matchers::Equals; using Catch::Matchers::StartsWith; using Catch::Matchers::ContainsSubstring; @@ -296,7 +296,7 @@ TEST_CASE("fasm_integration_test", "[fasm]") { echo_enabled, echo_file_name, is_flat); - vpr_free_all((const Netlist<>&) g_vpr_ctx.clustering().clb_nlist, arch, vpr_setup); + vpr_free_all(arch, vpr_setup); } t_vpr_setup vpr_setup; @@ -586,20 +586,17 @@ TEST_CASE("fasm_integration_test", "[fasm]") { // Verify routes const auto & route_ctx = g_vpr_ctx.routing(); - for(const auto &trace : route_ctx.trace) { - const t_trace *head = trace.head; - while(head != nullptr) { - const t_trace *next = head->next; - - if(next != nullptr && head->iswitch != OPEN) { - const auto next_inode = next->index; - auto iter = routing_edges.find(std::make_tuple(head->index, next_inode, head->iswitch)); + for(const auto& root : route_ctx.route_trees) { + if(!root) + continue; + + for(auto& rt_node: root.value().all_nodes()) { + for(auto& child_node: rt_node.child_nodes()){ + auto iter = routing_edges.find(std::make_tuple(size_t(rt_node.inode), size_t(child_node.inode), size_t(child_node.parent_switch))); if (iter == routing_edges.end()) { - FAIL_CHECK("source: " << head->index << " sink: " << next_inode << " switch:" << head->iswitch); + FAIL_CHECK("source: " << size_t(rt_node.inode) << " sink: " << size_t(child_node.inode) << " switch:" << size_t(child_node.parent_switch)); } } - - head = next; } } @@ -635,7 +632,7 @@ TEST_CASE("fasm_integration_test", "[fasm]") { CHECK(found_mux4); - vpr_free_all((const Netlist<>&) g_vpr_ctx.clustering().clb_nlist, arch, vpr_setup); + vpr_free_all(arch, vpr_setup); } } // namespace diff --git a/utils/fasm/test/test_lut.cpp b/utils/fasm/test/test_lut.cpp index 8c8d18c2664..bea5edc698d 100644 --- a/utils/fasm/test/test_lut.cpp +++ b/utils/fasm/test/test_lut.cpp @@ -12,7 +12,7 @@ TEST_CASE("default_lut", "[fasm]") { fasm::Lut lut(num_inputs); const LogicVec &table = lut.table(); - CHECK(table.size() == (1 << num_inputs)); + CHECK(table.size() == size_t(1 << num_inputs)); for(const vtr::LogicValue & value : table) { CHECK(value == vtr::LogicValue::FALSE); @@ -27,7 +27,7 @@ TEST_CASE("const_true", "[fasm]") { lut.SetConstant(vtr::LogicValue::TRUE); const LogicVec &table = lut.table(); - CHECK(table.size() == (1 << num_inputs)); + CHECK(table.size() == size_t(1 << num_inputs)); for(const vtr::LogicValue & value : table) { CHECK(value == vtr::LogicValue::TRUE); @@ -42,7 +42,7 @@ TEST_CASE("const_false", "[fasm]") { lut.SetConstant(vtr::LogicValue::FALSE); const LogicVec &table = lut.table(); - CHECK(table.size() == (1 << num_inputs)); + CHECK(table.size() == size_t(1 << num_inputs)); for(const vtr::LogicValue & value : table) { CHECK(value == vtr::LogicValue::FALSE); @@ -58,7 +58,7 @@ TEST_CASE("wire", "[fasm]") { lut.CreateWire(input_pin); const LogicVec &table = lut.table(); - CHECK(table.size() == (1 << num_inputs)); + CHECK(table.size() == size_t(1 << num_inputs)); for(size_t i = 0; i < table.size(); ++i) { if(((1 << input_pin) & i) != 0) { CHECK(table[i] == vtr::LogicValue::TRUE); diff --git a/utils/route_diag/src/main.cpp b/utils/route_diag/src/main.cpp index a36bb4b2aa5..752fc38d827 100644 --- a/utils/route_diag/src/main.cpp +++ b/utils/route_diag/src/main.cpp @@ -31,10 +31,9 @@ #include "RoutingDelayCalculator.h" #include "place_and_route.h" #include "router_delay_profiling.h" -#include "route_tree_type.h" +#include "route_tree.h" #include "route_common.h" #include "route_timing.h" -#include "route_tree_timing.h" #include "route_export.h" #include "rr_graph.h" #include "rr_graph2.h" @@ -74,7 +73,7 @@ static void do_one_route(const Netlist<>& net_list, const auto& rr_graph = device_ctx.rr_graph; auto& route_ctx = g_vpr_ctx.routing(); - t_rt_node* rt_root = init_route_tree_to_source_no_net(source_node); + RouteTree tree((RRNodeId(source_node))); /* Update base costs according to fanout and criticality rules */ update_rr_base_costs(1); @@ -118,31 +117,29 @@ static void do_one_route(const Netlist<>& net_list, -1, false, std::unordered_map()); - std::tie(found_path, cheapest) = router.timing_driven_route_connection_from_route_tree(rt_root, - sink_node, - cost_params, - bounding_box, - router_stats, - conn_params); + std::tie(found_path, cheapest) = router.timing_driven_route_connection_from_route_tree(tree.root(), + sink_node, + cost_params, + bounding_box, + router_stats, + conn_params); if (found_path) { VTR_ASSERT(cheapest.index == sink_node); - t_rt_node* rt_node_of_sink = update_route_tree(&cheapest, OPEN, nullptr, router_opts.flat_routing); + vtr::optional rt_node_of_sink; + std::tie(std::ignore, rt_node_of_sink) = tree.update_from_heap(&cheapest, OPEN, nullptr, router_opts.flat_routing); //find delay - float net_delay = rt_node_of_sink->Tdel; + float net_delay = rt_node_of_sink.value().Tdel; VTR_LOG("Routed successfully, delay = %g!\n", net_delay); VTR_LOG("\n"); - print_route_tree_node(rt_root); - VTR_LOG("\n"); - print_route_tree(rt_root); + tree.print(); VTR_LOG("\n"); - VTR_ASSERT_MSG(route_ctx.rr_node_route_inf[rt_root->inode].occ() <= rr_graph.node_capacity(RRNodeId(rt_root->inode)), "SOURCE should never be congested"); - free_route_tree(rt_root); + VTR_ASSERT_MSG(route_ctx.rr_node_route_inf[size_t(tree.root().inode)].occ() <= rr_graph.node_capacity(tree.root().inode), "SOURCE should never be congested"); } else { - VTR_LOG("Routed failed"); + VTR_LOG("Routing failed"); } //Reset for the next router call @@ -347,10 +344,10 @@ int main(int argc, const char **argv) { vpr_setup.Segments, is_flat); } - free_routing_structs(net_list); + free_routing_structs(); /* free data structures */ - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); } catch (const tatum::Error& tatum_error) { vtr::printf_error(__FILE__, __LINE__, "STA Engine: %s\n", tatum_error.what()); diff --git a/vpr/src/base/old_traceback.cpp b/vpr/src/base/old_traceback.cpp new file mode 100644 index 00000000000..317a4fe27a0 --- /dev/null +++ b/vpr/src/base/old_traceback.cpp @@ -0,0 +1,222 @@ +#include "old_traceback.h" + +#include "clustered_netlist_fwd.h" +#include "globals.h" +#include "vtr_assert.h" + +#include + +std::pair traceback_from_route_tree_recurr(t_trace* head, t_trace* tail, const RouteTreeNode& node); +bool validate_traceback_recurr(t_trace* trace, std::set& seen_rr_nodes); +void free_trace_data(t_trace* tptr); + +/* Builds a skeleton route tree from a traceback + * does not calculate R_upstream, C_downstream, or Tdel (left uninitialized) + * returns the root of the converted route tree */ +vtr::optional TracebackCompat::traceback_to_route_tree(t_trace* head) { + if (head == nullptr) + return vtr::nullopt; + + RouteTree tree(RRNodeId(head->index)); + + if (head->next) + traceback_to_route_tree_x(head->next, tree, tree._root, RRSwitchId(head->iswitch)); + + tree.reload_timing(); + return tree; +} + +/* Add the path indicated by the trace to parent */ +void TracebackCompat::traceback_to_route_tree_x(t_trace* trace, RouteTree& tree, RouteTreeNode* parent, RRSwitchId parent_switch) { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + RRNodeId inode = RRNodeId(trace->index); + + RouteTreeNode* new_node = new RouteTreeNode(inode, parent_switch, parent); + tree.add_node(parent, new_node); + new_node->net_pin_index = trace->net_pin_index; + new_node->R_upstream = std::numeric_limits::quiet_NaN(); + new_node->C_downstream = std::numeric_limits::quiet_NaN(); + new_node->Tdel = std::numeric_limits::quiet_NaN(); + auto node_type = rr_graph.node_type(inode); + if (node_type == IPIN || node_type == SINK) + new_node->re_expand = false; + else + new_node->re_expand = true; + + if (rr_graph.node_type(inode) == SINK) { + /* The traceback returns to the previous branch point if there is more than one SINK, otherwise we are at the last SINK */ + if (trace->next) { + RRNodeId next_rr_node = RRNodeId(trace->next->index); + RouteTreeNode* branch = tree._rr_node_to_rt_node.at(next_rr_node); + VTR_ASSERT(trace->next->next); + traceback_to_route_tree_x(trace->next->next, tree, branch, RRSwitchId(trace->next->iswitch)); + } + } else { + traceback_to_route_tree_x(trace->next, tree, new_node, RRSwitchId(trace->iswitch)); + } +} + +std::pair traceback_from_route_tree_recurr(t_trace* head, t_trace* tail, const RouteTreeNode& node) { + if (!node.is_leaf()) { + //Recursively add children + for (auto& child : node.child_nodes()) { + t_trace* curr = alloc_trace_data(); + curr->index = size_t(node.inode); + curr->net_pin_index = node.net_pin_index; + curr->iswitch = size_t(child.parent_switch); + curr->next = nullptr; + + if (tail) { + VTR_ASSERT(tail->next == nullptr); + tail->next = curr; + } + + tail = curr; + + if (!head) { + head = tail; + } + + std::tie(head, tail) = traceback_from_route_tree_recurr(head, tail, child); + } + } else { + //Leaf + t_trace* curr = alloc_trace_data(); + curr->index = size_t(node.inode); + curr->net_pin_index = node.net_pin_index; + curr->iswitch = OPEN; + curr->next = nullptr; + + if (tail) { + VTR_ASSERT(tail->next == nullptr); + tail->next = curr; + } + + tail = curr; + + if (!head) { + head = tail; + } + } + + return {head, tail}; +} + +/* Creates a traceback from the route tree */ +t_trace* TracebackCompat::traceback_from_route_tree(const RouteTree& tree) { + t_trace* head; + t_trace* tail; + + std::tie(head, tail) = traceback_from_route_tree_recurr(nullptr, nullptr, tree.root()); + + VTR_ASSERT(validate_traceback(head)); + + return head; +} + +void print_traceback(const t_trace* trace) { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + auto& route_ctx = g_vpr_ctx.routing(); + const t_trace* prev = nullptr; + while (trace) { + int inode = trace->index; + VTR_LOG("%d (%s)", inode, rr_node_typename[rr_graph.node_type(RRNodeId(inode))]); + + if (trace->iswitch == OPEN) { + VTR_LOG(" !"); //End of branch + } + + if (prev && prev->iswitch != OPEN && !rr_graph.rr_switch_inf(RRSwitchId(prev->iswitch)).configurable()) { + VTR_LOG("*"); //Reached non-configurably + } + + if (route_ctx.rr_node_route_inf[inode].occ() > rr_graph.node_capacity(RRNodeId(inode))) { + VTR_LOG(" x"); //Overused + } + VTR_LOG("\n"); + prev = trace; + trace = trace->next; + } + VTR_LOG("\n"); +} + +bool validate_traceback(t_trace* trace) { + std::set seen_rr_nodes; + + return validate_traceback_recurr(trace, seen_rr_nodes); +} + +bool validate_traceback_recurr(t_trace* trace, std::set& seen_rr_nodes) { + if (!trace) { + return true; + } + + seen_rr_nodes.insert(trace->index); + + t_trace* next = trace->next; + + if (next) { + if (trace->iswitch == OPEN) { //End of a branch + + //Verify that the next element (branch point) has been already seen in the traceback so far + if (!seen_rr_nodes.count(next->index)) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Traceback branch point %d not found", next->index); + } else { + //Recurse along the new branch + return validate_traceback_recurr(next, seen_rr_nodes); + } + } else { //Midway along branch + + //Check there is an edge connecting trace and next + + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + bool found = false; + for (t_edge_size iedge = 0; iedge < rr_graph.num_edges(RRNodeId(trace->index)); ++iedge) { + int to_node = size_t(rr_graph.edge_sink_node(RRNodeId(trace->index), iedge)); + + if (to_node == next->index) { + found = true; + + //Verify that the switch matches + int rr_iswitch = rr_graph.edge_switch(RRNodeId(trace->index), iedge); + if (trace->iswitch != rr_iswitch) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Traceback mismatched switch type: traceback %d rr_graph %d (RR nodes %d -> %d)\n", + trace->iswitch, rr_iswitch, + trace->index, to_node); + } + break; + } + } + + if (!found) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Traceback no RR edge between RR nodes %d -> %d\n", trace->index, next->index); + } + + //Recurse + return validate_traceback_recurr(next, seen_rr_nodes); + } + } + + VTR_ASSERT(!next); + return true; //End of traceback +} + +t_trace* +alloc_trace_data() { + return (t_trace*)malloc(sizeof(t_trace)); +} + +void free_trace_data(t_trace* tptr) { + free(tptr); +} + +void free_traceback(t_trace* tptr) { + while (tptr != nullptr) { + t_trace* tempptr = tptr->next; + free_trace_data(tptr); + tptr = tempptr; + } +} diff --git a/vpr/src/base/old_traceback.h b/vpr/src/base/old_traceback.h new file mode 100644 index 00000000000..6c9a9ed2581 --- /dev/null +++ b/vpr/src/base/old_traceback.h @@ -0,0 +1,49 @@ +#pragma once + +#include "route_tree.h" + +/* Contains the old traceback structure for compatibility with .route files. + * Use route/route_tree_type.h for new code. */ + +/** + * @brief Legacy element used to store the traceback (routing) of each net. + * + * @param index Array index (ID) of this routing resource node. + * @param net_pin_index: Net pin index associated with the node. This value + * ranges from 1 to fanout [1..num_pins-1]. For cases when + * different speed paths are taken to the same SINK for + * different pins, node index cannot uniquely identify + * each SINK, so the net pin index guarantees an unique + * identification for each SINK node. For non-SINK nodes + * and for SINK nodes with no associated net pin index + * (i.e. special SINKs like the source of a clock tree + * which do not correspond to an actual netlist connection), + * the value for this member should be set to OPEN (-1). + * @param iswitch Index of the switch type used to go from this rr_node to + * the next one in the routing. OPEN if there is no next node + * (i.e. this node is the last one (a SINK) in a branch of the + * net's routing). + * @param next Pointer to the next traceback element in this route. + */ +struct t_trace { + t_trace* next; + int index; + int net_pin_index = -1; + //int net_pin_index = OPEN; + short iswitch; +}; + +/* This class is a friend of RouteTree so it can build one and we don't have to clutter route tree code */ +class TracebackCompat { + public: + static t_trace* traceback_from_route_tree(const RouteTree& tree); + static vtr::optional traceback_to_route_tree(t_trace* head); + + private: + static void traceback_to_route_tree_x(t_trace* trace, RouteTree& tree, RouteTreeNode* parent, RRSwitchId parent_switch); +}; + +t_trace* alloc_trace_data(); +void free_traceback(t_trace* trace); +void print_traceback(const t_trace* trace); +bool validate_traceback(t_trace* trace); diff --git a/vpr/src/base/place_and_route.cpp b/vpr/src/base/place_and_route.cpp index d12a203390c..c34cd9cfbae 100644 --- a/vpr/src/base/place_and_route.cpp +++ b/vpr/src/base/place_and_route.cpp @@ -66,8 +66,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, std::shared_ptr timing_info, std::shared_ptr delay_calc, bool is_flat) { - vtr::vector best_routing; /* Saves the best routing found so far. */ - + vtr::vector> best_routing; /* Saves the best routing found so far. */ int current, low, high, final; bool success, prev_success, prev2_success, Fc_clipped = false; bool using_minw_hint = false; @@ -100,8 +99,6 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, graph_directionality = (det_routing_arch->directionality == BI_DIRECTIONAL ? GRAPH_BIDIR : GRAPH_UNIDIR); } - best_routing = alloc_saved_routing(router_net_list); - VTR_ASSERT(net_delay.size()); if (det_routing_arch->directionality == BI_DIRECTIONAL) @@ -226,8 +223,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, } /* Save routing in case it is best. */ - save_routing(router_net_list, - best_routing, + save_routing(best_routing, route_ctx.clb_opins_used_locally, saved_clb_opins_used_locally); @@ -351,8 +347,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, if (success && Fc_clipped == false) { final = current; - save_routing(router_net_list, - best_routing, + save_routing(best_routing, route_ctx.clb_opins_used_locally, saved_clb_opins_used_locally); @@ -401,8 +396,7 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, router_opts.has_choking_spot, is_flat); - restore_routing(router_net_list, - best_routing, + restore_routing(best_routing, route_ctx.clb_opins_used_locally, saved_clb_opins_used_locally); @@ -416,7 +410,6 @@ int binary_search_place_and_route(const Netlist<>& placement_net_list, filename_opts.RouteFile.c_str(), is_flat); - free_saved_routing(router_net_list, best_routing); fflush(stdout); return (final); @@ -497,9 +490,9 @@ t_chan_width init_chan(int cfactor, t_chan_width_dist chan_width_dist, t_graph_t } /** - * @brief Computes the channel width and adjusts it to be an an even number if unidirectional + * @brief Computes the channel width and adjusts it to be an an even number if unidirectional * since unidirectional graphs need to have paired wires. - * + * * @param cfactor Channel width factor: multiplier on the channel width distribution (usually the number of tracks in the widest channel). * @param chan_dist Channel width distribution. * @param x The distance (between 0 and 1) we are across the chip. diff --git a/vpr/src/base/read_route.cpp b/vpr/src/base/read_route.cpp index 4cc85050bb4..348beea3033 100644 --- a/vpr/src/base/read_route.cpp +++ b/vpr/src/base/read_route.cpp @@ -26,6 +26,7 @@ #include "atom_netlist_utils.h" #include "rr_graph.h" #include "vtr_assert.h" +#include "vtr_digest.h" #include "vtr_util.h" #include "tatum/echo_writer.hpp" #include "vtr_log.h" @@ -43,9 +44,12 @@ #include "route_export.h" #include "echo_files.h" #include "route_common.h" +#include "route_tree.h" #include "read_route.h" #include "binary_heap.h" +#include "old_traceback.h" + /*************Functions local to this module*************/ static void process_route(std::ifstream& fp, const char* filename, int& lineno); static void process_nodes(std::ifstream& fp, ClusterNetId inet, const char* filename, int& lineno); @@ -55,6 +59,7 @@ static void format_coordinates(int& x, int& y, std::string coord, ClusterNetId n static void format_pin_info(std::string& pb_name, std::string& port_name, int& pb_pin_num, std::string input); static std::string format_name(std::string name); static bool check_rr_graph_connectivity(RRNodeId prev_node, RRNodeId node); +void print_route(const Netlist<>& net_list, FILE* fp, bool is_flat); /*************Global Functions****************************/ @@ -223,7 +228,8 @@ static void process_nodes(std::ifstream& fp, ClusterNetId inet, const char* file auto& route_ctx = g_vpr_ctx.mutable_routing(); auto& place_ctx = g_vpr_ctx.placement(); - t_trace* tptr = route_ctx.trace[(const ParentNetId&)inet].head; + t_trace* head_ptr = nullptr; + t_trace* tptr = nullptr; /*remember the position of the last line in order to go back*/ std::streampos oldpos = fp.tellg(); @@ -249,13 +255,13 @@ static void process_nodes(std::ifstream& fp, ClusterNetId inet, const char* file /*End of the nodes list, * return by moving the position of next char of input stream to be before net*/ fp.seekg(oldpos); - return; + break; } else if (input == "\n\nUsed in local cluster only, reserved one CLB pin\n\n") { if (cluster_ctx.clb_nlist.net_sinks(inet).size() != 0) { vpr_throw(VPR_ERROR_ROUTE, filename, lineno, "Net %d should be used in local cluster only, reserved one CLB pin"); } - return; + break; } else if (tokens[0] == "Node:") { /*An actual line, go through each node and add it to the route tree*/ inode = atoi(tokens[1].c_str()); @@ -372,17 +378,17 @@ static void process_nodes(std::ifstream& fp, ClusterNetId inet, const char* file "%d (sink) node does not have net pin index. If you are using an old .route file without this information, please re-generate the routing.", inode); } } else { - net_pin_index = OPEN; //net pin index is invalid for non-SINKs + net_pin_index = OPEN; // net pin index is invalid for non-SINKs } - /* Allocate and load correct values to trace.head*/ + /* Allocate and load correct values to trace.head */ if (node_count == 0) { - route_ctx.trace[(const ParentNetId&)inet].head = alloc_trace_data(); - route_ctx.trace[(const ParentNetId&)inet].head->index = inode; - route_ctx.trace[(const ParentNetId&)inet].head->net_pin_index = net_pin_index; - route_ctx.trace[(const ParentNetId&)inet].head->iswitch = switch_id; - route_ctx.trace[(const ParentNetId&)inet].head->next = nullptr; - tptr = route_ctx.trace[(const ParentNetId&)inet].head; + head_ptr = alloc_trace_data(); + head_ptr->index = inode; + head_ptr->net_pin_index = net_pin_index; + head_ptr->iswitch = switch_id; + head_ptr->next = nullptr; + tptr = head_ptr; node_count++; } else { tptr->next = alloc_trace_data(); @@ -394,9 +400,14 @@ static void process_nodes(std::ifstream& fp, ClusterNetId inet, const char* file node_count++; } } - /*stores last line so can easily go back to read*/ + /* stores last line so can easily go back to read */ oldpos = fp.tellg(); } + + /* Convert to route_tree after reading */ + VTR_ASSERT(validate_traceback(head_ptr)); + route_ctx.route_trees[inet] = TracebackCompat::traceback_to_route_tree(head_ptr); + free_traceback(head_ptr); } /** @@ -538,3 +549,157 @@ static bool check_rr_graph_connectivity(RRNodeId prev_node, RRNodeId node) { return false; } + +void print_route(const Netlist<>& net_list, + FILE* fp, + bool is_flat) { + auto& place_ctx = g_vpr_ctx.placement(); + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + auto& route_ctx = g_vpr_ctx.mutable_routing(); + + if (route_ctx.route_trees.empty()) + return; //Only if routing exists + + for (auto net_id : net_list.nets()) { + if (!net_list.net_is_ignored(net_id)) { + fprintf(fp, "\n\nNet %zu (%s)\n\n", size_t(net_id), net_list.net_name(net_id).c_str()); + if (net_list.net_sinks(net_id).size() == false) { + fprintf(fp, "\n\nUsed in local cluster only, reserved one CLB pin\n\n"); + } else { + if (!route_ctx.route_trees[net_id]) + continue; + + t_trace* head = TracebackCompat::traceback_from_route_tree(route_ctx.route_trees[net_id].value()); + t_trace* tptr = head; + + while (tptr != nullptr) { + RRNodeId inode = RRNodeId(tptr->index); + t_rr_type rr_type = rr_graph.node_type(inode); + int ilow = rr_graph.node_xlow(inode); + int jlow = rr_graph.node_ylow(inode); + + fprintf(fp, "Node:\t%zu\t%6s (%d,%d) ", size_t(inode), + rr_graph.node_type_string(inode), ilow, jlow); + + if ((ilow != rr_graph.node_xhigh(inode)) + || (jlow != rr_graph.node_yhigh(inode))) + fprintf(fp, "to (%d,%d) ", rr_graph.node_xhigh(inode), + rr_graph.node_yhigh(inode)); + + switch (rr_type) { + case IPIN: + case OPIN: + if (is_io_type(device_ctx.grid.get_physical_type(ilow, jlow))) { + fprintf(fp, " Pad: "); + } else { /* IO Pad. */ + fprintf(fp, " Pin: "); + } + break; + + case CHANX: + case CHANY: + fprintf(fp, " Track: "); + break; + + case SOURCE: + case SINK: + if (is_io_type(device_ctx.grid.get_physical_type(ilow, jlow))) { + fprintf(fp, " Pad: "); + } else { /* IO Pad. */ + fprintf(fp, " Class: "); + } + break; + + default: + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "in print_route: Unexpected traceback element type: %d (%s).\n", + rr_type, rr_graph.node_type_string(inode)); + break; + } + + fprintf(fp, "%d ", rr_graph.node_ptc_num(inode)); + + auto physical_tile = device_ctx.grid.get_physical_type(ilow, jlow); + if (!is_io_type(physical_tile) && (rr_type == IPIN || rr_type == OPIN)) { + int pin_num = rr_graph.node_pin_num(inode); + int xoffset = device_ctx.grid.get_width_offset(ilow, jlow); + int yoffset = device_ctx.grid.get_height_offset(ilow, jlow); + const t_sub_tile* sub_tile; + int sub_tile_rel_cap; + std::tie(sub_tile, sub_tile_rel_cap) = get_sub_tile_from_pin_physical_num(physical_tile, pin_num); + int sub_tile_offset = sub_tile->capacity.low + sub_tile_rel_cap; + + ClusterBlockId iblock = place_ctx.grid_blocks[ilow - xoffset][jlow - yoffset].blocks[sub_tile_offset]; + VTR_ASSERT(iblock); + const t_pb_graph_pin* pb_pin; + if (is_pin_on_tile(physical_tile, pin_num)) { + pb_pin = get_pb_graph_node_pin_from_block_pin(iblock, pin_num); + } else { + pb_pin = get_pb_pin_from_pin_physical_num(physical_tile, pin_num); + } + const t_pb_type* pb_type = pb_pin->parent_node->pb_type; + fprintf(fp, " %s.%s[%d] ", pb_type->name, pb_pin->port->name, pb_pin->pin_number); + } + + /* Uncomment line below if you're debugging and want to see the switch types * + * used in the routing. */ + fprintf(fp, "Switch: %d", int(tptr->iswitch)); + + //Save net pin index for sinks + if (rr_type == SINK) { + fprintf(fp, " Net_pin_index: %d", tptr->net_pin_index); + } + + fprintf(fp, "\n"); + + tptr = tptr->next; + } + + free_traceback(head); + } + } else { /* Global net. Never routed. */ + fprintf(fp, "\n\nNet %zu (%s): global net connecting:\n\n", size_t(net_id), + net_list.net_name(net_id).c_str()); + + for (auto pin_id : net_list.net_pins(net_id)) { + ParentBlockId block_id = net_list.pin_block(pin_id); + int iclass = get_block_pin_class_num(block_id, pin_id, is_flat); + t_block_loc blk_loc; + blk_loc = get_block_loc(block_id, is_flat); + fprintf(fp, "Block %s (#%zu) at (%d,%d), Pin class %d.\n", + net_list.block_name(block_id).c_str(), + size_t(block_id), + blk_loc.loc.x, + blk_loc.loc.y, + iclass); + } + } + } +} + +/* Prints out the routing to file route_file. */ +void print_route(const Netlist<>& net_list, + const char* placement_file, + const char* route_file, + bool is_flat) { + FILE* fp; + + fp = fopen(route_file, "w"); + + auto& place_ctx = g_vpr_ctx.placement(); + auto& device_ctx = g_vpr_ctx.device(); + auto& route_ctx = g_vpr_ctx.mutable_routing(); + + fprintf(fp, "Placement_File: %s Placement_ID: %s\n", placement_file, place_ctx.placement_id.c_str()); + + fprintf(fp, "Array size: %zu x %zu logic blocks.\n", device_ctx.grid.width(), device_ctx.grid.height()); + fprintf(fp, "\nRouting:"); + + print_route(net_list, fp, is_flat); + + fclose(fp); + + //Save the digest of the route file + route_ctx.routing_id = vtr::secure_digest_file(route_file); +} diff --git a/vpr/src/base/read_route.h b/vpr/src/base/read_route.h index efabf702ad1..e1014f1e65f 100644 --- a/vpr/src/base/read_route.h +++ b/vpr/src/base/read_route.h @@ -1,7 +1,6 @@ /** * @file - * @brief Read a .route file and load the route tree and other associated data structure - * with the correct values. + * @brief Functions to read/write a .route file, which contains a serialized routing state. * * This is used to perform --analysis only */ @@ -9,6 +8,10 @@ #ifndef READ_ROUTE_H #define READ_ROUTE_H +#include "netlist.h" +#include "vpr_types.h" + bool read_route(const char* route_file, const t_router_opts& RouterOpts, bool verify_file_digests); +void print_route(const Netlist<>& net_list, const char* placement_file, const char* route_file, bool is_flat); #endif /* READ_ROUTE_H */ diff --git a/vpr/src/base/stats.cpp b/vpr/src/base/stats.cpp index 156f9682468..337a1964d6b 100644 --- a/vpr/src/base/stats.cpp +++ b/vpr/src/base/stats.cpp @@ -3,6 +3,7 @@ #include #include +#include "route_tree.h" #include "vtr_assert.h" #include "vtr_log.h" #include "vtr_math.h" @@ -245,10 +246,6 @@ static void get_channel_occupancy_stats(const Netlist<>& net_list, bool /***/) { static void load_channel_occupancies(const Netlist<>& net_list, vtr::Matrix& chanx_occ, vtr::Matrix& chany_occ) { - int i, j, inode; - t_trace* tptr; - t_rr_type rr_type; - auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; auto& route_ctx = g_vpr_ctx.routing(); @@ -263,30 +260,23 @@ static void load_channel_occupancies(const Netlist<>& net_list, if (net_list.net_is_ignored(net_id) && net_list.net_sinks(net_id).size() != 0) continue; - tptr = route_ctx.trace[net_id].head; - while (tptr != nullptr) { - inode = tptr->index; - rr_type = rr_graph.node_type(RRNodeId(inode)); + auto& tree = route_ctx.route_trees[net_id]; + if (!tree) + continue; - if (rr_type == SINK) { - tptr = tptr->next; /* Skip next segment. */ - if (tptr == nullptr) - break; - } + for (auto& rt_node : tree.value().all_nodes()) { + RRNodeId inode = rt_node.inode; + t_rr_type rr_type = rr_graph.node_type(inode); - else if (rr_type == CHANX) { - j = rr_graph.node_ylow(RRNodeId(inode)); - for (i = rr_graph.node_xlow(RRNodeId(inode)); i <= rr_graph.node_xhigh(RRNodeId(inode)); i++) + if (rr_type == CHANX) { + int j = rr_graph.node_ylow(inode); + for (int i = rr_graph.node_xlow(inode); i <= rr_graph.node_xhigh(inode); i++) chanx_occ[i][j]++; - } - - else if (rr_type == CHANY) { - i = rr_graph.node_xlow(RRNodeId(inode)); - for (j = rr_graph.node_ylow(RRNodeId(inode)); j <= rr_graph.node_yhigh(RRNodeId(inode)); j++) + } else if (rr_type == CHANY) { + int i = rr_graph.node_xlow(inode); + for (int j = rr_graph.node_ylow(inode); j <= rr_graph.node_yhigh(inode); j++) chany_occ[i][j]++; } - - tptr = tptr->next; } } } @@ -300,56 +290,44 @@ void get_num_bends_and_length(ParentNetId inet, int* bends_ptr, int* len_ptr, in auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; - t_trace *tptr, *prevptr; - int inode; - t_rr_type curr_type, prev_type; int bends, length, segments; - bool is_absorbed = true; - bends = 0; length = 0; segments = 0; - prevptr = route_ctx.trace[inet].head; /* Should always be SOURCE. */ - if (prevptr == nullptr) { + const vtr::optional& tree = route_ctx.route_trees[inet]; + if (!tree) { VPR_FATAL_ERROR(VPR_ERROR_OTHER, - "in get_num_bends_and_length: net #%lu has no traceback.\n", size_t(inet)); + "in get_num_bends_and_length: net #%lu has no routing.\n", size_t(inet)); } - inode = prevptr->index; - prev_type = rr_graph.node_type(RRNodeId(inode)); - - tptr = prevptr->next; - while (tptr != nullptr) { - inode = tptr->index; - curr_type = rr_graph.node_type(RRNodeId(inode)); + t_rr_type prev_type = rr_graph.node_type(tree->root().inode); + RouteTree::iterator it = tree->all_nodes().begin(); + RouteTree::iterator end = tree->all_nodes().end(); + ++it; /* start from the next node after source */ - if (curr_type == SINK) { /* Starting a new segment */ - tptr = tptr->next; /* Link to existing path - don't add to len. */ - if (tptr == nullptr) - break; - - curr_type = rr_graph.node_type(RRNodeId(tptr->index)); - } + for (; it != end; ++it) { + const RouteTreeNode& rt_node = *it; + RRNodeId inode = rt_node.inode; + t_rr_type curr_type = rr_graph.node_type(inode); - else if (curr_type == CHANX || curr_type == CHANY) { - is_absorbed = false; + if (curr_type == CHANX || curr_type == CHANY) { segments++; - length += rr_graph.node_length(RRNodeId(inode)); + length += rr_graph.node_length(inode); if (curr_type != prev_type && (prev_type == CHANX || prev_type == CHANY)) bends++; } - prev_type = curr_type; - tptr = tptr->next; + /* The all_nodes iterator walks all nodes in the tree. If we are at a leaf and going back to the top, prev_type is invalid: just set it to SINK */ + prev_type = rt_node.is_leaf() ? SINK : curr_type; } *bends_ptr = bends; *len_ptr = length; *segments_ptr = segments; - *is_absorbed_ptr = is_absorbed; + *is_absorbed_ptr = (segments == 0); } /** diff --git a/vpr/src/base/vpr_api.cpp b/vpr/src/base/vpr_api.cpp index 3400e3139a5..23353e22d15 100644 --- a/vpr/src/base/vpr_api.cpp +++ b/vpr/src/base/vpr_api.cpp @@ -96,6 +96,7 @@ #include "iostream" #ifdef VPR_USE_TBB +# define TBB_PREVIEW_GLOBAL_CONTROL 1 # include # include #endif @@ -825,8 +826,7 @@ RouteStatus vpr_route_flow(const Netlist<>& net_list, arch, chan_width, timing_info, - net_delay, - is_flat); + net_delay); } //Post-implementation @@ -969,8 +969,7 @@ RouteStatus vpr_load_routing(t_vpr_setup& vpr_setup, const t_arch& /*arch*/, int fixed_channel_width, std::shared_ptr timing_info, - NetPinsMatrix& net_delay, - bool is_flat) { + NetPinsMatrix& net_delay) { vtr::ScopedStartFinishTimer timer("Load Routing"); if (NO_FIXED_CHANNEL_WIDTH == fixed_channel_width) { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Fixed channel width must be specified when loading routing (was %d)", fixed_channel_width); @@ -984,8 +983,7 @@ RouteStatus vpr_load_routing(t_vpr_setup& vpr_setup, if (vpr_setup.Timing.timing_analysis_enabled) { //Update timing info load_net_delay_from_routing((const Netlist<>&)g_vpr_ctx.clustering().clb_nlist, - net_delay, - is_flat); + net_delay); timing_info->update(); } @@ -1152,7 +1150,6 @@ void free_device(const t_det_routing_arch& /*routing_arch*/) { device_ctx.all_sw_inf.clear(); free_complex_block_types(); - free_chunk_memory_trace(); } static void free_complex_block_types() { @@ -1186,7 +1183,7 @@ static void free_placement() { static void free_routing() { auto& routing_ctx = g_vpr_ctx.mutable_routing(); - routing_ctx.trace.clear(); + routing_ctx.route_trees.clear(); routing_ctx.trace_nodes.clear(); routing_ctx.net_rr_terminals.clear(); routing_ctx.rr_blk_source.clear(); @@ -1195,6 +1192,7 @@ static void free_routing() { routing_ctx.net_status.clear(); routing_ctx.route_bb.clear(); } + /** * @brief handles the deletion of NoC related datastructures. */ @@ -1216,14 +1214,12 @@ void vpr_free_vpr_data_structures(t_arch& Arch, free_noc(); } -void vpr_free_all(const Netlist<>& net_list, - t_arch& Arch, +void vpr_free_all(t_arch& Arch, t_vpr_setup& vpr_setup) { free_rr_graph(); if (vpr_setup.RouterOpts.doRouting) { free_route_structs(); } - free_trace_structs(net_list); vpr_free_vpr_data_structures(Arch, vpr_setup); } @@ -1377,9 +1373,7 @@ void vpr_analysis(const Netlist<>& net_list, auto& route_ctx = g_vpr_ctx.routing(); auto& atom_ctx = g_vpr_ctx.atom(); - //Check the first index to see if a pointer exists - //TODO: Implement a better error check - if (route_ctx.trace.empty()) { + if (route_ctx.route_trees.empty()) { VPR_FATAL_ERROR(VPR_ERROR_ANALYSIS, "No routing loaded -- can not perform post-routing analysis"); } @@ -1399,8 +1393,7 @@ void vpr_analysis(const Netlist<>& net_list, NetPinsMatrix net_delay = make_net_pins_matrix(net_list); load_net_delay_from_routing(net_list, - net_delay, - vpr_setup.RouterOpts.flat_routing); + net_delay); //Do final timing analysis auto analysis_delay_calc = std::make_shared(atom_ctx.nlist, atom_ctx.lookup, net_delay, vpr_setup.RouterOpts.flat_routing); diff --git a/vpr/src/base/vpr_api.h b/vpr/src/base/vpr_api.h index b7111851a18..b834c0da9fe 100644 --- a/vpr/src/base/vpr_api.h +++ b/vpr/src/base/vpr_api.h @@ -108,8 +108,7 @@ RouteStatus vpr_load_routing(t_vpr_setup& vpr_setup, const t_arch& arch, int fixed_channel_width, std::shared_ptr timing_info, - NetPinsMatrix& net_delay, - bool is_flat); + NetPinsMatrix& net_delay); /* Analysis */ @@ -147,8 +146,7 @@ void vpr_setup_noc(const t_vpr_setup& vpr_setup, const t_arch& arch); void vpr_setup_noc_routing_algorithm(std::string noc_routing_algorithm_name); void vpr_free_vpr_data_structures(t_arch& Arch, t_vpr_setup& vpr_setup); -void vpr_free_all(const Netlist<>& net_list, - t_arch& Arch, +void vpr_free_all(t_arch& Arch, t_vpr_setup& vpr_setup); /* Display general info to user */ diff --git a/vpr/src/base/vpr_context.h b/vpr/src/base/vpr_context.h index 7a2d8450819..7d615e36949 100644 --- a/vpr/src/base/vpr_context.h +++ b/vpr/src/base/vpr_context.h @@ -7,6 +7,7 @@ #include "vpr_types.h" #include "vtr_ndmatrix.h" +#include "vtr_optional.h" #include "vtr_vector.h" #include "atom_netlist.h" #include "clustered_netlist.h" @@ -22,7 +23,7 @@ #include "device_grid.h" #include "clock_network_builders.h" #include "clock_connection_builders.h" -#include "route_traceback.h" +#include "route_tree.h" #include "router_lookahead.h" #include "place_macro.h" #include "compressed_grid.h" @@ -61,7 +62,7 @@ struct AtomContext : public Context { ********************************************************************/ /** * @brief constructor - * + * * In the constructor initialize the list of pack molecules to nullptr and defines a custom deletor for it */ AtomContext() @@ -75,7 +76,7 @@ struct AtomContext : public Context { /** * @brief The molecules associated with each atom block. - * + * * This map is loaded in the pre-packing stage and freed at the very end of vpr flow run. * The pointers in this multimap is shared with list_of_pack_molecules. */ @@ -83,7 +84,7 @@ struct AtomContext : public Context { /** * @brief A linked list of all the packing molecules that are loaded in pre-packing stage. - * + * * Is is useful in freeing the pack molecules at the destructor of the Atom context using free_pack_molecules. */ std::unique_ptr list_of_pack_molecules; @@ -192,12 +193,12 @@ struct DeviceContext : public Context { ///@brief Reverse look-up from RR node to non-configurably connected node set (index into rr_nonconf_node_sets) std::unordered_map rr_node_to_non_config_node_set; - /* A writeable view of routing resource graph to be the ONLY database + /* A writeable view of routing resource graph to be the ONLY database * for routing resource graph builder functions. */ RRGraphBuilder rr_graph_builder{}; - /* A read-only view of routing resource graph to be the ONLY database + /* A read-only view of routing resource graph to be the ONLY database * for client functions: GUI, placer, router, timing analyzer etc. */ RRGraphView rr_graph{rr_graph_builder.rr_nodes(), rr_graph_builder.node_lookup(), rr_graph_builder.rr_node_metadata(), rr_graph_builder.rr_edge_metadata(), rr_indexed_data, rr_rc_data, rr_graph_builder.rr_segments(), rr_graph_builder.rr_switch()}; @@ -306,7 +307,7 @@ struct ClusteringContext : public Context { /** * @brief State relating to helper data structure using in the clustering stage - * + * * This should contain helper data structures that are useful in the clustering/packing stage. * They are encapsulated here as they are useful in clustering and reclustering algorithms that may be used * in packing or placement stages. @@ -410,7 +411,7 @@ struct PlacementContext : public Context { */ struct RoutingContext : public Context { /* [0..num_nets-1] of linked list start pointers. Defines the routing. */ - vtr::vector trace; + vtr::vector> route_trees; vtr::vector> trace_nodes; @@ -504,20 +505,20 @@ struct FloorplanningContext : public Context { */ struct NocContext : public Context { /** - * @brief A model of the NoC + * @brief A model of the NoC * * Contains all the routers and links that make up the NoC. The routers contain * information regarding the physical tile positions they represent. The links * define the connections between every router (topology) and also metrics that describe its - * "usage". - * + * "usage". + * * - * The NoC model is created once from the architecture file description. + * The NoC model is created once from the architecture file description. */ NocStorage noc_model; /** - * @brief Stores all the communication happening between routers in the NoC + * @brief Stores all the communication happening between routers in the NoC * * Contains all of the traffic flows that describe which pairs of logical routers are communicating and also some metrics and constraints on the data transfer between the two routers. * @@ -528,9 +529,9 @@ struct NocContext : public Context { /** * @brief Contains the packet routing algorithm used by the NoC. - * + * * This should be used to route traffic flows within the NoC. - * + * * This is created from a user supplied command line option "--noc_routing_algorithm" */ NocRouting* noc_flows_router; diff --git a/vpr/src/base/vpr_types.h b/vpr/src/base/vpr_types.h index 1fc87f71e7c..f959f4f056e 100644 --- a/vpr/src/base/vpr_types.h +++ b/vpr/src/base/vpr_types.h @@ -1440,17 +1440,17 @@ struct t_det_routing_arch { * Note that this index will store the index of the segment * relative to its **parallel** segment types, not all segments * as stored in device_ctx. Look in rr_graph.cpp: build_rr_graph - * for details but here is an example: say our segment_inf_vec in + * for details but here is an example: say our segment_inf_vec in * device_ctx is as follows: [seg_a_x, seg_b_x, seg_a_y, seg_b_y] - * when building the rr_graph, static segment_inf_vectors will be - * created for each direction, thus you will have the following - * 2 vectors: X_vec =[seg_a_x,seg_b_x] and Y_vec = [seg_a_y,seg_b_y]. - * As a result, e.g. seg_b_y::index == 1 (index in Y_vec) + * when building the rr_graph, static segment_inf_vectors will be + * created for each direction, thus you will have the following + * 2 vectors: X_vec =[seg_a_x,seg_b_x] and Y_vec = [seg_a_y,seg_b_y]. + * As a result, e.g. seg_b_y::index == 1 (index in Y_vec) * and != 3 (index in device_ctx segment_inf_vec). - * @param abs_index index is relative to the segment_inf vec as stored in device_ctx. - * Note that the above vector is **unifies** both x-parallel and - * y-parallel segments and is loaded up originally in read_xml_arch_file.cpp - * + * @param abs_index index is relative to the segment_inf vec as stored in device_ctx. + * Note that the above vector is **unifies** both x-parallel and + * y-parallel segments and is loaded up originally in read_xml_arch_file.cpp + * * @param type_name_ptr pointer to name of the segment type this track belongs * to. points to the appropriate name in s_segment_inf */ @@ -1534,7 +1534,7 @@ class t_chan_seg_details { const t_seg_details* seg_detail_ = nullptr; }; -/* Defines a 3-D array of t_chan_seg_details data structures (one per-each horizontal and vertical channel) +/* Defines a 3-D array of t_chan_seg_details data structures (one per-each horizontal and vertical channel) * once allocated in rr_graph2.cpp, is can be accessed like: [0..grid.width()][0..grid.height()][0..num_tracks-1] */ typedef vtr::NdMatrix t_chan_details; @@ -1553,33 +1553,6 @@ constexpr bool is_pin(e_rr_type type) { return (type == IPIN || type == OPIN); } constexpr bool is_chan(e_rr_type type) { return (type == CHANX || type == CHANY); } constexpr bool is_src_sink(e_rr_type type) { return (type == SOURCE || type == SINK); } -/** - * @brief Basic element used to store the traceback (routing) of each net. - * - * @param index Array index (ID) of this routing resource node. - * @param net_pin_index: Net pin index associated with the node. This value - * ranges from 1 to fanout [1..num_pins-1]. For cases when - * different speed paths are taken to the same SINK for - * different pins, node index cannot uniquely identify - * each SINK, so the net pin index guarantees an unique - * identification for each SINK node. For non-SINK nodes - * and for SINK nodes with no associated net pin index - * (i.e. special SINKs like the source of a clock tree - * which do not correspond to an actual netlist connection), - * the value for this member should be set to OPEN (-1). - * @param iswitch Index of the switch type used to go from this rr_node to - * the next one in the routing. OPEN if there is no next node - * (i.e. this node is the last one (a SINK) in a branch of the - * net's routing). - * @param next Pointer to the next traceback element in this route. - */ -struct t_trace { - t_trace* next; - int index; - int net_pin_index = OPEN; - short iswitch; -}; - /** * @brief Extra information about each rr_node needed only during routing * (i.e. during the maze expansion). @@ -1723,12 +1696,12 @@ struct t_power_opts { }; /** @brief Channel width data - * @param max= Maximum channel width between x_max and y_max. - * @param x_min= Minimum channel width of horizontal channels. Initialized when init_chan() is invoked in rr_graph2.cpp - * @param y_min= Same as above but for vertical channels. - * @param x_max= Maximum channel width of horiozntal channels. Initialized when init_chan() is invoked in rr_graph2.cpp - * @param y_max= Same as above but for vertical channels. - * @param x_list= Stores the channel width of all horizontal channels and thus goes from [0..grid.height()] + * @param max= Maximum channel width between x_max and y_max. + * @param x_min= Minimum channel width of horizontal channels. Initialized when init_chan() is invoked in rr_graph2.cpp + * @param y_min= Same as above but for vertical channels. + * @param x_max= Maximum channel width of horiozntal channels. Initialized when init_chan() is invoked in rr_graph2.cpp + * @param y_max= Same as above but for vertical channels. + * @param x_list= Stores the channel width of all horizontal channels and thus goes from [0..grid.height()] * (imagine a 2D Cartesian grid with horizontal lines starting at every grid point on a line parallel to the y-axis) * @param y_list= Stores the channel width of all verical channels and thus goes from [0..grid.width()] * (imagine a 2D Cartesian grid with vertical lines starting at every grid point on a line parallel to the x-axis) diff --git a/vpr/src/draw/draw.cpp b/vpr/src/draw/draw.cpp index 87e0d71cbb4..84baaded52e 100644 --- a/vpr/src/draw/draw.cpp +++ b/vpr/src/draw/draw.cpp @@ -1,10 +1,10 @@ /*********************************** Top-level Summary ************************************* * This is VPR's main graphics application program. The program interacts with ezgl/graphics.hpp, * which provides an API for displaying graphics on both X11 and Win32. The most important - * subroutine in this file is draw_main_canvas(), which is a callback function that will be called + * subroutine in this file is draw_main_canvas(), which is a callback function that will be called * whenever the screen needs to be updated. Then, draw_main_canvas() will decide what * drawing subroutines to call depending on whether PLACEMENT or ROUTING is shown on screen. - * The initial_setup_X() functions link the menu button signals to the corresponding drawing functions. + * The initial_setup_X() functions link the menu button signals to the corresponding drawing functions. * As a note, looks into draw_global.c for understanding the data structures associated with drawing-> * * Contains all functions that didn't fit in any other draw_*.cpp file. @@ -133,11 +133,6 @@ static void run_graphics_commands(std::string commands); /************************** File Scope Variables ****************************/ -//The arrow head position for turning/straight-thru connections in a switch box -constexpr float SB_EDGE_TURN_ARROW_POSITION = 0.2; -constexpr float SB_EDGE_STRAIGHT_ARROW_POSITION = 0.95; -constexpr float EMPTY_BLOCK_LIGHTEN_FACTOR = 0.20; - //Kelly's maximum contrast colors are selected to be easily distinguishable as described in: // Kenneth Kelly, "Twenty-Two Colors of Maximum Contrast", Color Eng. 3(6), 1943 //We use these to highlight a relatively small number of things (e.g. stages in a critical path, @@ -286,9 +281,9 @@ static void draw_main_canvas(ezgl::renderer* g) { /** * @brief Default setup function, connects signals/sets up ui created in main.ui file - * + * * To minimize code repetition, this function sets up all buttons that ALWAYS get set up. - * If you want to add to the initial setup functions, and your new setup function will always be called, + * If you want to add to the initial setup functions, and your new setup function will always be called, * please put it here instead of writing it 5 independent times. Thanks! * @param app ezgl application */ @@ -302,8 +297,8 @@ static void default_setup(ezgl::application* app) { // Initial Setup functions run default setup if they are a new window. Then, they will run // the specific hiding/showing functions that separate them from the other init. setup functions -/* function below intializes the interface window with a set of buttons and links - * signals to corresponding functions for situation where the window is opened from +/* function below intializes the interface window with a set of buttons and links + * signals to corresponding functions for situation where the window is opened from * NO_PICTURE_to_PLACEMENT */ static void initial_setup_NO_PICTURE_to_PLACEMENT(ezgl::application* app, bool is_new_window) { @@ -315,8 +310,8 @@ static void initial_setup_NO_PICTURE_to_PLACEMENT(ezgl::application* app, hide_crit_path_button(app); } -/* function below intializes the interface window with a set of buttons and links - * signals to corresponding functions for situation where the window is opened from +/* function below intializes the interface window with a set of buttons and links + * signals to corresponding functions for situation where the window is opened from * NO_PICTURE_to_PLACEMENT_with_crit_path */ static void initial_setup_NO_PICTURE_to_PLACEMENT_with_crit_path( ezgl::application* app, @@ -331,8 +326,8 @@ static void initial_setup_NO_PICTURE_to_PLACEMENT_with_crit_path( hide_widget("RoutingMenuButton", app); } -/* function below intializes the interface window with a set of buttons and links - * signals to corresponding functions for situation where the window is opened from +/* function below intializes the interface window with a set of buttons and links + * signals to corresponding functions for situation where the window is opened from * PLACEMENT_to_ROUTING */ static void initial_setup_PLACEMENT_to_ROUTING(ezgl::application* app, bool is_new_window) { @@ -343,8 +338,8 @@ static void initial_setup_PLACEMENT_to_ROUTING(ezgl::application* app, hide_crit_path_button(app); } -/* function below intializes the interface window with a set of buttons and links - * signals to corresponding functions for situation where the window is opened from +/* function below intializes the interface window with a set of buttons and links + * signals to corresponding functions for situation where the window is opened from * ROUTING_to_PLACEMENT */ static void initial_setup_ROUTING_to_PLACEMENT(ezgl::application* app, bool is_new_window) { @@ -356,8 +351,8 @@ static void initial_setup_ROUTING_to_PLACEMENT(ezgl::application* app, hide_crit_path_button(app); } -/* function below intializes the interface window with a set of buttons and links - * signals to corresponding functions for situation where the window is opened from +/* function below intializes the interface window with a set of buttons and links + * signals to corresponding functions for situation where the window is opened from * NO_PICTURE_to_ROUTING */ static void initial_setup_NO_PICTURE_to_ROUTING(ezgl::application* app, bool is_new_window) { @@ -368,8 +363,8 @@ static void initial_setup_NO_PICTURE_to_ROUTING(ezgl::application* app, hide_crit_path_button(app); } -/* function below intializes the interface window with a set of buttons and links - * signals to corresponding functions for situation where the window is opened from +/* function below intializes the interface window with a set of buttons and links + * signals to corresponding functions for situation where the window is opened from * NO_PICTURE_to_ROUTING_with_crit_path */ static void initial_setup_NO_PICTURE_to_ROUTING_with_crit_path( ezgl::application* app, @@ -675,7 +670,7 @@ bool draw_if_net_highlighted(ClusterNetId inet) { /** * @brief cbk function for key press - * + * * At the moment, only does something if user is currently typing in searchBar and * hits enter, at which point it runs autocomplete */ @@ -773,9 +768,7 @@ void act_on_mouse_press(ezgl::application* app, GdkEventButton* event, double x, // std::cout << "mouse button at coordinates (" << x << "," << y << ") " << std::endl; } -void act_on_mouse_move(ezgl::application* app, GdkEventButton* event, double x, double y) { - // std::cout << "Mouse move at coordinates (" << x << "," << y << ") "<< std::endl; - +void act_on_mouse_move(ezgl::application* app, GdkEventButton* /* event */, double x, double y) { // user has clicked the window button, in window mode if (window_point_1_collected) { // draw a grey, dashed-line box to indicate the zoom-in region @@ -809,7 +802,6 @@ void act_on_mouse_move(ezgl::application* app, GdkEventButton* event, double x, } } } - event = event; // just for hiding warning message } ezgl::point2d atom_pin_draw_coord(AtomPinId pin) { @@ -843,61 +835,47 @@ ezgl::point2d atom_pin_draw_coord(AtomPinId pin) { std::vector trace_routed_connection_rr_nodes( const ClusterNetId net_id, const int driver_pin, - const int sink_pin, - bool is_flat) { + const int sink_pin) { auto& route_ctx = g_vpr_ctx.routing(); - bool allocated_route_tree_structs = alloc_route_tree_timing_structs(true); //Needed for traceback_to_route_tree - - //Conver the traceback into an easily search-able - t_rt_node* rt_root = traceback_to_route_tree(ParentNetId(size_t(net_id)), is_flat); + VTR_ASSERT(route_ctx.route_trees[net_id]); + const RouteTree& tree = route_ctx.route_trees[net_id].value(); - VTR_ASSERT( - rt_root - && rt_root->inode - == route_ctx.net_rr_terminals[ParentNetId(size_t(net_id))][driver_pin]); + VTR_ASSERT(tree.root().inode == RRNodeId(route_ctx.net_rr_terminals[net_id][driver_pin])); int sink_rr_node = route_ctx.net_rr_terminals[ParentNetId(size_t(net_id))][sink_pin]; std::vector rr_nodes_on_path; //Collect the rr nodes - trace_routed_connection_rr_nodes_recurr(rt_root, sink_rr_node, + trace_routed_connection_rr_nodes_recurr(tree.root(), + sink_rr_node, rr_nodes_on_path); //Traced from sink to source, but we want to draw from source to sink std::reverse(rr_nodes_on_path.begin(), rr_nodes_on_path.end()); - free_route_tree(rt_root); - - if (allocated_route_tree_structs) { - free_route_tree_timing_structs(); - } return rr_nodes_on_path; } //Helper function for trace_routed_connection_rr_nodes //Adds the rr nodes linking rt_node to sink_rr_node to rr_nodes_on_path //Returns true if rt_node is on the path -bool trace_routed_connection_rr_nodes_recurr(const t_rt_node* rt_node, +bool trace_routed_connection_rr_nodes_recurr(const RouteTreeNode& rt_node, int sink_rr_node, std::vector& rr_nodes_on_path) { //DFS from the current rt_node to the sink_rr_node, when the sink is found trace back the used rr nodes - if (rt_node->inode == sink_rr_node) { + if (rt_node.inode == RRNodeId(sink_rr_node)) { rr_nodes_on_path.push_back(sink_rr_node); return true; } - for (t_linked_rt_edge* edge = rt_node->u.child_list; edge != nullptr; edge = edge->next) { - t_rt_node* child_rt_node = edge->child; - VTR_ASSERT(child_rt_node); - + for (const RouteTreeNode& child_rt_node : rt_node.child_nodes()) { bool on_path_to_sink = trace_routed_connection_rr_nodes_recurr( child_rt_node, sink_rr_node, rr_nodes_on_path); - if (on_path_to_sink) { - rr_nodes_on_path.push_back(rt_node->inode); + rr_nodes_on_path.push_back(size_t(rt_node.inode)); return true; } } @@ -1433,8 +1411,8 @@ ezgl::color lighten_color(ezgl::color color, float amount) { } /** * @brief Returns the max fanout - * - * @return size_t + * + * @return size_t */ size_t get_max_fanout() { //find maximum fanout diff --git a/vpr/src/draw/draw.h b/vpr/src/draw/draw.h index 7faea50973c..8a4c15077ed 100644 --- a/vpr/src/draw/draw.h +++ b/vpr/src/draw/draw.h @@ -1,18 +1,18 @@ /** * @file draw.h - * + * * The main drawing file. Contains the setup for ezgl application, ui setup, and graphis functions - * + * * This is VPR's main graphics application program. The program interacts with ezgl/graphics.hpp, * which provides an API for displaying graphics on both X11 and Win32. The most important - * subroutine in this file is draw_main_canvas(), which is a callback function that will be called + * subroutine in this file is draw_main_canvas(), which is a callback function that will be called * whenever the screen needs to be updated. Then, draw_main_canvas() will decide what * drawing subroutines to call depending on whether PLACEMENT or ROUTING is shown on screen. - * The initial_setup_X() functions link the menu button signals to the corresponding drawing functions. + * The initial_setup_X() functions link the menu button signals to the corresponding drawing functions. * As a note, looks into draw_global.c for understanding the data structures associated with drawing-> * * Contains all functions that didn't fit in any other draw_*.cpp file. - * + * * Authors: Vaughn Betz, Long Yu (Mike) Wang, Dingyu (Tina) Yang, Sebastian Lievano * Last updated: August 2022 */ @@ -20,6 +20,7 @@ #ifndef DRAW_H #define DRAW_H +#include "rr_graph_fwd.h" #include "timing_info.h" #include "physical_types.h" @@ -93,13 +94,12 @@ bool draw_if_net_highlighted(ClusterNetId inet); std::vector trace_routed_connection_rr_nodes( const ClusterNetId net_id, const int driver_pin, - const int sink_pin, - bool is_flat); + const int sink_pin); /* Helper function for trace_routed_connection_rr_nodes * Adds the rr nodes linking rt_node to sink_rr_node to rr_nodes_on_path * Returns true if rt_node is on the path. */ -bool trace_routed_connection_rr_nodes_recurr(const t_rt_node* rt_node, +bool trace_routed_connection_rr_nodes_recurr(const RouteTreeNode& rt_node, int sink_rr_node, std::vector& rr_nodes_on_path); diff --git a/vpr/src/draw/draw_basic.cpp b/vpr/src/draw/draw_basic.cpp index 1c2f828cea1..7beb012d3d2 100644 --- a/vpr/src/draw/draw_basic.cpp +++ b/vpr/src/draw/draw_basic.cpp @@ -544,17 +544,12 @@ void draw_routed_net(ParentNetId net_id, ezgl::renderer* g) { if (cluster_ctx.clb_nlist.net_is_ignored(convert_to_cluster_net_id(net_id))) /* Don't draw. */ return; - if (route_ctx.trace[net_id].head == nullptr) /* No routing-> Skip. (Allows me to draw */ - return; /* partially complete routes). */ - - t_trace* tptr = route_ctx.trace[net_id].head; /* SOURCE to start */ - int inode = tptr->index; + if (!route_ctx.route_trees[net_id]) // No routing -> Skip. (Allows me to draw partially complete routes) + return; std::vector rr_nodes_to_draw; - rr_nodes_to_draw.push_back(inode); - for (;;) { - tptr = tptr->next; - inode = tptr->index; + for (auto& rt_node : route_ctx.route_trees[net_id].value().all_nodes()) { + int inode = size_t(rt_node.inode); if (draw_if_net_highlighted(convert_to_cluster_net_id(net_id))) { /* If a net has been highlighted, highlight the whole net in * @@ -568,19 +563,12 @@ void draw_routed_net(ParentNetId net_id, ezgl::renderer* g) { rr_nodes_to_draw.push_back(inode); - if (tptr->iswitch == OPEN) { //End of branch + if (rt_node.is_leaf()) { // End of branch draw_partial_route(rr_nodes_to_draw, g); rr_nodes_to_draw.clear(); - - /* Skip the next segment */ - tptr = tptr->next; - if (tptr == nullptr) - break; - inode = tptr->index; - rr_nodes_to_draw.push_back(inode); } - } /* End loop over traceback. */ + } /* End loop over route tree. */ draw_partial_route(rr_nodes_to_draw, g); } @@ -1150,8 +1138,7 @@ void draw_routed_timing_edge_connection(tatum::NodeId src_tnode, t_draw_state* draw_state = get_draw_state_vars(); - std::vector routed_rr_nodes = trace_routed_connection_rr_nodes( - net_id, 0, sink_net_pin_index, draw_state->is_flat); + std::vector routed_rr_nodes = trace_routed_connection_rr_nodes(net_id, 0, sink_net_pin_index); //Mark all the nodes highlighted diff --git a/vpr/src/draw/draw_searchbar.cpp b/vpr/src/draw/draw_searchbar.cpp index bfb9edff53d..7dac1522043 100644 --- a/vpr/src/draw/draw_searchbar.cpp +++ b/vpr/src/draw/draw_searchbar.cpp @@ -8,6 +8,7 @@ #include #include +#include "netlist_fwd.h" #include "vtr_assert.h" #include "vtr_ndoffsetmatrix.h" #include "vtr_memory.h" @@ -166,24 +167,27 @@ void draw_highlight_blocks_color(t_logical_block_type_ptr type, * If so, and toggle nets is selected, highlight the whole net in that colour. */ void highlight_nets(char* message, int hit_node, bool is_flat) { - t_trace* tptr; auto& cluster_ctx = g_vpr_ctx.clustering(); auto& route_ctx = g_vpr_ctx.routing(); t_draw_state* draw_state = get_draw_state_vars(); for (auto net_id : cluster_ctx.clb_nlist.nets()) { - for (tptr = route_ctx.trace[get_cluster_net_parent_id(g_vpr_ctx.atom().lookup, net_id, is_flat)].head; tptr != nullptr; - tptr = tptr->next) { - if (draw_state->draw_rr_node[tptr->index].color == ezgl::MAGENTA) { - draw_state->net_color[net_id] = draw_state->draw_rr_node[tptr->index].color; - if (tptr->index == hit_node) { + if (!route_ctx.route_trees[net_id]) + continue; + ParentNetId parent_id = get_cluster_net_parent_id(g_vpr_ctx.atom().lookup, net_id, is_flat); + + for (auto& rt_node : route_ctx.route_trees[parent_id].value().all_nodes()) { + int inode = size_t(rt_node.inode); + if (draw_state->draw_rr_node[inode].color == ezgl::MAGENTA) { + draw_state->net_color[net_id] = draw_state->draw_rr_node[inode].color; + if (inode == hit_node) { std::string orig_msg(message); sprintf(message, "%s || Net: %zu (%s)", orig_msg.c_str(), size_t(net_id), cluster_ctx.clb_nlist.net_name(net_id).c_str()); } - } else if (draw_state->draw_rr_node[tptr->index].color + } else if (draw_state->draw_rr_node[inode].color == ezgl::WHITE) { // If node is de-selected. draw_state->net_color[net_id] = ezgl::BLACK; diff --git a/vpr/src/draw/intra_logic_block.h b/vpr/src/draw/intra_logic_block.h index 4543bd66b01..6a86a58155a 100644 --- a/vpr/src/draw/intra_logic_block.h +++ b/vpr/src/draw/intra_logic_block.h @@ -23,7 +23,6 @@ # include "vpr_types.h" # include "draw_types.h" # include "atom_netlist_fwd.h" -# include "route_tree_timing.h" # include # include "ezgl/point.hpp" diff --git a/vpr/src/draw/search_bar.cpp b/vpr/src/draw/search_bar.cpp index 5339bb4cb27..2c3c6627ffa 100644 --- a/vpr/src/draw/search_bar.cpp +++ b/vpr/src/draw/search_bar.cpp @@ -4,13 +4,13 @@ * @brief Contains search/auto-complete related functions * @version 0.1 * @date 2022-07-20 - * + * * This file essentially follows the whole search process, from searching, finding the match, * and finally highlighting the searched for item. Additionally, auto-complete related stuff is found * here. - * + * * @copyright Copyright (c) 2022 - * + * */ #ifndef NO_GRAPHICS @@ -111,11 +111,11 @@ void search_and_highlight(GtkWidget* /*widget*/, ezgl::application* app) { } else if (search_type == "Block Name") { - /* If the block exists in atom netlist, proceeding with highlighting process. + /* If the block exists in atom netlist, proceeding with highlighting process. * if highlight atom block fn returns false, that means that the block can't be highlighted * We've already confirmed the block exists in the netlist, so that means that at this zoom lvl, * the subblock is not shown. Therefore highlight the clb mapping. - * + * * If the block does not exist in the atom netlist, we will check the CLB netlist to see if * they searched for a cluster block*/ std::string block_name = ""; @@ -273,7 +273,7 @@ void auto_zoom_rr_node(int rr_node_id) { /** * @brief Highlights the given cluster block - * + * * @param clb_index Cluster Index to be highlighted */ void highlight_cluster_block(ClusterBlockId clb_index) { @@ -306,7 +306,7 @@ void highlight_cluster_block(ClusterBlockId clb_index) { /** * @brief Finds and highlights the atom block. Returns true if block shows, false if not - * + * * @param atom_blk AtomBlockId being searched for * @param cl_blk ClusterBlock containing atom_blk * @param app ezgl:: application used @@ -340,7 +340,7 @@ void highlight_nets(ClusterNetId net_id) { t_draw_state* draw_state = get_draw_state_vars(); //If routing does not exist return - if (int(route_ctx.trace.size()) == 0) return; + if (route_ctx.route_trees.empty()) return; draw_state->net_color[net_id] = ezgl::MAGENTA; } @@ -374,11 +374,11 @@ void warning_dialog_box(const char* message) { /** * @brief manages auto-complete options when search type is changed - * + * * Selects appropriate gtkEntryCompletion item when changed signal is sent - * from gtkComboBox SearchType. Currently only sets a completion model for Block Name options, + * from gtkComboBox SearchType. Currently only sets a completion model for Block Name options, * sets null for anything else. brh - * + * * @param self GtkComboBox that holds current Search Setting * @param app ezgl app used to access other objects */ @@ -411,7 +411,7 @@ void search_type_changed(GtkComboBox* self, ezgl::application* app) { /** * @brief A non-default matching function. As opposed to simply searching for a prefix(default), * searches string for presence of a substring. Case-insensitive - * + * * @param completer the GtkEntryCompletion being used * @param key a normalized and case-folded key representing the text * @param iter GtkTreeIter pointing at the current entry being compared @@ -439,7 +439,7 @@ gboolean customMatchingFunction( /** * @brief Creates a GdkEvent that simulates user pressing key "key". * Currently used to fool GtkEntryCompletion into showing options w/o receiving a new input - * + * * @param key character value * @param window GdkWindow * @return GdkEvent Keypress event @@ -463,16 +463,16 @@ GdkEvent simulate_keypress(char key, GdkWindow* window) { /** * @brief Turns on autocomplete - * - * This function enables the auto-complete fuctionality for the search bar. + * + * This function enables the auto-complete fuctionality for the search bar. * Normally, this is pretty simple, but the idea is to have auto-complete appear as soon * as the user hits the "Enter" key. To accomplish this, a fake Gdk event is created * to simulate the user hitting a key. - * - * This was done for usability reasons; if this is not done, user will need to input another key before seeing + * + * This was done for usability reasons; if this is not done, user will need to input another key before seeing * autocomplete results. Considering the enter is supposed to be a search, we want to search for the users * key, not the key + another char - * + * * PERFORMANCE DATA * Correlation between key length and time is shaky; there might be some correlation to * how many strings are similar to it. All tests are performed with the key "1" - pretty common diff --git a/vpr/src/main.cpp b/vpr/src/main.cpp index 55d2658dd59..6643de94840 100644 --- a/vpr/src/main.cpp +++ b/vpr/src/main.cpp @@ -53,16 +53,15 @@ int main(int argc, const char** argv) { /* Read options, architecture, and circuit netlist */ vpr_init(argc, argv, &Options, &vpr_setup, &Arch); - const Netlist<>& net_list = vpr_setup.RouterOpts.flat_routing ? (const Netlist<>&)g_vpr_ctx.atom().nlist : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; if (Options.show_version) { - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); return SUCCESS_EXIT_CODE; } bool flow_succeeded = vpr_flow(vpr_setup, Arch); if (!flow_succeeded) { VTR_LOG("VPR failed to implement circuit\n"); - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); return UNIMPLEMENTABLE_EXIT_CODE; } @@ -70,32 +69,28 @@ int main(int argc, const char** argv) { print_timing_stats("Flow", timing_ctx.stats); /* free data structures */ - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); VTR_LOG("VPR succeeded\n"); } catch (const tatum::Error& tatum_error) { VTR_LOG_ERROR("%s\n", format_tatum_error(tatum_error).c_str()); - auto net_list = vpr_setup.RouterOpts.flat_routing ? (const Netlist<>&)g_vpr_ctx.atom().nlist : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); return ERROR_EXIT_CODE; } catch (const VprError& vpr_error) { vpr_print_error(vpr_error); - auto net_list = vpr_setup.RouterOpts.flat_routing ? (const Netlist<>&)g_vpr_ctx.atom().nlist : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; + vpr_free_all(Arch, vpr_setup); if (vpr_error.type() == VPR_ERROR_INTERRUPTED) { - vpr_free_all(net_list, Arch, vpr_setup); return INTERRUPTED_EXIT_CODE; } else { - vpr_free_all(net_list, Arch, vpr_setup); return ERROR_EXIT_CODE; } } catch (const vtr::VtrError& vtr_error) { VTR_LOG_ERROR("%s:%d %s\n", vtr_error.filename_c_str(), vtr_error.line(), vtr_error.what()); - auto net_list = vpr_setup.RouterOpts.flat_routing ? (const Netlist<>&)g_vpr_ctx.atom().nlist : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; - vpr_free_all(net_list, Arch, vpr_setup); + vpr_free_all(Arch, vpr_setup); return ERROR_EXIT_CODE; } diff --git a/vpr/src/pack/cluster_router.cpp b/vpr/src/pack/cluster_router.cpp index 6c44ff7f8b9..892d508f107 100644 --- a/vpr/src/pack/cluster_router.cpp +++ b/vpr/src/pack/cluster_router.cpp @@ -1328,7 +1328,7 @@ static void reset_explored_node_tb(t_lb_router_data* router_data) { } } -/* Save last successful intra-logic block route and reset current traceback */ +/* Save last successful intra-logic block route and reset current lb_traceback */ static void save_and_reset_lb_route(t_lb_router_data* router_data) { std::vector& lb_nets = *router_data->intra_lb_nets; @@ -1446,7 +1446,7 @@ static std::string describe_congested_rr_nodes(const std::vector& congested for (unsigned int inet = 0; inet < lb_nets.size(); inet++) { AtomNetId atom_net = lb_nets[inet].atom_net_id; - //Walk the traceback to find congested RR nodes for each net + //Walk the lb_traceback to find congested RR nodes for each net std::queue q; if (lb_nets[inet].rt_tree) { diff --git a/vpr/src/pack/pack_types.h b/vpr/src/pack/pack_types.h index ce7e2bba557..3c587bcb464 100644 --- a/vpr/src/pack/pack_types.h +++ b/vpr/src/pack/pack_types.h @@ -253,7 +253,7 @@ struct t_lb_router_data { bool is_routed; /* Stores whether or not the current logical-to-physical mapping has a routed solution */ /* Stores state info during Pathfinder iterative routing */ - t_explored_node_tb* explored_node_tb; /* [0..lb_type_graph->size()-1] Stores mode exploration and traceback info for nodes */ + t_explored_node_tb* explored_node_tb; /* [0..lb_type_graph->size()-1] Stores mode exploration and lb_traceback info for nodes */ int explore_id_index; /* used in conjunction with node_traceback to determine whether or not a location has been explored. By using a unique identifier every route, I don't have to clear the previous route exploration */ /* Current type */ diff --git a/vpr/src/place/timing_place_lookup.cpp b/vpr/src/place/timing_place_lookup.cpp index 3936e1cf4f7..55667ecb8d0 100644 --- a/vpr/src/place/timing_place_lookup.cpp +++ b/vpr/src/place/timing_place_lookup.cpp @@ -19,7 +19,6 @@ #include "globals.h" #include "place_and_route.h" #include "route_common.h" -#include "route_tree_timing.h" #include "route_timing.h" #include "route_export.h" #include "rr_graph.h" @@ -210,7 +209,7 @@ std::unique_ptr compute_place_delay_model(const t_placer_opts& } /*free all data structures that are no longer needed */ - free_routing_structs(net_list); + free_routing_structs(); return place_delay_model; } diff --git a/vpr/src/power/power.cpp b/vpr/src/power/power.cpp index c12d503e1c6..7591d2f183c 100644 --- a/vpr/src/power/power.cpp +++ b/vpr/src/power/power.cpp @@ -810,28 +810,29 @@ static void power_usage_routing(t_power_usage* power_usage, /* Populate net indices into rr graph */ for (auto net_id : cluster_ctx.clb_nlist.nets()) { - t_trace* trace; - - for (trace = route_ctx.trace[get_cluster_net_parent_id(g_vpr_ctx.atom().lookup, net_id, is_flat)].head; - trace != nullptr; trace = trace->next) { - rr_node_power[trace->index].visited = false; - rr_node_power[trace->index].net_num = net_id; + ParentNetId parent_id = get_cluster_net_parent_id(g_vpr_ctx.atom().lookup, net_id, is_flat); + if (!route_ctx.route_trees[parent_id]) + continue; + for (auto& rt_node : route_ctx.route_trees[parent_id].value().all_nodes()) { + rr_node_power[size_t(rt_node.inode)].visited = false; + rr_node_power[size_t(rt_node.inode)].net_num = net_id; } } /* Populate net indices into rr graph */ for (auto net_id : cluster_ctx.clb_nlist.nets()) { - t_trace* trace; - - for (trace = route_ctx.trace[ParentNetId(size_t(net_id))].head; trace != nullptr; trace = trace->next) { - t_rr_node_power* node_power = &rr_node_power[trace->index]; + ParentNetId parent_id = get_cluster_net_parent_id(g_vpr_ctx.atom().lookup, net_id, is_flat); + if (!route_ctx.route_trees[parent_id]) + continue; + for (auto& rt_node : route_ctx.route_trees[parent_id].value().all_nodes()) { + t_rr_node_power* node_power = &rr_node_power[size_t(rt_node.inode)]; if (node_power->visited) { continue; } - for (t_edge_size edge_idx = 0; edge_idx < rr_graph.num_edges(RRNodeId(trace->index)); edge_idx++) { - const auto& next_node_id = size_t(rr_graph.edge_sink_node(RRNodeId(trace->index), edge_idx)); + for (t_edge_size edge_idx = 0; edge_idx < rr_graph.num_edges(rt_node.inode); edge_idx++) { + const auto& next_node_id = size_t(rr_graph.edge_sink_node(rt_node.inode, edge_idx)); if (next_node_id != size_t(OPEN)) { t_rr_node_power* next_node_power = &rr_node_power[next_node_id]; diff --git a/vpr/src/route/annotate_routing.cpp b/vpr/src/route/annotate_routing.cpp index 8a3a3248499..46762dd0f9b 100644 --- a/vpr/src/route/annotate_routing.cpp +++ b/vpr/src/route/annotate_routing.cpp @@ -12,11 +12,11 @@ #include "annotate_routing.h" /******************************************************************** - * Create a mapping between each rr_node and its mapped nets + * Create a mapping between each rr_node and its mapped nets * based on VPR routing results * - Store the net ids mapped to each routing resource nodes * - Mapped nodes should have valid net ids (except SOURCE and SINK nodes) - * - Unmapped rr_node will use invalid ids + * - Unmapped rr_node will use invalid ids *******************************************************************/ vtr::vector annotate_rr_node_nets(const Netlist<>& net_list, const DeviceContext& device_ctx, @@ -35,22 +35,21 @@ vtr::vector annotate_rr_node_nets(const Netlist<>& net_li if (net_list.net_is_ignored(net_id)) { continue; } - /* Ignore used in local cluster only, reserved one CLB pin */ if (net_list.net_sinks(net_id).empty()) { continue; } - t_trace* tptr = routing_ctx.trace[net_id].head; - while (tptr != nullptr) { - const RRNodeId& rr_node = RRNodeId(tptr->index); + + for (auto& rt_node : routing_ctx.route_trees[net_id].value().all_nodes()) { + const RRNodeId rr_node = rt_node.inode; /* Ignore source and sink nodes, they are the common node multiple starting and ending points */ if ((SOURCE != rr_graph.node_type(rr_node)) && (SINK != rr_graph.node_type(rr_node))) { /* Sanity check: ensure we do not revoke any net mapping * In some routing architectures, node capacity is more than 1 * which allows a node to be mapped by multiple nets - * Therefore, the sanity check should focus on the nodes - * whose capacity is 1 + * Therefore, the sanity check should focus on the nodes + * whose capacity is 1 */ if ((rr_node_nets[rr_node]) && (1 == rr_graph.node_capacity(rr_node)) @@ -71,7 +70,6 @@ vtr::vector annotate_rr_node_nets(const Netlist<>& net_li } counter++; } - tptr = tptr->next; } } diff --git a/vpr/src/route/check_route.cpp b/vpr/src/route/check_route.cpp index 5c81895dcf9..e2c048fe0f7 100644 --- a/vpr/src/route/check_route.cpp +++ b/vpr/src/route/check_route.cpp @@ -1,5 +1,6 @@ #include +#include "route_common.h" #include "vtr_assert.h" #include "vtr_log.h" #include "vtr_memory.h" @@ -14,11 +15,10 @@ #include "rr_graph.h" #include "check_rr_graph.h" #include "read_xml_arch_file.h" -#include "route_tree_type.h" -#include "route_tree_timing.h" +#include "route_tree.h" /******************** Subroutines local to this module **********************/ -static void check_node_and_range(int inode, +static void check_node_and_range(RRNodeId inode, enum e_route_type route_type, bool is_flat); static void check_source(const Netlist<>& net_list, @@ -26,14 +26,15 @@ static void check_source(const Netlist<>& net_list, ParentNetId net_id, bool is_flat); static void check_sink(const Netlist<>& net_list, - int inode, + RRNodeId inode, int net_pin_index, ParentNetId net_id, bool* pin_done); -static void check_switch(t_trace* tptr, int num_switch); -static bool check_adjacent(int from_node, int to_node, bool is_flat); -static int chanx_chany_adjacent(int chanx_node, int chany_node); -static void reset_flags(ParentNetId inet, bool* connected_to_route); + +static void check_switch(const RouteTreeNode& rt_node, size_t num_switch); +static bool check_adjacent(RRNodeId from_node, RRNodeId to_node, bool is_flat); +static int chanx_chany_adjacent(RRNodeId chanx_node, RRNodeId chany_node); + static void check_locally_used_clb_opins(const t_clb_opins_used& clb_opins_used_locally, enum e_route_type route_type, bool is_flat); @@ -64,16 +65,15 @@ void check_route(const Netlist<>& net_list, return; } - int max_pins, inode, net_pin_index, prev_node; + int max_pins; unsigned int ipin; bool valid, connects; - t_trace* tptr; auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; auto& route_ctx = g_vpr_ctx.routing(); - const int num_switches = rr_graph.num_rr_switches(); + const size_t num_switches = rr_graph.num_rr_switches(); VTR_LOG("\n"); VTR_LOG("Checking to ensure routing is legal...\n"); @@ -95,9 +95,6 @@ void check_route(const Netlist<>& net_list, is_flat); } - auto connected_to_route = std::make_unique(rr_graph.num_nodes()); - std::fill_n(connected_to_route.get(), rr_graph.num_nodes(), false); - max_pins = 0; for (auto net_id : net_list.nets()) max_pins = std::max(max_pins, (int)net_list.net_pins(net_id).size()); @@ -111,66 +108,44 @@ void check_route(const Netlist<>& net_list, std::fill_n(pin_done.get(), net_list.net_pins(net_id).size(), false); - /* Check the SOURCE of the net. */ - tptr = route_ctx.trace[net_id].head; - if (tptr == nullptr) { + if (!route_ctx.route_trees[net_id]) { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "in check_route: net %d has no routing.\n", size_t(net_id)); } - inode = tptr->index; - check_node_and_range(inode, route_type, is_flat); - check_switch(tptr, num_switches); - connected_to_route[inode] = true; /* Mark as in path. */ + /* Check the SOURCE of the net. */ + RRNodeId source_inode = route_ctx.route_trees[net_id].value().root().inode; + check_node_and_range(source_inode, route_type, is_flat); + check_source(net_list, source_inode, net_id, is_flat); - check_source(net_list, RRNodeId(inode), net_id, is_flat); pin_done[0] = true; - prev_node = inode; - int prev_switch = tptr->iswitch; - tptr = tptr->next; - /* Check the rest of the net */ size_t num_sinks = 0; - while (tptr != nullptr) { - inode = tptr->index; - net_pin_index = tptr->net_pin_index; + for (auto& rt_node : route_ctx.route_trees[net_id].value().all_nodes()) { + RRNodeId inode = rt_node.inode; + int net_pin_index = rt_node.net_pin_index; check_node_and_range(inode, route_type, is_flat); - check_switch(tptr, num_switches); + check_switch(rt_node, num_switches); - if (prev_switch == OPEN) { //Start of a new branch - if (connected_to_route[inode] == false) { - VPR_ERROR(VPR_ERROR_ROUTE, - "in check_route: node %d does not link into existing routing for net %d.\n", inode, size_t(net_id)); - } - } else { //Continuing along existing branch - connects = check_adjacent(prev_node, inode, is_flat); + if (rt_node.parent()) { + connects = check_adjacent(rt_node.parent()->inode, rt_node.inode, is_flat); if (!connects) { VPR_ERROR(VPR_ERROR_ROUTE, "in check_route: found non-adjacent segments in traceback while checking net %d:\n" " %s\n" " %s\n", size_t(net_id), - describe_rr_node(rr_graph, device_ctx.grid, device_ctx.rr_indexed_data, prev_node, is_flat).c_str(), - describe_rr_node(rr_graph, device_ctx.grid, device_ctx.rr_indexed_data, inode, is_flat).c_str()); - } - - connected_to_route[inode] = true; /* Mark as in path. */ - - if (rr_graph.node_type(RRNodeId(inode)) == SINK) { - check_sink(net_list, - inode, - net_pin_index, - net_id, - pin_done.get()); - num_sinks += 1; + describe_rr_node(rr_graph, device_ctx.grid, device_ctx.rr_indexed_data, size_t(rt_node.parent()->inode), is_flat).c_str(), + describe_rr_node(rr_graph, device_ctx.grid, device_ctx.rr_indexed_data, size_t(inode), is_flat).c_str()); } + } - } /* End of prev_node type != SINK */ - prev_node = inode; - prev_switch = tptr->iswitch; - tptr = tptr->next; - } /* End while */ + if (rr_graph.node_type(inode) == SINK) { + check_sink(net_list, inode, net_pin_index, net_id, pin_done.get()); + num_sinks += 1; + } + } if (num_sinks != net_list.net_sinks(net_id).size()) { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, @@ -186,12 +161,7 @@ void check_route(const Netlist<>& net_list, } } - check_net_for_stubs(net_list, - net_id, - is_flat); - - reset_flags(net_id, connected_to_route.get()); - + check_net_for_stubs(net_list, net_id, is_flat); } /* End for each net */ if (check_route_option == e_check_route_option::FULL) { @@ -207,14 +177,14 @@ void check_route(const Netlist<>& net_list, /* Checks that this SINK node is one of the terminals of inet, and marks * * the appropriate pin as being reached. */ static void check_sink(const Netlist<>& net_list, - int inode, + RRNodeId inode, int net_pin_index, ParentNetId net_id, bool* pin_done) { auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; - VTR_ASSERT(rr_graph.node_type(RRNodeId(inode)) == SINK); + VTR_ASSERT(rr_graph.node_type(inode) == SINK); if (net_pin_index == OPEN) { /* If there is no legal net pin index associated with this sink node */ VPR_FATAL_ERROR(VPR_ERROR_ROUTE, @@ -265,61 +235,20 @@ static void check_source(const Netlist<>& net_list, } } -static void check_switch(t_trace* tptr, int num_switch) { - /* Checks that the switch leading from this traceback element to the next * - * one is a legal switch type. */ - - int inode; - short switch_type; - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - - inode = tptr->index; - switch_type = tptr->iswitch; - - if (rr_graph.node_type(RRNodeId(inode)) != SINK) { - if (switch_type >= num_switch) { - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, - "in check_switch: rr_node %d left via switch type %d.\n" - "\tSwitch type is out of range.\n", - inode, switch_type); - } - } - - else { /* Is a SINK */ - - /* Without feedthroughs, there should be no switch. If feedthroughs are * - * allowed, change to treat a SINK like any other node (as above). */ - - if (switch_type != OPEN) { - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, - "in check_switch: rr_node %d is a SINK, but attempts to use a switch of type %d.\n", inode, switch_type); - } - } -} - -static void reset_flags(ParentNetId inet, bool* connected_to_route) { - /* This routine resets the flags of all the channel segments contained * - * in the traceback of net inet to 0. This allows us to check the * - * next net for connectivity (and the default state of the flags * - * should always be zero after they have been used). */ - - t_trace* tptr; - int inode; - - auto& route_ctx = g_vpr_ctx.routing(); - - tptr = route_ctx.trace[inet].head; +static void check_switch(const RouteTreeNode& rt_node, size_t num_switch) { + /* Checks that the switch leading to this rt_node is a legal switch type. */ + if (!rt_node.parent()) + return; - while (tptr != nullptr) { - inode = tptr->index; - connected_to_route[inode] = false; /* Not in routed path now. */ - tptr = tptr->next; + if (size_t(rt_node.parent_switch) >= num_switch) { + VPR_FATAL_ERROR(VPR_ERROR_ROUTE, + "in check_switch: rr_node %d left via switch type %d.\n" + "\tSwitch type is out of range.\n", + size_t(rt_node.inode), size_t(rt_node.parent_switch)); } } -static bool check_adjacent(int from_node, int to_node, bool is_flat) { +static bool check_adjacent(RRNodeId from_node, RRNodeId to_node, bool is_flat) { /* This routine checks if the rr_node to_node is reachable from from_node. * * It returns true if is reachable and false if it is not. Check_node has * * already been used to verify that both nodes are valid rr_nodes, so only * @@ -340,7 +269,7 @@ static bool check_adjacent(int from_node, int to_node, bool is_flat) { reached = false; for (t_edge_size iconn = 0; iconn < rr_graph.num_edges(RRNodeId(from_node)); iconn++) { - if (size_t(rr_graph.edge_sink_node(RRNodeId(from_node), iconn)) == size_t(to_node)) { + if (size_t(rr_graph.edge_sink_node(from_node, iconn)) == size_t(to_node)) { reached = true; break; } @@ -355,20 +284,18 @@ static bool check_adjacent(int from_node, int to_node, bool is_flat) { num_adj = 0; - auto from_rr = RRNodeId(from_node); - auto to_rr = RRNodeId(to_node); - from_type = rr_graph.node_type(from_rr); - from_xlow = rr_graph.node_xlow(from_rr); - from_ylow = rr_graph.node_ylow(from_rr); - from_xhigh = rr_graph.node_xhigh(from_rr); - from_yhigh = rr_graph.node_yhigh(from_rr); - from_ptc = rr_graph.node_ptc_num(from_rr); - to_type = rr_graph.node_type(to_rr); - to_xlow = rr_graph.node_xlow(to_rr); - to_ylow = rr_graph.node_ylow(to_rr); - to_xhigh = rr_graph.node_xhigh(to_rr); - to_yhigh = rr_graph.node_yhigh(to_rr); - to_ptc = rr_graph.node_ptc_num(to_rr); + from_type = rr_graph.node_type(from_node); + from_xlow = rr_graph.node_xlow(from_node); + from_ylow = rr_graph.node_ylow(from_node); + from_xhigh = rr_graph.node_xhigh(from_node); + from_yhigh = rr_graph.node_yhigh(from_node); + from_ptc = rr_graph.node_ptc_num(from_node); + to_type = rr_graph.node_type(to_node); + to_xlow = rr_graph.node_xlow(to_node); + to_ylow = rr_graph.node_ylow(to_node); + to_xhigh = rr_graph.node_xhigh(to_node); + to_yhigh = rr_graph.node_yhigh(to_node); + to_ptc = rr_graph.node_ptc_num(to_node); switch (from_type) { case SOURCE: @@ -447,8 +374,8 @@ static bool check_adjacent(int from_node, int to_node, bool is_flat) { if (to_type == IPIN) { num_adj += 1; //adjacent } else if (to_type == CHANX) { - from_xhigh = rr_graph.node_xhigh(from_rr); - to_xhigh = rr_graph.node_xhigh(to_rr); + from_xhigh = rr_graph.node_xhigh(from_node); + to_xhigh = rr_graph.node_xhigh(to_node); if (from_ylow == to_ylow) { /* UDSD Modification by WMF Begin */ /*For Fs > 3, can connect to overlapping wire segment */ @@ -480,8 +407,8 @@ static bool check_adjacent(int from_node, int to_node, bool is_flat) { if (to_type == IPIN) { num_adj += 1; //adjacent } else if (to_type == CHANY) { - from_yhigh = rr_graph.node_yhigh(from_rr); - to_yhigh = rr_graph.node_yhigh(to_rr); + from_yhigh = rr_graph.node_yhigh(from_node); + to_yhigh = rr_graph.node_yhigh(to_node); if (from_xlow == to_xlow) { /* UDSD Modification by WMF Begin */ if (to_yhigh == from_ylow - 1 || from_yhigh == to_ylow - 1) { @@ -522,14 +449,14 @@ static bool check_adjacent(int from_node, int to_node, bool is_flat) { return false; //Should not reach here once thrown } -static int chanx_chany_adjacent(int chanx_node, int chany_node) { +static int chanx_chany_adjacent(RRNodeId chanx_node, RRNodeId chany_node) { /* Returns 1 if the specified CHANX and CHANY nodes are adjacent, 0 * * otherwise. */ auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; - if (rr_graph.nodes_are_adjacent(RRNodeId(chanx_node), RRNodeId(chany_node))) { + if (rr_graph.nodes_are_adjacent(chanx_node, chany_node)) { return (1); } else { return (0); @@ -542,10 +469,6 @@ void recompute_occupancy_from_scratch(const Netlist<>& net_list, bool is_flat) { * according to the resource usage of the current routing. It does a * brute force recompute from scratch that is useful for sanity checking. */ - - int inode, iclass, ipin, num_local_opins; - t_trace* tptr; - auto& route_ctx = g_vpr_ctx.mutable_routing(); auto& device_ctx = g_vpr_ctx.device(); @@ -557,24 +480,15 @@ void recompute_occupancy_from_scratch(const Netlist<>& net_list, bool is_flat) { /* Now go through each net and count the tracks and pins used everywhere */ for (auto net_id : net_list.nets()) { - if (net_list.net_is_ignored(net_id)) /* Skip ignored nets. */ + if (!route_ctx.route_trees[net_id]) continue; - tptr = route_ctx.trace[net_id].head; - if (tptr == nullptr) + if (net_list.net_is_ignored(net_id)) /* Skip ignored nets. */ continue; - for (;;) { - inode = tptr->index; + for (auto& rt_node : route_ctx.route_trees[net_id].value().all_nodes()) { + size_t inode = size_t(rt_node.inode); route_ctx.rr_node_route_inf[inode].set_occ(route_ctx.rr_node_route_inf[inode].occ() + 1); - - if (tptr->iswitch == OPEN) { - tptr = tptr->next; /* Skip next segment. */ - if (tptr == nullptr) - break; - } - - tptr = tptr->next; } } @@ -585,11 +499,11 @@ void recompute_occupancy_from_scratch(const Netlist<>& net_list, bool is_flat) { * locally). */ for (auto blk_id : net_list.blocks()) { auto cluster_blk_id = convert_to_cluster_block_id(blk_id); - for (iclass = 0; iclass < (int)physical_tile_type(cluster_blk_id)->class_inf.size(); iclass++) { - num_local_opins = route_ctx.clb_opins_used_locally[cluster_blk_id][iclass].size(); + for (int iclass = 0; iclass < (int)physical_tile_type(cluster_blk_id)->class_inf.size(); iclass++) { + int num_local_opins = route_ctx.clb_opins_used_locally[cluster_blk_id][iclass].size(); /* Will always be 0 for pads or SINK classes. */ - for (ipin = 0; ipin < num_local_opins; ipin++) { - inode = route_ctx.clb_opins_used_locally[cluster_blk_id][iclass][ipin]; + for (int ipin = 0; ipin < num_local_opins; ipin++) { + int inode = route_ctx.clb_opins_used_locally[cluster_blk_id][iclass][ipin]; VTR_ASSERT(inode >= 0 && inode < (ssize_t)device_ctx.rr_graph.num_nodes()); route_ctx.rr_node_route_inf[inode].set_occ(route_ctx.rr_node_route_inf[inode].occ() + 1); } @@ -618,7 +532,7 @@ static void check_locally_used_clb_opins(const t_clb_opins_used& clb_opins_used_ for (ipin = 0; ipin < num_local_opins; ipin++) { inode = clb_opins_used_locally[blk_id][iclass][ipin]; - check_node_and_range(inode, route_type, is_flat); /* Node makes sense? */ + check_node_and_range(RRNodeId(inode), route_type, is_flat); /* Node makes sense? */ /* Now check that node is an OPIN of the right type. */ @@ -642,7 +556,7 @@ static void check_locally_used_clb_opins(const t_clb_opins_used& clb_opins_used_ } } -static void check_node_and_range(int inode, +static void check_node_and_range(RRNodeId inode, enum e_route_type route_type, bool is_flat) { /* Checks that inode is within the legal range, then calls check_node to * @@ -650,16 +564,16 @@ static void check_node_and_range(int inode, auto& device_ctx = g_vpr_ctx.device(); - if (inode < 0 || inode >= (int)device_ctx.rr_graph.num_nodes()) { + if (size_t(inode) >= device_ctx.rr_graph.num_nodes()) { VPR_FATAL_ERROR(VPR_ERROR_ROUTE, - "in check_node_and_range: rr_node #%d is out of legal, range (0 to %d).\n", inode, device_ctx.rr_graph.num_nodes() - 1); + "in check_node_and_range: rr_node #%zu is out of legal range (0 to %d).\n", size_t(inode), device_ctx.rr_graph.num_nodes() - 1); } check_rr_node(device_ctx.rr_graph, device_ctx.rr_indexed_data, device_ctx.grid, device_ctx.chan_width, route_type, - inode, + size_t(inode), is_flat); } @@ -677,7 +591,7 @@ static void check_all_non_configurable_edges(const Netlist<>& net_list, bool is_ } } -//Checks that the specified routing is legal with respect to non-configurable edges +// Checks that the specified routing is legal with respect to non-configurable edges // //For routing to be legal if *any* non-configurable edge is used, so must *all* //other non-configurable edges in the same set @@ -686,26 +600,24 @@ static bool check_non_configurable_edges(const Netlist<>& net_list, const t_non_configurable_rr_sets& non_configurable_rr_sets, bool is_flat) { const auto& device_ctx = g_vpr_ctx.device(); - auto& route_ctx = g_vpr_ctx.routing(); - t_trace* head = route_ctx.trace[net].head; + auto& route_ctx = g_vpr_ctx.mutable_routing(); - //Collect all the edges used by this net's routing + if (!route_ctx.route_trees[net]) // no routing + return true; + + // Collect all the edges used by this net's routing std::set routing_edges; std::set routing_nodes; - for (t_trace* trace = head; trace != nullptr; trace = trace->next) { - int inode = trace->index; - - routing_nodes.insert(inode); - - if (trace->iswitch == OPEN) { - continue; //End of branch - } else if (trace->next) { - int inode_next = trace->next->index; - - t_node_edge edge = {inode, inode_next}; - - routing_edges.insert(edge); - } + for (auto& rt_node : route_ctx.route_trees[net].value().all_nodes()) { + routing_nodes.insert(size_t(rt_node.inode)); + if (!rt_node.parent()) + continue; + /* a lot of casts to silence the warnings + * to clean this up: fix the type in vpr_types.h to use RRNodeIDs instead */ + int parent_inode = size_t(rt_node.parent()->inode); + int this_inode = size_t(rt_node.inode); + t_node_edge edge = {parent_inode, this_inode}; + routing_edges.insert(edge); } //We need to perform two types of checks: @@ -845,9 +757,7 @@ static bool check_non_configurable_edges(const Netlist<>& net_list, // are children of a configurable node have at least one sink. class StubFinder { public: - StubFinder(const Netlist<>& net_list, bool is_flat) - : net_list_(net_list) - , is_flat_(is_flat) {} + StubFinder() {} // Checks specified net for stubs, return true if at least one stub is // found. @@ -859,14 +769,12 @@ class StubFinder { } private: - bool RecurseTree(t_rt_node* rt_root); + bool RecurseTree(const RouteTreeNode& rt_node); // Set of stub nodes // Note this is an ordered set so that node output is sorted by node // id. std::set stub_nodes_; - const Netlist<>& net_list_; - bool is_flat_; }; //Cheks for stubs in a net's routing. @@ -878,7 +786,7 @@ class StubFinder { void check_net_for_stubs(const Netlist<>& net_list, ParentNetId net, bool is_flat) { - StubFinder stub_finder(net_list, is_flat); + StubFinder stub_finder; bool any_stubs = stub_finder.CheckNet(net); if (any_stubs) { @@ -898,43 +806,44 @@ void check_net_for_stubs(const Netlist<>& net_list, } bool StubFinder::CheckNet(ParentNetId net) { + auto& route_ctx = g_vpr_ctx.mutable_routing(); stub_nodes_.clear(); - t_rt_node* rt_root = traceback_to_route_tree(net, is_flat_); - RecurseTree(rt_root); - free_route_tree(rt_root); + if (!route_ctx.route_trees[net]) + return false; + + RecurseTree(route_ctx.route_trees[net].value().root()); return !stub_nodes_.empty(); } // Returns true if this node is a stub. -bool StubFinder::RecurseTree(t_rt_node* rt_root) { +bool StubFinder::RecurseTree(const RouteTreeNode& rt_node) { auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; - if (rt_root->u.child_list == nullptr) { + if (rt_node.is_leaf()) { //If a leaf of the route tree is not a SINK, then it is a stub - if (rr_graph.node_type(RRNodeId(rt_root->inode)) != SINK) { + if (rr_graph.node_type(rt_node.inode) != SINK) { return true; //It is the current root of this stub } else { return false; } - } else { - bool is_stub = true; - for (t_linked_rt_edge* edge = rt_root->u.child_list; edge != nullptr; edge = edge->next) { - bool driver_switch_configurable = rr_graph.rr_switch_inf(RRSwitchId(edge->iswitch)).configurable(); - - bool child_is_stub = RecurseTree(edge->child); - if (!child_is_stub) { - // Because the child was not a stub, this node is not a stub. - is_stub = false; - } else if (driver_switch_configurable) { - // This child was stub, and we drove it from a configurable - // edge, this is an error. - stub_nodes_.insert(edge->child->inode); - } - } + } - return is_stub; + bool is_stub = true; + for (auto& child_node : rt_node.child_nodes()) { + bool driver_switch_configurable = rr_graph.rr_switch_inf(child_node.parent_switch).configurable(); + bool child_is_stub = RecurseTree(child_node); + if (!child_is_stub) { + // Because the child was not a stub, this node is not a stub. + is_stub = false; + } else if (driver_switch_configurable) { + // This child was stub, and we drove it from a configurable + // edge, this is an error. + stub_nodes_.insert(size_t(child_node.inode)); + } } + + return is_stub; } diff --git a/vpr/src/route/connection_based_routing.h b/vpr/src/route/connection_based_routing.h index bf2e534149f..9e24998ac95 100644 --- a/vpr/src/route/connection_based_routing.h +++ b/vpr/src/route/connection_based_routing.h @@ -1,7 +1,7 @@ #pragma once #include #include -#include "route_tree_type.h" +#include "route_tree_fwd.h" #include "vpr_types.h" #include "timing_info.h" #include "vpr_net_pins_matrix.h" @@ -24,7 +24,7 @@ class Connection_based_routing_resources { // contains rt_nodes representing sinks reached legally while pruning the route tree // used to populate rt_node_of_sink after building route tree from traceback // order does not matter - std::vector reached_rt_sinks; + std::vector reached_rt_sinks; public: Connection_based_routing_resources(const Netlist<>& net_list, @@ -34,11 +34,11 @@ class Connection_based_routing_resources { // mark rr sink node as something that still needs to be reached void toreach_rr_sink(int rr_sink_node) { remaining_targets.push_back(rr_sink_node); } // mark rt sink node as something that has been legally reached - void reached_rt_sink(t_rt_node* rt_sink) { reached_rt_sinks.push_back(rt_sink); } + void reached_rt_sink(RRNodeId rt_sink) { reached_rt_sinks.push_back(rt_sink); } // get a handle on the resources std::vector& get_remaining_targets() { return remaining_targets; } - std::vector& get_reached_rt_sinks() { return reached_rt_sinks; } + std::vector& get_reached_rt_sinks() { return reached_rt_sinks; } bool sanity_check_lookup() const; diff --git a/vpr/src/route/connection_based_routing_fwd.h b/vpr/src/route/connection_based_routing_fwd.h new file mode 100644 index 00000000000..7f65017ac1e --- /dev/null +++ b/vpr/src/route/connection_based_routing_fwd.h @@ -0,0 +1,4 @@ +#pragma once + +class Connection_based_routing_resources; +using CBRR = Connection_based_routing_resources; // shorthand diff --git a/vpr/src/route/connection_router.cpp b/vpr/src/route/connection_router.cpp index 1e4d3f911ea..d5bd3e7a93d 100644 --- a/vpr/src/route/connection_router.cpp +++ b/vpr/src/route/connection_router.cpp @@ -1,21 +1,13 @@ #include "connection_router.h" -#include "route_tree_timing.h" #include "rr_graph.h" #include "binary_heap.h" #include "bucket.h" -//Finds a path from the route tree rooted at rt_root to sink_node -// -//This is used when you want to allow previous routing of the same net to serve -//as valid start locations for the current connection. -// -//Returns either the last element of the path, or nullptr if no path is found - inline static bool relevant_node_to_target(const RRGraphView* rr_graph, RRNodeId node_to_add, RRNodeId target_node) { - VTR_ASSERT(rr_graph->node_type(RRNodeId(target_node)) == t_rr_type::SINK); + VTR_ASSERT(rr_graph->node_type(target_node) == t_rr_type::SINK); auto node_to_add_type = rr_graph->node_type(node_to_add); if (node_to_add_type == t_rr_type::OPIN || node_to_add_type == t_rr_type::SOURCE || node_to_add_type == t_rr_type::CHANX || node_to_add_type == t_rr_type::CHANY || node_to_add_type == SINK) { return true; @@ -65,7 +57,7 @@ inline void update_router_stats(const DeviceContext& device_ctx, template std::pair ConnectionRouter::timing_driven_route_connection_from_route_tree( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, int sink_node, const t_conn_cost_params cost_params, t_bb bounding_box, @@ -91,7 +83,7 @@ std::pair ConnectionRouter::timing_driven_route_connection_f template t_heap* ConnectionRouter::timing_driven_route_connection_common_setup( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, int sink_node, const t_conn_cost_params cost_params, t_bb bounding_box) { @@ -101,12 +93,10 @@ t_heap* ConnectionRouter::timing_driven_route_connection_common_setup( add_route_tree_to_heap(rt_root, sink_node, cost_params, false); heap_.build_heap(); // via sifting down everything - int source_node = rt_root->inode; + int source_node = size_t(rt_root.inode); if (heap_.is_empty_heap()) { VTR_LOG("No source in route tree: %s\n", describe_unrouteable_connection(source_node, sink_node, is_flat_).c_str()); - - free_route_tree(rt_root); return nullptr; } @@ -119,12 +109,12 @@ t_heap* ConnectionRouter::timing_driven_route_connection_common_setup( bounding_box); if (cheapest == nullptr) { - //Found no path found within the current bounding box. - //Try again with no bounding box (i.e. a full device grid bounding box). + // No path found within the current bounding box. + // Try again with no bounding box (i.e. a full device grid bounding box). // - //Note that the additional run-time overhead of re-trying only occurs - //when we were otherwise going to give up -- the typical case (route - //found with the bounding box) remains fast and never re-tries . + // Note that the additional run-time overhead of re-trying only occurs + // when we were otherwise going to give up -- the typical case (route + // found with the bounding box) remains fast and never re-tries . VTR_LOG_WARN("No routing path for connection to sink_rr %d, retrying with full device bounding box\n", sink_node); t_bb full_device_bounding_box; @@ -163,8 +153,6 @@ t_heap* ConnectionRouter::timing_driven_route_connection_common_setup( if (cheapest == nullptr) { VTR_LOG("%s\n", describe_unrouteable_connection(source_node, sink_node, is_flat_).c_str()); - - free_route_tree(rt_root); return nullptr; } @@ -177,7 +165,7 @@ t_heap* ConnectionRouter::timing_driven_route_connection_common_setup( //which is spatially close to the sink is added to the heap. template std::pair ConnectionRouter::timing_driven_route_connection_from_route_tree_high_fanout( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, int sink_node, const t_conn_cost_params cost_params, t_bb net_bounding_box, @@ -193,12 +181,10 @@ std::pair ConnectionRouter::timing_driven_route_connection_f t_bb high_fanout_bb = add_high_fanout_route_tree_to_heap(rt_root, sink_node, cost_params, spatial_rt_lookup, net_bounding_box); heap_.build_heap(); - int source_node = rt_root->inode; + int source_node = size_t(rt_root.inode); if (heap_.is_empty_heap()) { VTR_LOG("No source in route tree: %s\n", describe_unrouteable_connection(source_node, sink_node, is_flat_).c_str()); - - free_route_tree(rt_root); return std::make_pair(false, t_heap()); } @@ -245,11 +231,11 @@ std::pair ConnectionRouter::timing_driven_route_connection_f return std::make_pair(true, out); } -//Finds a path to sink_node, starting from the elements currently in the heap. +// Finds a path to sink_node, starting from the elements currently in the heap. // -//This is the core maze routing routine. +// This is the core maze routing routine. // -//Returns either the last element of the path, or nullptr if no path is found +// Returns either the last element of the path, or nullptr if no path is found template t_heap* ConnectionRouter::timing_driven_route_connection_from_heap(int sink_node, const t_conn_cost_params cost_params, @@ -277,7 +263,7 @@ t_heap* ConnectionRouter::timing_driven_route_connection_from_heap(int sin VTR_LOGV_DEBUG(router_debug_, " Popping node %d (cost: %g)\n", inode, cheapest->cost); - //Have we found the target? + // Have we found the target? if (inode == sink_node) { // If we're running RCV, the path will be stored in the path_data->path_rr vector // This is then placed into the traceback so that the correct path is returned @@ -289,7 +275,7 @@ t_heap* ConnectionRouter::timing_driven_route_connection_from_heap(int sin break; } - //If not, keep searching + // If not, keep searching timing_driven_expand_cheapest(cheapest, sink_node, cost_params, @@ -313,10 +299,10 @@ t_heap* ConnectionRouter::timing_driven_route_connection_from_heap(int sin return cheapest; } -//Find shortest paths from specified route tree to all nodes in the RR graph +// Find shortest paths from specified route tree to all nodes in the RR graph template std::vector ConnectionRouter::timing_driven_find_all_shortest_paths_from_route_tree( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, const t_conn_cost_params cost_params, t_bb bounding_box, RouterStats& router_stats, @@ -324,7 +310,7 @@ std::vector ConnectionRouter::timing_driven_find_all_shortest_path router_stats_ = &router_stats; conn_params_ = &conn_params; - //Add the route tree to the heap with no specific target node + // Add the route tree to the heap with no specific target node int target_node = OPEN; add_route_tree_to_heap(rt_root, target_node, cost_params, false); heap_.build_heap(); // via sifting down everything @@ -335,13 +321,13 @@ std::vector ConnectionRouter::timing_driven_find_all_shortest_path return res; } -//Find shortest paths from current heap to all nodes in the RR graph +// Find shortest paths from current heap to all nodes in the RR graph // -//Since there is no single *target* node this uses Dijkstra's algorithm -//with a modified exit condition (runs until heap is empty). +// Since there is no single *target* node this uses Dijkstra's algorithm +// with a modified exit condition (runs until heap is empty). // -//Note that to re-use code used for the regular A*-based router we use a -//no-operation lookahead which always returns zero. +// Note that to re-use code used for the regular A*-based router we use a +// no-operation lookahead which always returns zero. template std::vector ConnectionRouter::timing_driven_find_all_shortest_paths_from_heap( const t_conn_cost_params cost_params, @@ -350,7 +336,7 @@ std::vector ConnectionRouter::timing_driven_find_all_shortest_path VTR_ASSERT_SAFE(heap_.is_valid()); - if (heap_.is_empty_heap()) { //No source + if (heap_.is_empty_heap()) { // No source VTR_LOGV_DEBUG(router_debug_, " Initial heap empty (no source)\n"); } @@ -367,11 +353,11 @@ std::vector ConnectionRouter::timing_driven_find_all_shortest_path VTR_LOGV_DEBUG(router_debug_, " Popping node %d (cost: %g)\n", inode, cheapest->cost); - //Since we want to find shortest paths to all nodes in the graph - //we do not specify a target node. + // Since we want to find shortest paths to all nodes in the graph + // we do not specify a target node. // - //By setting the target_node to OPEN in combination with the NoOp router - //lookahead we can re-use the node exploration code from the regular router + // By setting the target_node to OPEN in combination with the NoOp router + // lookahead we can re-use the node exploration code from the regular router int target_node = OPEN; timing_driven_expand_cheapest(cheapest, @@ -416,8 +402,8 @@ void ConnectionRouter::timing_driven_expand_cheapest(t_heap* cheapest, * re-expansion based on a higher total cost. */ if (best_total_cost > new_total_cost && ((rcv_path_manager.is_enabled()) || best_back_cost > new_back_cost)) { - //Explore from this node, since the current/new partial path has the best cost - //found so far + // Explore from this node, since the current/new partial path has the best cost + // found so far VTR_LOGV_DEBUG(router_debug_, " Better cost to %d\n", inode); VTR_LOGV_DEBUG(router_debug_, " New total cost: %g\n", new_total_cost); VTR_LOGV_DEBUG(router_debug_, " New back cost: %g\n", new_back_cost); @@ -431,8 +417,8 @@ void ConnectionRouter::timing_driven_expand_cheapest(t_heap* cheapest, timing_driven_expand_neighbours(cheapest, cost_params, bounding_box, target_node); } else { - //Post-heap prune, do not re-explore from the current/new partial path as it - //has worse cost than the best partial path to this node found so far + // Post-heap prune, do not re-explore from the current/new partial path as it + // has worse cost than the best partial path to this node found so far VTR_LOGV_DEBUG(router_debug_, " Worse cost to %d\n", inode); VTR_LOGV_DEBUG(router_debug_, " Old total cost: %g\n", best_total_cost); VTR_LOGV_DEBUG(router_debug_, " Old back cost: %g\n", best_back_cost); @@ -457,7 +443,7 @@ void ConnectionRouter::timing_driven_expand_neighbours(t_heap* current, target_bb.ymax = rr_graph_->node_yhigh(RRNodeId(target_node)); } - //For each node associated with the current heap element, expand all of it's neighbors + // For each node associated with the current heap element, expand all of it's neighbors int from_node_int = current->index; RRNodeId from_node(from_node_int); auto edges = rr_nodes_.edge_range(from_node); @@ -500,9 +486,9 @@ void ConnectionRouter::timing_driven_expand_neighbours(t_heap* current, } } -//Conditionally adds to_node to the router heap (via path from from_node via from_edge). -//RR nodes outside the expanded bounding box specified in bounding_box are not added -//to the heap. +// Conditionally adds to_node to the router heap (via path from from_node via from_edge). +// RR nodes outside the expanded bounding box specified in bounding_box are not added +// to the heap. template void ConnectionRouter::timing_driven_expand_neighbour(t_heap* current, const int from_node, @@ -521,10 +507,10 @@ void ConnectionRouter::timing_driven_expand_neighbour(t_heap* current, // BB-pruning // Disable BB-pruning if RCV is enabled, as this can make it harder for circuits with high negative hold slack to resolve this // TODO: Only disable pruning if the net has negative hold slack, maybe go off budgets - if ((to_xhigh < bounding_box.xmin //Strictly left of BB left-edge - || to_xlow > bounding_box.xmax //Strictly right of BB right-edge - || to_yhigh < bounding_box.ymin //Strictly below BB bottom-edge - || to_ylow > bounding_box.ymax) //Strictly above BB top-edge + if ((to_xhigh < bounding_box.xmin // Strictly left of BB left-edge + || to_xlow > bounding_box.xmax // Strictly right of BB right-edge + || to_yhigh < bounding_box.ymin // Strictly below BB bottom-edge + || to_ylow > bounding_box.ymax) // Strictly above BB top-edge && !rcv_path_manager.is_enabled()) { VTR_LOGV_DEBUG(router_debug_, " Pruned expansion of node %d edge %zu -> %d" @@ -543,7 +529,7 @@ void ConnectionRouter::timing_driven_expand_neighbour(t_heap* current, if (target_node != OPEN) { t_rr_type to_type = rr_graph_->node_type(to_node); if (to_type == IPIN) { - //Check if this IPIN leads to the target block + // Check if this IPIN leads to the target block // IPIN's of the target block should be contained within it's bounding box if (to_xlow < target_bb.xmin || to_ylow < target_bb.ymin @@ -582,7 +568,7 @@ void ConnectionRouter::timing_driven_expand_neighbour(t_heap* current, } } -//Add to_node to the heap, and also add any nodes which are connected by non-configurable edges +// Add to_node to the heap, and also add any nodes which are connected by non-configurable edges template void ConnectionRouter::timing_driven_add_to_heap(const t_conn_cost_params cost_params, const t_heap* current, @@ -596,7 +582,7 @@ void ConnectionRouter::timing_driven_add_to_heap(const t_conn_cost_params // Initalize RCV data struct if needed, otherwise it's set to nullptr rcv_path_manager.alloc_path_struct(next.path_data); - //Costs initialized to current + // Costs initialized to current next.cost = std::numeric_limits::infinity(); //Not used directly next.backward_path_cost = current->backward_path_cost; @@ -892,7 +878,7 @@ void ConnectionRouter::empty_heap_annotating_node_route_inf() { //used as branch-points for further routing. template void ConnectionRouter::add_route_tree_to_heap( - t_rt_node* rt_node, + const RouteTreeNode& rt_node, int target_node, const t_conn_cost_params cost_params, bool from_high_fanout) { @@ -906,25 +892,19 @@ void ConnectionRouter::add_route_tree_to_heap( router_stats_->add_all_rt++; } - t_rt_node* child_node; - t_linked_rt_edge* linked_rt_edge; - /* Pre-order depth-first traversal */ // IPINs and SINKS are not re_expanded - if (rt_node->re_expand) { + if (rt_node.re_expand) { add_route_tree_node_to_heap(rt_node, target_node, cost_params, false); } - linked_rt_edge = rt_node->u.child_list; - - while (linked_rt_edge != nullptr) { - child_node = linked_rt_edge->child; + for (const RouteTreeNode& child_node : rt_node.child_nodes()) { if (is_flat_) { if (relevant_node_to_target(rr_graph_, - RRNodeId(child_node->inode), + child_node.inode, RRNodeId(target_node))) { add_route_tree_to_heap(child_node, target_node, @@ -937,29 +917,26 @@ void ConnectionRouter::add_route_tree_to_heap( cost_params, from_high_fanout); } - - linked_rt_edge = linked_rt_edge->next; } } //Unconditionally adds rt_node to the heap // -//Note that if you want to respect rt_node->re_expand that is the caller's +//Note that if you want to respect rt_node.re_expand that is the caller's //responsibility. template void ConnectionRouter::add_route_tree_node_to_heap( - t_rt_node* rt_node, + const RouteTreeNode& rt_node, int target_node, const t_conn_cost_params cost_params, bool is_high_fanout) { const auto& device_ctx = g_vpr_ctx.device(); - int inode = rt_node->inode; - float backward_path_cost = cost_params.criticality * rt_node->Tdel; - - float R_upstream = rt_node->R_upstream; + const RRNodeId inode = rt_node.inode; + float backward_path_cost = cost_params.criticality * rt_node.Tdel; + float R_upstream = rt_node.R_upstream; - //after budgets are loaded, calculate delay cost as described by RCV paper - /*R. Fung, V. Betz and W. Chow, "Slack Allocation and Routing to Improve FPGA Timing While + // after budgets are loaded, calculate delay cost as described by RCV paper + /* R. Fung, V. Betz and W. Chow, "Slack Allocation and Routing to Improve FPGA Timing While * Repairing Short-Path Violations," in IEEE Transactions on Computer-Aided Design of * Integrated Circuits and Systems, vol. 27, no. 4, pp. 686-697, April 2008.*/ // float expected_cost = router_lookahead_.get_expected_cost(inode, target_node, cost_params, R_upstream); @@ -968,23 +945,23 @@ void ConnectionRouter::add_route_tree_node_to_heap( // tot_cost = backward_path_cost + cost_params.astar_fac * expected_cost; float tot_cost = backward_path_cost + cost_params.astar_fac - * router_lookahead_.get_expected_cost(RRNodeId(inode), + * router_lookahead_.get_expected_cost(inode, RRNodeId(target_node), cost_params, R_upstream); VTR_LOGV_DEBUG(router_debug_, " Adding node %8d to heap from init route tree with cost %g (%s)\n", inode, tot_cost, - describe_rr_node(device_ctx.rr_graph, device_ctx.grid, device_ctx.rr_indexed_data, inode, is_flat_).c_str()); + describe_rr_node(device_ctx.rr_graph, device_ctx.grid, device_ctx.rr_indexed_data, size_t(inode), is_flat_).c_str()); push_back_node(&heap_, rr_node_route_inf_, - inode, tot_cost, NO_PREVIOUS, RREdgeId::INVALID(), + size_t(inode), tot_cost, NO_PREVIOUS, RREdgeId::INVALID(), backward_path_cost, R_upstream); } else { - float expected_total_cost = compute_node_cost_using_rcv(cost_params, inode, target_node, rt_node->Tdel, 0, R_upstream); + float expected_total_cost = compute_node_cost_using_rcv(cost_params, size_t(inode), target_node, rt_node.Tdel, 0, R_upstream); - push_back_node_with_info(&heap_, inode, expected_total_cost, - backward_path_cost, R_upstream, rt_node->Tdel, &rcv_path_manager); + push_back_node_with_info(&heap_, size_t(inode), expected_total_cost, + backward_path_cost, R_upstream, rt_node.Tdel, &rcv_path_manager); } update_router_stats(device_ctx, @@ -1015,7 +992,7 @@ static t_bb adjust_highfanout_bounding_box(t_bb highfanout_bb) { template t_bb ConnectionRouter::add_high_fanout_route_tree_to_heap( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, int target_node, const t_conn_cost_params cost_params, const SpatialRouteTreeLookup& spatial_rt_lookup, @@ -1058,23 +1035,23 @@ t_bb ConnectionRouter::add_high_fanout_route_tree_to_heap( if (bin_y > spatial_rt_lookup.dim_size(1) - 1) continue; //Out of range - for (t_rt_node* rt_node : spatial_rt_lookup[bin_x][bin_y]) { - if (!rt_node->re_expand) continue; //Some nodes (like IPINs) shouldn't be re-expanded - RRNodeId rr_node_to_add = RRNodeId(rt_node->inode); + for (const RouteTreeNode& rt_node : spatial_rt_lookup[bin_x][bin_y]) { + if (!rt_node.re_expand) continue; //Some nodes (like IPINs) shouldn't be re-expanded + RRNodeId rr_node_to_add = rt_node.inode; - //Put the node onto the heap if (is_flat_) { if (!relevant_node_to_target(rr_graph_, rr_node_to_add, target_node_id)) continue; } + // Put the node onto the heap add_route_tree_node_to_heap(rt_node, target_node, cost_params, true); - //Update Bounding Box - RRNodeId node(rt_node->inode); - highfanout_bb.xmin = std::min(highfanout_bb.xmin, rr_graph_->node_xlow(node)); - highfanout_bb.ymin = std::min(highfanout_bb.ymin, rr_graph_->node_ylow(node)); - highfanout_bb.xmax = std::max(highfanout_bb.xmax, rr_graph_->node_xhigh(node)); - highfanout_bb.ymax = std::max(highfanout_bb.ymax, rr_graph_->node_yhigh(node)); + + // Update Bounding Box + highfanout_bb.xmin = std::min(highfanout_bb.xmin, rr_graph_->node_xlow(rr_node_to_add)); + highfanout_bb.ymin = std::min(highfanout_bb.ymin, rr_graph_->node_ylow(rr_node_to_add)); + highfanout_bb.xmax = std::max(highfanout_bb.xmax, rr_graph_->node_xhigh(rr_node_to_add)); + highfanout_bb.ymax = std::max(highfanout_bb.ymax, rr_graph_->node_yhigh(rr_node_to_add)); if (is_flat_) { if (rr_graph_->node_type(rr_node_to_add) == CHANY || rr_graph_->node_type(rr_node_to_add) == CHANX) { chan_nodes_added++; diff --git a/vpr/src/route/connection_router.h b/vpr/src/route/connection_router.h index fa837c1fed6..5956d5b0ad9 100644 --- a/vpr/src/route/connection_router.h +++ b/vpr/src/route/connection_router.h @@ -5,7 +5,7 @@ #include "rr_graph_storage.h" #include "route_common.h" #include "router_lookahead.h" -#include "route_tree_type.h" +#include "route_tree.h" #include "rr_rc_data.h" #include "router_stats.h" #include "spatial_route_tree_lookup.h" @@ -68,7 +68,7 @@ class ConnectionRouter : public ConnectionRouterInterface { // Returns either the last element of the path, or nullptr if no path is // found std::pair timing_driven_route_connection_from_route_tree( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, int sink_node, const t_conn_cost_params cost_params, t_bb bounding_box, @@ -81,7 +81,7 @@ class ConnectionRouter : public ConnectionRouterInterface { // Unlike timing_driven_route_connection_from_route_tree(), only part of // the route tree which is spatially close to the sink is added to the heap. std::pair timing_driven_route_connection_from_route_tree_high_fanout( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, int sink_node, const t_conn_cost_params cost_params, t_bb bounding_box, @@ -99,7 +99,7 @@ class ConnectionRouter : public ConnectionRouterInterface { // empty). When using cost_params.astar_fac = 0, for efficiency the // RouterLookahead used should be the NoOpLookahead. std::vector timing_driven_find_all_shortest_paths_from_route_tree( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, const t_conn_cost_params cost_params, t_bb bounding_box, RouterStats& router_stats, @@ -148,7 +148,7 @@ class ConnectionRouter : public ConnectionRouterInterface { // timing_driven_route_connection_from_route_tree_high_fanout for running // connection router. t_heap* timing_driven_route_connection_common_setup( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, int sink_node, const t_conn_cost_params cost_params, t_bb bounding_box); @@ -224,7 +224,7 @@ class ConnectionRouter : public ConnectionRouterInterface { //Adds the route tree rooted at rt_node to the heap, preparing it to be //used as branch-points for further routing. - void add_route_tree_to_heap(t_rt_node* rt_node, + void add_route_tree_to_heap(const RouteTreeNode& rt_node, int target_node, const t_conn_cost_params cost_params, bool from_high_fanout); @@ -242,13 +242,13 @@ class ConnectionRouter : public ConnectionRouterInterface { //Note that if you want to respect rt_node->re_expand that is the caller's //responsibility. void add_route_tree_node_to_heap( - t_rt_node* rt_node, + const RouteTreeNode& rt_node, int target_node, const t_conn_cost_params cost_params, bool is_high_fanout); t_bb add_high_fanout_route_tree_to_heap( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, int target_node, const t_conn_cost_params cost_params, const SpatialRouteTreeLookup& spatial_route_tree_lookup, diff --git a/vpr/src/route/connection_router_interface.h b/vpr/src/route/connection_router_interface.h index 83fcf83ca65..c81d081b19a 100644 --- a/vpr/src/route/connection_router_interface.h +++ b/vpr/src/route/connection_router_interface.h @@ -4,7 +4,7 @@ #include #include "heap_type.h" -#include "route_tree_type.h" +#include "route_tree_fwd.h" #include "vpr_types.h" #include "router_stats.h" #include "spatial_route_tree_lookup.h" @@ -51,7 +51,7 @@ class ConnectionRouterInterface { // Returns either the last element of the path, or nullptr if no path is // found virtual std::pair timing_driven_route_connection_from_route_tree( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, int sink_node, const t_conn_cost_params cost_params, t_bb bounding_box, @@ -65,7 +65,7 @@ class ConnectionRouterInterface { // Unlike timing_driven_route_connection_from_route_tree(), only part of // the route tree which is spatially close to the sink is added to the heap. virtual std::pair timing_driven_route_connection_from_route_tree_high_fanout( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, int sink_node, const t_conn_cost_params cost_params, t_bb bounding_box, @@ -84,7 +84,7 @@ class ConnectionRouterInterface { // empty). When using cost_params.astar_fac = 0, for efficiency the // RouterLookahead used should be the NoOpLookahead. virtual std::vector timing_driven_find_all_shortest_paths_from_route_tree( - t_rt_node* rt_root, + const RouteTreeNode& rt_root, const t_conn_cost_params cost_params, t_bb bounding_box, RouterStats& router_stats, diff --git a/vpr/src/route/overuse_report.cpp b/vpr/src/route/overuse_report.cpp index 87520e6733c..1fd65115aea 100644 --- a/vpr/src/route/overuse_report.cpp +++ b/vpr/src/route/overuse_report.cpp @@ -170,12 +170,14 @@ void generate_overused_nodes_to_congested_net_lookup(const Netlist<>& net_list, //Create overused nodes to congested nets look up by //traversing through the net trace backs linked lists for (ParentNetId net_id : net_list.nets()) { - for (t_trace* tptr = route_ctx.trace[net_id].head; tptr != nullptr; tptr = tptr->next) { - int inode = tptr->index; + if (!route_ctx.route_trees[net_id]) + continue; - int overuse = route_ctx.rr_node_route_inf[inode].occ() - rr_graph.node_capacity(RRNodeId(inode)); + for (auto& rt_node : route_ctx.route_trees[net_id].value().all_nodes()) { + RRNodeId inode = rt_node.inode; + int overuse = route_ctx.rr_node_route_inf[size_t(inode)].occ() - rr_graph.node_capacity(inode); if (overuse > 0) { - nodes_to_nets_lookup[RRNodeId(inode)].insert(net_id); + nodes_to_nets_lookup[inode].insert(net_id); } } } @@ -188,9 +190,11 @@ static void generate_node_to_net_lookup(const Netlist<>& net_list, //Create overused nodes to congested nets look up by //traversing through the net trace backs linked lists for (ParentNetId net_id : net_list.nets()) { - for (t_trace* tptr = route_ctx.trace[net_id].head; tptr != nullptr; tptr = tptr->next) { - int inode = tptr->index; - rr_node_to_net_map[RRNodeId(inode)].insert(net_id); + if (!route_ctx.route_trees[net_id]) + continue; + + for (const RouteTreeNode& rt_node : route_ctx.route_trees[net_id].value().all_nodes()) { + rr_node_to_net_map[rt_node.inode].insert(net_id); } } } diff --git a/vpr/src/route/route_breadth_first.cpp b/vpr/src/route/route_breadth_first.cpp deleted file mode 100644 index 5e53ce2bd6d..00000000000 --- a/vpr/src/route/route_breadth_first.cpp +++ /dev/null @@ -1,465 +0,0 @@ -#include - -#include "vtr_log.h" - -#include "vpr_types.h" -#include "globals.h" -#include "route_export.h" -#include "route_common.h" -#include "route_breadth_first.h" - -//Print out extensive debug information about router operations -//#define ROUTER_DEBUG - -/********************* Subroutines local to this module *********************/ - -// Note: Breadth first router is still using only the BinaryHeap because it is -// deprecated, and not getting further development. - -static bool breadth_first_route_net(BinaryHeap& heap, ClusterNetId net_id, float bend_cost, float pres_fac); - -static void breadth_first_expand_trace_segment(BinaryHeap& heap, t_trace* start_ptr, int remaining_connections_to_sink, std::vector& modified_rr_node_inf); - -static void breadth_first_expand_neighbours(BinaryHeap& heap, int inode, float pcost, ClusterNetId net_id, float bend_cost, float pres_fac); - -static void breadth_first_add_to_heap(BinaryHeap& heap, const float path_cost, const float bend_cost, const int from_node, const RRNodeId to_node, const RREdgeId from_edge, float pres_fac); - -static float evaluate_node_cost(const float prev_path_cost, const float bend_cost, const int from_node, const int to_node, float pres_fac); - -static void breadth_first_add_source_to_heap(BinaryHeap& heap, ClusterNetId net_id, float pres_fac); - -/************************ Subroutine definitions ****************************/ - -bool try_breadth_first_route(const t_router_opts& router_opts) { - /* Iterated maze router ala Pathfinder Negotiated Congestion algorithm, * - * (FPGA 95 p. 111). Returns true if it can route this FPGA, false if * - * it can't. */ - - VTR_LOG( - "**********************************************************************\n" - "* !!! WARNING !!! *\n" - "* *\n" - "* Routing with the DEPRECATED 'Breadth-First' router, which *\n" - "* is inferrior and may be removed in a future release. *\n" - "* *\n" - "* Use the 'Timing-Driven' router instead, which requires much *\n" - "* less run-time (> 300x faster) and produces higher quality *\n" - "* results (less wirelength, lower delay), even when *\n" - "* architecture timing information is unavailable. *\n" - "* *\n" - "* !!! WARNING !!! *\n" - "**********************************************************************\n" - "\n"); - - float pres_fac; - bool success, is_routable, rip_up_local_opins; - int itry; - - auto& device_ctx = g_vpr_ctx.device(); - auto& cluster_ctx = g_vpr_ctx.clustering(); - auto& route_ctx = g_vpr_ctx.mutable_routing(); - - /* Usually the first iteration uses a very small (or 0) pres_fac to find * - * the shortest path and get a congestion map. For fast compiles, I set * - * pres_fac high even for the first iteration. */ - - pres_fac = update_pres_fac(router_opts.first_iter_pres_fac); - - if (router_opts.router_heap != e_heap_type::BINARY_HEAP) { - VTR_LOG_WARN("Breadth-first router only uses the BINARY_HEAP."); - } - - BinaryHeap heap; - heap.init_heap(device_ctx.grid); - - OveruseInfo overuse_info(device_ctx.rr_graph.num_nodes()); - - for (itry = 1; itry <= router_opts.max_router_iterations; itry++) { - VTR_LOG("Routing Iteration %d\n", itry); - - /* Reset "is_routed" and "is_fixed" flags to indicate nets not pre-routed (yet) */ - for (auto net_id : cluster_ctx.clb_nlist.nets()) { - route_ctx.net_status.set_is_routed((const ParentNetId&)net_id, false); - route_ctx.net_status.set_is_fixed((const ParentNetId&)net_id, false); - } - - for (auto net_id : cluster_ctx.clb_nlist.nets()) { - is_routable = try_breadth_first_route_net(heap, net_id, pres_fac, router_opts); - if (!is_routable) { - return (false); - } - } - - /* Make sure any CLB OPINs used up by subblocks being hooked directly * - * to them are reserved for that purpose. */ - - if (itry == 1) - rip_up_local_opins = false; - else - rip_up_local_opins = true; - - reserve_locally_used_opins(&heap, pres_fac, router_opts.acc_fac, rip_up_local_opins, false); - - success = feasible_routing(); - if (success) { - VTR_LOG("Successfully routed after %d routing iterations.\n", itry); - return (true); - } - - if (itry == 1) - pres_fac = update_pres_fac(router_opts.initial_pres_fac); - else - pres_fac *= router_opts.pres_fac_mult; - - pres_fac = update_pres_fac(std::min(pres_fac, static_cast(HUGE_POSITIVE_FLOAT / 1e5))); - - pathfinder_update_acc_cost_and_overuse_info(router_opts.acc_fac, overuse_info); - } - - VTR_LOG("Routing failed.\n"); - -#ifdef ROUTER_DEBUG - print_invalid_routing_info((const Netlist<>&)g_vpr_ctx.clustering().clb_nlist); -#endif - - return (false); -} - -bool try_breadth_first_route_net(BinaryHeap& heap, ClusterNetId net_id, float pres_fac, const t_router_opts& router_opts) { - bool is_routed = false; - - auto& cluster_ctx = g_vpr_ctx.clustering(); - auto& route_ctx = g_vpr_ctx.mutable_routing(); - - if (route_ctx.net_status.is_fixed((const ParentNetId&)net_id)) { /* Skip pre-routed nets. */ - is_routed = true; - - } else if (cluster_ctx.clb_nlist.net_is_ignored(net_id)) { /* Skip ignored nets. */ - is_routed = true; - - } else { - pathfinder_update_path_occupancy(route_ctx.trace[(const ParentNetId&)net_id].head, -1); - is_routed = breadth_first_route_net(heap, net_id, router_opts.bend_cost, pres_fac); - - /* Impossible to route? (disconnected rr_graph) */ - if (is_routed) { - route_ctx.net_status.set_is_routed((const ParentNetId&)net_id, false); - } else { - VTR_LOG("Routing failed.\n"); - } - - pathfinder_update_path_occupancy(route_ctx.trace[(const ParentNetId&)net_id].head, 1); - } - return (is_routed); -} - -static bool breadth_first_route_net(BinaryHeap& heap, ClusterNetId net_id, float bend_cost, float pres_fac) { - /* Uses a maze routing (Dijkstra's) algorithm to route a net. The net * - * begins at the net output, and expands outward until it hits a target * - * pin. The algorithm is then restarted with the entire first wire segment * - * included as part of the source this time. For an n-pin net, the maze * - * router is invoked n-1 times to complete all the connections. net_id is * - * the index of the net to be routed. Bends are penalized by bend_cost * - * (which is typically zero for detailed routing and nonzero only for global * - * routing), since global routes with lots of bends are tougher to detailed * - * route (using a detailed router like SEGA). * - * If this routine finds that a net *cannot* be connected (due to a complete * - * lack of potential paths, rather than congestion), it returns false, as * - * routing is impossible on this architecture. Otherwise it returns true. */ - - int inode, remaining_connections_to_sink; - float pcost, new_pcost; - t_heap* current; - t_trace* tptr; - - auto& cluster_ctx = g_vpr_ctx.clustering(); - auto& route_ctx = g_vpr_ctx.mutable_routing(); - -#ifdef ROUTER_DEBUG - VTR_LOG("Routing Net %zu (%zu sinks)\n", size_t(net_id), cluster_ctx.clb_nlist.net_sinks(net_id).size()); -#endif - - free_traceback((const ParentNetId&)net_id); - - breadth_first_add_source_to_heap(heap, net_id, pres_fac); - mark_ends((const Netlist<>&)cluster_ctx.clb_nlist, (const ParentNetId&)net_id); - - tptr = nullptr; - remaining_connections_to_sink = 0; - - auto src_pin_id = cluster_ctx.clb_nlist.net_driver(net_id); - - std::vector modified_rr_node_inf; //RR node indicies with modified rr_node_route_inf - - for (auto pin_id : cluster_ctx.clb_nlist.net_sinks(net_id)) { /* Need n-1 wires to connect n pins */ - - breadth_first_expand_trace_segment(heap, tptr, remaining_connections_to_sink, modified_rr_node_inf); - current = heap.get_heap_head(); - - if (current == nullptr) { /* Infeasible routing. No possible path for net. */ - VTR_LOG("Cannot route net #%zu (%s) from (%s) to sink pin (%s) -- no possible path.\n", - size_t(net_id), cluster_ctx.clb_nlist.net_name(net_id).c_str(), - cluster_ctx.clb_nlist.pin_name(src_pin_id).c_str(), - cluster_ctx.clb_nlist.pin_name(pin_id).c_str()); - reset_path_costs(modified_rr_node_inf); /* Clean up before leaving. */ - return (false); - } - - inode = current->index; - -#ifdef ROUTER_DEBUG - VTR_LOG(" Popped node %d\n", inode); -#endif - - while (route_ctx.rr_node_route_inf[inode].target_flag == 0) { - pcost = route_ctx.rr_node_route_inf[inode].path_cost; - new_pcost = current->cost; - if (pcost > new_pcost) { /* New path is lowest cost. */ -#ifdef ROUTER_DEBUG - VTR_LOG(" New best cost %g\n", new_pcost); -#endif - -#ifdef ROUTER_DEBUG - VTR_LOG(" Setting routing paths for associated node %d\n", current->index); -#endif - add_to_mod_list(current->index, modified_rr_node_inf); - - route_ctx.rr_node_route_inf[current->index].path_cost = new_pcost; - route_ctx.rr_node_route_inf[current->index].prev_node = current->prev_node(); - route_ctx.rr_node_route_inf[current->index].prev_edge = current->prev_edge(); - -#ifdef ROUTER_DEBUG - VTR_LOG(" Expanding node %d neighbours\n", inode); -#endif - breadth_first_expand_neighbours(heap, inode, new_pcost, net_id, bend_cost, pres_fac); - } - - heap.free(current); - current = heap.get_heap_head(); - - if (current == nullptr) { /* Impossible routing. No path for net. */ - VTR_LOG("Cannot route net #%zu (%s) from (%s) to sink pin (%s) -- no possible path.\n", - size_t(net_id), cluster_ctx.clb_nlist.net_name(net_id).c_str(), - cluster_ctx.clb_nlist.pin_name(src_pin_id).c_str(), cluster_ctx.clb_nlist.pin_name(pin_id).c_str()); - reset_path_costs(modified_rr_node_inf); - return (false); - } - - inode = current->index; - -#ifdef ROUTER_DEBUG - VTR_LOG(" Popped node %d\n", inode); -#endif - } -#ifdef ROUTER_DEBUG - VTR_LOG(" Found target node %d\n", inode); -#endif - if (current != nullptr) { - //Update link to target - add_to_mod_list(current->index, modified_rr_node_inf); - - route_ctx.rr_node_route_inf[current->index].path_cost = current->cost; - route_ctx.rr_node_route_inf[current->index].prev_node = current->prev_node(); - route_ctx.rr_node_route_inf[current->index].prev_edge = current->prev_edge(); - } - - route_ctx.rr_node_route_inf[inode].target_flag--; /* Connected to this SINK. */ - remaining_connections_to_sink = route_ctx.rr_node_route_inf[inode].target_flag; - size_t ipin = cluster_ctx.clb_nlist.pin_net_index(pin_id); - tptr = update_traceback(current, ipin, (const ParentNetId&)net_id); - heap.free(current); - } - -#ifdef ROUTER_DEBUG - VTR_LOG("Routed Net %zu\n", size_t(net_id)); -#endif - - heap.empty_heap(); - reset_path_costs(modified_rr_node_inf); - return (true); -} - -static void breadth_first_expand_trace_segment(BinaryHeap& heap, t_trace* start_ptr, int remaining_connections_to_sink, std::vector& modified_rr_node_inf) { - /* Adds all the rr_nodes in the traceback segment starting at tptr (and * - * continuing to the end of the traceback) to the heap with a cost of zero. * - * This allows expansion to begin from the existing wiring. The * - * remaining_connections_to_sink value is 0 if the route segment ending * - * at this location is the last one to connect to the SINK ending the route * - * segment. This is the usual case. If it is not the last connection this * - * net must make to this SINK, I have a hack to ensure the next connection * - * to this SINK goes through a different IPIN. Without this hack, the * - * router would always put all the connections from this net to this SINK * - * through the same IPIN. With LUTs or cluster-based logic blocks, you * - * should never have a net connecting to two logically-equivalent pins on * - * the same logic block, so the hack will never execute. If your logic * - * block is an and-gate, however, nets might connect to two and-inputs on * - * the same logic block, and since the and-inputs are logically-equivalent, * - * this means two connections to the same SINK. */ - - t_trace *tptr, *next_ptr; - int inode, sink_node, last_ipin_node; - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - auto& route_ctx = g_vpr_ctx.mutable_routing(); - - tptr = start_ptr; - if (tptr != nullptr && rr_graph.node_type(RRNodeId(tptr->index)) == SINK) { - /* During logical equivalence case, only use one opin */ - tptr = tptr->next; - } - - if (remaining_connections_to_sink == 0) { /* Usual case. */ - while (tptr != nullptr) { -#ifdef ROUTER_DEBUG - VTR_LOG(" Adding previous routing node %d to heap\n", tptr->index); -#endif - add_node_to_heap(&heap, route_ctx.rr_node_route_inf, - tptr->index, 0., - NO_PREVIOUS, RREdgeId::INVALID(), OPEN, OPEN); - tptr = tptr->next; - } - } else { /* This case never executes for most logic blocks. */ - - /* Weird case. Lots of hacks. The cleanest way to do this would be to empty * - * the heap, update the congestion due to the partially-completed route, put * - * the whole route so far (excluding IPINs and SINKs) on the heap with cost * - * 0., and expand till you hit the next SINK. That would be slow, so I * - * do some hacks to enable incremental wavefront expansion instead. */ - - if (tptr == nullptr) - return; /* No route yet */ - - next_ptr = tptr->next; - last_ipin_node = OPEN; /* Stops compiler from complaining. */ - - /* Can't put last SINK on heap with NO_PREVIOUS, etc, since that won't let * - * us reach it again. Instead, leave the last traceback element (SINK) off * - * the heap. */ - - while (next_ptr != nullptr) { - inode = tptr->index; -#ifdef ROUTER_DEBUG - VTR_LOG(" Adding previous routing node %d to heap*\n", tptr->index); -#endif - add_node_to_heap(&heap, route_ctx.rr_node_route_inf, - inode, 0., NO_PREVIOUS, RREdgeId::INVALID(), - OPEN, OPEN); - - if (rr_graph.node_type(RRNodeId(inode)) == IPIN) - last_ipin_node = inode; - - tptr = next_ptr; - next_ptr = tptr->next; - } - VTR_ASSERT(last_ipin_node >= 0); - - /* This will stop the IPIN node used to get to this SINK from being * - * reexpanded for the remainder of this net's routing. This will make us * - * hook up more IPINs to this SINK (which is what we want). If IPIN * - * doglegs are allowed in the graph, we won't be able to use this IPIN to * - * do a dogleg, since it won't be re-expanded. Shouldn't be a big problem. */ - - add_to_mod_list(last_ipin_node, modified_rr_node_inf); - route_ctx.rr_node_route_inf[last_ipin_node].path_cost = -HUGE_POSITIVE_FLOAT; - - /* Also need to mark the SINK as having high cost, so another connection can * - * be made to it. */ - - sink_node = tptr->index; - add_to_mod_list(sink_node, modified_rr_node_inf); - route_ctx.rr_node_route_inf[sink_node].path_cost = HUGE_POSITIVE_FLOAT; - - /* Finally, I need to remove any pending connections to this SINK via the * - * IPIN I just used (since they would result in congestion). Scan through * - * the heap to do this. */ - - heap.invalidate_heap_entries(sink_node, last_ipin_node); - } -} - -static void breadth_first_expand_neighbours(BinaryHeap& heap, int inode, float pcost, ClusterNetId net_id, float bend_cost, float pres_fac) { - /* Puts all the rr_nodes adjacent to inode on the heap. rr_nodes outside * - * the expanded bounding box specified in route_bb are not added to the * - * heap. pcost is the path_cost to get to inode. */ - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - auto& route_ctx = g_vpr_ctx.routing(); - - for (RREdgeId from_edge : rr_graph.edge_range(RRNodeId(inode))) { - RRNodeId to_node = device_ctx.rr_graph.rr_nodes().edge_sink_node(from_edge); - - vtr::Point lower_left(route_ctx.route_bb[(const ParentNetId&)net_id].xmin, - route_ctx.route_bb[(const ParentNetId&)net_id].ymin); - vtr::Point upper_right(route_ctx.route_bb[(const ParentNetId&)net_id].xmax, - route_ctx.route_bb[(const ParentNetId&)net_id].ymax); - vtr::Rect bounding_box(lower_left, upper_right); - - if (!rr_graph.node_is_inside_bounding_box(to_node, bounding_box)) continue; - - breadth_first_add_to_heap(heap, pcost, bend_cost, inode, to_node, from_edge, pres_fac); - } -} - -//Add to_node to the heap, and also add any nodes which are connected by non-configurable edges -static void breadth_first_add_to_heap(BinaryHeap& heap, const float path_cost, const float bend_cost, const int from_node, const RRNodeId to_node, const RREdgeId from_edge, float pres_fac) { -#ifdef ROUTER_DEBUG - VTR_LOG(" Expanding node %d\n", to_node); -#endif - - //Create a heap element to represent this node (and any non-configurably connected nodes) - t_heap* next = heap.alloc(); - next->index = size_t(to_node); - next->backward_path_cost = OPEN; - next->R_upstream = OPEN; - next->cost = std::numeric_limits::infinity(); - - //Path cost to 'to_node' - float new_path_cost = evaluate_node_cost(path_cost, bend_cost, from_node, size_t(to_node), pres_fac); - - next->cost = new_path_cost; - - //Record how we reached this node - next->index = size_t(to_node); - next->set_prev_edge(from_edge); - next->set_prev_node(from_node); - - heap.add_to_heap(next); -} - -static float evaluate_node_cost(const float prev_path_cost, const float bend_cost, const int from_node, const int to_node, float pres_fac) { - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - - float tot_cost = prev_path_cost + get_rr_cong_cost(to_node, pres_fac); - - if (bend_cost != 0.) { - t_rr_type from_type = rr_graph.node_type(RRNodeId(from_node)); - t_rr_type to_type = rr_graph.node_type(RRNodeId(to_node)); - if ((from_type == CHANX && to_type == CHANY) - || (from_type == CHANY && to_type == CHANX)) - tot_cost += bend_cost; - } - - return tot_cost; -} - -static void breadth_first_add_source_to_heap(BinaryHeap& heap, ClusterNetId net_id, float pres_fac) { - /* Adds the SOURCE of this net to the heap. Used to start a net's routing. */ - - int inode; - float cost; - - auto& route_ctx = g_vpr_ctx.routing(); - - inode = route_ctx.net_rr_terminals[(const ParentNetId&)net_id][0]; /* SOURCE */ - cost = get_rr_cong_cost(inode, pres_fac); - -#ifdef ROUTER_DEBUG - VTR_LOG(" Adding Source node %d to heap\n", inode); -#endif - - add_node_to_heap(&heap, route_ctx.rr_node_route_inf, inode, cost, - NO_PREVIOUS, RREdgeId::INVALID(), OPEN, OPEN); -} diff --git a/vpr/src/route/route_breadth_first.h b/vpr/src/route/route_breadth_first.h deleted file mode 100644 index fafbbf8f85f..00000000000 --- a/vpr/src/route/route_breadth_first.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef ROUTE_BREADTH_FIRST_H -#define ROUTE_BREADTH_FIRST_H - -#include "route_common.h" -#include "binary_heap.h" - -bool try_breadth_first_route(const t_router_opts& router_opts); -bool try_breadth_first_route_net(BinaryHeap& heap, ClusterNetId net_id, float pres_fac, const t_router_opts& router_opts); - -#endif diff --git a/vpr/src/route/route_common.cpp b/vpr/src/route/route_common.cpp index 22261a5f81a..fd2091fbc9b 100644 --- a/vpr/src/route/route_common.cpp +++ b/vpr/src/route/route_common.cpp @@ -5,6 +5,7 @@ #include #include +#include "route_tree.h" #include "vtr_assert.h" #include "vtr_util.h" #include "vtr_log.h" @@ -19,9 +20,7 @@ #include "globals.h" #include "route_export.h" #include "route_common.h" -#include "route_tree_timing.h" #include "route_timing.h" -#include "route_breadth_first.h" #include "place_and_route.h" #include "rr_graph.h" #include "rr_graph2.h" @@ -40,22 +39,6 @@ #include "bucket.h" #include "draw_global.h" -/**************** Types local to route_common.c ******************/ -struct t_trace_branch { - t_trace* head; - t_trace* tail; -}; - -/**************** Static variables local to route_common.c ******************/ - -/* For managing my own list of currently free trace data structures. */ -static t_trace* trace_free_head = nullptr; -/* For keeping track of the sudo malloc memory for the trace*/ -static vtr::t_chunk trace_ch; - -static int num_trace_allocated = 0; /* To watch for memory leaks. */ -static int num_linked_f_pointer_allocated = 0; - /* The numbering relation between the channels and clbs is: * * * * | IO | chan_ | CLB | chan_ | CLB | * @@ -86,10 +69,6 @@ static int num_linked_f_pointer_allocated = 0; * */ /******************** Subroutines local to route_common.c *******************/ -static t_trace_branch traceback_branch(int node, int target_net_pin_index, std::unordered_set& main_branch_visited); -static std::pair add_trace_non_configurable(t_trace* head, t_trace* tail, int node, std::unordered_set& visited); -static std::pair add_trace_non_configurable_recurr(int node, std::unordered_set& visited, int depth = 0); - static vtr::vector> load_net_rr_terminals(const RRGraphView& rr_graph, const Netlist<>& net_list, bool is_flat); @@ -108,9 +87,6 @@ static vtr::vector> load_rr_clb_sources(const RR static t_clb_opins_used alloc_and_load_clb_opins_used_locally(); static void adjust_one_rr_occ_and_acc_cost(int inode, int add_or_sub, float acc_fac); -bool validate_traceback_recurr(t_trace* trace, std::set& seen_rr_nodes); -static bool validate_trace_nodes(t_trace* head, const std::unordered_set& trace_nodes); - static vtr::vector load_is_clock_net(const Netlist<>& net_list, bool is_flat); @@ -118,70 +94,27 @@ static bool classes_in_same_block(ParentBlockId blk_id, int first_class_ptc_num, /************************** Subroutine definitions ***************************/ -void save_routing(const Netlist<>& net_list, - vtr::vector& best_routing, +void save_routing(vtr::vector>& best_routing, const t_clb_opins_used& clb_opins_used_locally, t_clb_opins_used& saved_clb_opins_used_locally) { - /* This routing frees any routing currently held in best routing, * - * then copies over the current routing (held in route_ctx.trace), and * - * finally sets route_ctx.trace_head and route_ctx.trace_tail to all NULLs so that the * - * connection to the saved routing is broken. This is necessary so * - * that the next iteration of the router does not free the saved * - * routing elements. Also saves any data about locally used clb_opins, * - * since this is also part of the routing. */ - - t_trace *tptr, *tempptr; - - auto& route_ctx = g_vpr_ctx.mutable_routing(); - - for (auto net_id : net_list.nets()) { - /* Free any previously saved routing. It is no longer best. */ - tptr = best_routing[net_id]; - while (tptr != nullptr) { - tempptr = tptr->next; - free_trace_data(tptr); - tptr = tempptr; - } - - /* Save a pointer to the current routing in best_routing. */ - best_routing[net_id] = route_ctx.trace[net_id].head; + auto& route_ctx = g_vpr_ctx.routing(); - /* Set the current (working) routing to NULL so the current trace * - * elements won't be reused by the memory allocator. */ + best_routing = route_ctx.route_trees; - route_ctx.trace[net_id].head = nullptr; - route_ctx.trace[net_id].tail = nullptr; - route_ctx.trace_nodes[net_id].clear(); - } - - /* Save which OPINs are locally used. */ + /* Save which OPINs are locally used. */ saved_clb_opins_used_locally = clb_opins_used_locally; } -/* Deallocates any current routing in route_ctx.trace_head, and replaces it with * - * the routing in best_routing. Best_routing is set to NULL to show that * - * it no longer points to a valid routing. NOTE: route_ctx.trace_tail is not * - * restored -- it is set to all NULLs since it is only used in * - * update_traceback. If you need route_ctx.trace_tail restored, modify this * - * routine. Also restores the locally used opin data. */ -void restore_routing(const Netlist<>& net_list, - vtr::vector& best_routing, +/* Empties route_ctx.current_rt and copies over best_routing onto it. + * Also restores the locally used opin data. */ +void restore_routing(vtr::vector>& best_routing, t_clb_opins_used& clb_opins_used_locally, const t_clb_opins_used& saved_clb_opins_used_locally) { auto& route_ctx = g_vpr_ctx.mutable_routing(); - for (auto net_id : net_list.nets()) { - /* Free any current routing. */ - pathfinder_update_path_occupancy(route_ctx.trace[net_id].head, -1); - free_traceback(net_id); - - /* Set the current routing to the saved one. */ - route_ctx.trace[net_id].head = best_routing[net_id]; - pathfinder_update_path_occupancy(route_ctx.trace[net_id].head, 1); - best_routing[net_id] = nullptr; /* No stored routing. */ - } + route_ctx.route_trees = best_routing; - /* Restore which OPINs are locally used. */ + /* Restore which OPINs are locally used. */ clb_opins_used_locally = saved_clb_opins_used_locally; } @@ -189,8 +122,7 @@ void restore_routing(const Netlist<>& net_list, * Use this number as a routing serial number to ensure that programming * * changes do not break the router. */ void get_serial_num(const Netlist<>& net_list) { - int serial_num, inode; - t_trace* tptr; + int serial_num; auto& route_ctx = g_vpr_ctx.routing(); auto& device_ctx = g_vpr_ctx.device(); @@ -199,20 +131,18 @@ void get_serial_num(const Netlist<>& net_list) { serial_num = 0; for (auto net_id : net_list.nets()) { - /* Global nets will have null trace_heads (never routed) so they * - * are not included in the serial number calculation. */ + if (!route_ctx.route_trees[net_id]) + continue; - tptr = route_ctx.trace[net_id].head; - while (tptr != nullptr) { - inode = tptr->index; + for (auto& rt_node : route_ctx.route_trees[net_id].value().all_nodes()) { + RRNodeId inode = rt_node.inode; serial_num += (size_t(net_id) + 1) - * (rr_graph.node_xlow(RRNodeId(inode)) * (device_ctx.grid.width()) - rr_graph.node_yhigh(RRNodeId(inode))); + * (rr_graph.node_xlow(inode) * (device_ctx.grid.width()) - rr_graph.node_yhigh(inode)); - serial_num -= rr_graph.node_ptc_num(RRNodeId(inode)) * (size_t(net_id) + 1) * 10; + serial_num -= rr_graph.node_ptc_num(inode) * (size_t(net_id) + 1) * 10; - serial_num -= rr_graph.node_type(RRNodeId(inode)) * (size_t(net_id) + 1) * 100; + serial_num -= rr_graph.node_type(inode) * (size_t(net_id) + 1) * 100; serial_num %= 2000000000; /* Prevent overflow */ - tptr = tptr->next; } } VTR_LOG("Serial number (magic cookie) for the routing is: %d\n", serial_num); @@ -328,8 +258,9 @@ bool try_route(const Netlist<>& net_list, } if (router_opts.router_algorithm == BREADTH_FIRST) { - VTR_LOG("Confirming router algorithm: BREADTH_FIRST.\n"); - success = try_breadth_first_route(router_opts); + VTR_LOG("Confirming router algorithm: BREADTH_FIRST (deleted, doesn't do anything).\n"); + //success = try_breadth_first_route(router_opts); + success = 0; } else { /* TIMING_DRIVEN route */ VTR_LOG("Confirming router algorithm: TIMING_DRIVEN.\n"); auto& atom_ctx = g_vpr_ctx.atom(); @@ -400,48 +331,15 @@ std::vector> collect_rr_node_nets() { std::vector> rr_node_nets(device_ctx.rr_graph.num_nodes()); for (ClusterNetId inet : cluster_ctx.clb_nlist.nets()) { - // TODO: Temporarily - This needs to be fixed properly - t_trace* trace_elem = route_ctx.trace[(const ParentNetId&)inet].head; - while (trace_elem) { - int rr_node = trace_elem->index; - - rr_node_nets[rr_node].insert(inet); - - trace_elem = trace_elem->next; + if (!route_ctx.route_trees[inet]) + continue; + for (auto& rt_node : route_ctx.route_trees[inet].value().all_nodes()) { + rr_node_nets[size_t(rt_node.inode)].insert(inet); } } return rr_node_nets; } -void pathfinder_update_path_occupancy(t_trace* route_segment_start, int add_or_sub) { - /* This routine updates the occupancy of the rr_nodes that are affected by - * the portion of the routing of one net that starts at route_segment_start. - * If route_segment_start is route_ctx.trace[net_id].head, the - * occupancy of all the nodes in the routing of net net_id are updated. - * If add_or_sub is -1 the net (or net portion) is ripped up, - * if it is 1, the net is added to the routing. - */ - - t_trace* tptr; - - tptr = route_segment_start; - if (tptr == nullptr) /* No routing yet. */ - return; - - for (;;) { - pathfinder_update_single_node_occupancy(tptr->index, add_or_sub); - - if (tptr->iswitch == OPEN) { //End of branch - tptr = tptr->next; /* Skip next segment. */ - if (tptr == nullptr) - break; - } - - tptr = tptr->next; - - } /* End while loop -- did an entire traceback. */ -} - void pathfinder_update_single_node_occupancy(int inode, int add_or_sub) { /* Updates pathfinder's occupancy by either adding or removing the * usage of a resource node. */ @@ -487,6 +385,14 @@ void pathfinder_update_acc_cost_and_overuse_info(float acc_fac, OveruseInfo& ove overuse_info.worst_overuse = worst_overuse; } +/** Update pathfinder cost of all nodes rooted at rt_node, including rt_node itself */ +void pathfinder_update_cost_from_route_tree(const RouteTreeNode& root, int add_or_sub) { + pathfinder_update_single_node_occupancy(size_t(root.inode), add_or_sub); + for (auto& node : root.all_nodes()) { + pathfinder_update_single_node_occupancy(size_t(node.inode), add_or_sub); + } +} + float update_pres_fac(float new_pres_fac) { /* This routine should take the new value of the present congestion factor * * and propagate it to all the relevant data fields in the vpr flow. * @@ -501,8 +407,8 @@ float update_pres_fac(float new_pres_fac) { return new_pres_fac; } -/* Call this before you route any nets. It frees any old traceback and * - * sets the list of rr_nodes touched to empty. */ +/* Call this before you route any nets. It frees any old route trees and + * sets the list of rr_nodes touched to empty. */ void init_route_structs(const Netlist<>& net_list, int bb_factor, bool has_choking_point, @@ -510,13 +416,9 @@ void init_route_structs(const Netlist<>& net_list, auto& device_ctx = g_vpr_ctx.device(); auto& route_ctx = g_vpr_ctx.mutable_routing(); - //Free any old tracebacks - for (auto net_id : net_list.nets()) - free_traceback(net_id); - - //Allocate new tracebacks - route_ctx.trace.resize(net_list.nets().size()); - route_ctx.trace_nodes.resize(net_list.nets().size()); + // Allocate and clear a new route_trees + route_ctx.route_trees.resize(net_list.nets().size()); + std::fill(route_ctx.route_trees.begin(), route_ctx.route_trees.end(), vtr::nullopt); //Various look-ups route_ctx.net_rr_terminals = load_net_rr_terminals(device_ctx.rr_graph, @@ -540,208 +442,6 @@ void init_route_structs(const Netlist<>& net_list, } } -/* This routine adds the most recently finished wire segment to the * - * traceback linked list. The first connection starts with the net SOURCE * - * and begins at the structure pointed to by route_ctx.trace[net_id].head. * - * Each connection ends with a SINK. After each SINK, the next connection * - * begins (if the net has more than 2 pins). The first element after the * - * SINK gives the routing node on a previous piece of the routing, which is * - * the link from the existing net to this new piece of the net. * - * In each traceback I start at the end of a path, which is a SINK with * - * target_net_pin_index (net pin index corresponding to the SINK, ranging * - * from 1 to fanout), and trace back through its predecessors to the * - * beginning. I have stored information on the predecesser of each node to * - * make traceback easy -- this sacrificies some memory for easier code * - * maintenance. This routine returns a pointer to the first "new" node in * - * the traceback (node not previously in trace). */ -t_trace* update_traceback(t_heap* hptr, - int target_net_pin_index, - ParentNetId net_id) { - auto& route_ctx = g_vpr_ctx.mutable_routing(); - - auto& trace_nodes = route_ctx.trace_nodes[net_id]; - - VTR_ASSERT_SAFE(validate_trace_nodes(route_ctx.trace[net_id].head, trace_nodes)); - - t_trace_branch branch = traceback_branch(hptr->index, target_net_pin_index, trace_nodes); - - VTR_ASSERT_SAFE(validate_trace_nodes(branch.head, trace_nodes)); - - t_trace* ret_ptr = nullptr; - if (route_ctx.trace[net_id].tail != nullptr) { - route_ctx.trace[net_id].tail->next = branch.head; /* Traceback ends with tptr */ - ret_ptr = branch.head->next; /* First new segment. */ - } else { /* This was the first "chunk" of the net's routing */ - route_ctx.trace[net_id].head = branch.head; - ret_ptr = branch.head; /* Whole traceback is new. */ - } - - route_ctx.trace[net_id].tail = branch.tail; - return (ret_ptr); -} - -//Traces back a new routing branch starting from the specified SINK 'node' with target_net_pin_index, which is the -//net pin index corresponding to the SINK (ranging from 1 to fanout), and working backwards to any existing routing. -//Returns the new branch, and also updates trace_nodes for any new nodes which are included in the branches traceback. -static t_trace_branch traceback_branch(int node, int target_net_pin_index, std::unordered_set& trace_nodes) { - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - auto& route_ctx = g_vpr_ctx.routing(); - - auto rr_type = rr_graph.node_type(RRNodeId(node)); - if (rr_type != SINK) { - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, - "in traceback_branch: Expected type = SINK (%d).\n"); - } - - //We construct the main traceback by walking from the given node back to the source, - //according to the previous edges/nodes recorded in rr_node_route_inf by the router. - t_trace* branch_head = alloc_trace_data(); - t_trace* branch_tail = branch_head; - branch_head->index = node; - branch_head->net_pin_index = target_net_pin_index; //The first node is the SINK node, so store its net pin index - branch_head->iswitch = OPEN; - branch_head->next = nullptr; - - trace_nodes.insert(node); - - std::vector new_nodes_added_to_traceback = {node}; - - auto iedge = route_ctx.rr_node_route_inf[node].prev_edge; - int inode = route_ctx.rr_node_route_inf[node].prev_node; - - while (inode != NO_PREVIOUS) { - //Add the current node to the head of traceback - t_trace* prev_ptr = alloc_trace_data(); - prev_ptr->index = inode; - prev_ptr->net_pin_index = OPEN; //Net pin index is invalid for Non-SINK nodes - prev_ptr->iswitch = rr_graph.rr_nodes().edge_switch(iedge); - - prev_ptr->next = branch_head; - branch_head = prev_ptr; - - if (trace_nodes.count(inode)) { - break; //Connected to existing routing - } - - trace_nodes.insert(inode); //Record this node as visited - new_nodes_added_to_traceback.push_back(inode); - - iedge = route_ctx.rr_node_route_inf[inode].prev_edge; - inode = route_ctx.rr_node_route_inf[inode].prev_node; - } - - //We next re-expand all the main-branch nodes to add any non-configurably connected side branches - // We are careful to do this *after* the main branch is constructed to ensure nodes which are both - // non-configurably connected *and* part of the main branch are only added to the traceback once. - for (int new_node : new_nodes_added_to_traceback) { - //Expand each main branch node - std::tie(branch_head, branch_tail) = add_trace_non_configurable(branch_head, branch_tail, new_node, trace_nodes); - } - - return {branch_head, branch_tail}; -} - -//Traces any non-configurable subtrees from branch_head, returning the new branch_head and updating trace_nodes -// -//This effectively does a depth-first traversal -static std::pair add_trace_non_configurable(t_trace* head, t_trace* tail, int node, std::unordered_set& trace_nodes) { - //Trace any non-configurable subtrees - t_trace* subtree_head = nullptr; - t_trace* subtree_tail = nullptr; - std::tie(subtree_head, subtree_tail) = add_trace_non_configurable_recurr(node, trace_nodes); - - //Add any non-empty subtree to tail of traceback - if (subtree_head && subtree_tail) { - if (!head) { //First subtree becomes head - head = subtree_head; - } else { //Later subtrees added to tail - VTR_ASSERT(tail); - tail->next = subtree_head; - } - - tail = subtree_tail; - } else { - VTR_ASSERT(subtree_head == nullptr && subtree_tail == nullptr); - } - - return {head, tail}; -} - -//Recursive helper function for add_trace_non_configurable() -static std::pair add_trace_non_configurable_recurr(int node, std::unordered_set& trace_nodes, int depth) { - t_trace* head = nullptr; - t_trace* tail = nullptr; - - //Record the non-configurable out-going edges - std::vector unvisited_non_configurable_edges; - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - for (auto iedge : rr_graph.non_configurable_edges(RRNodeId(node))) { - VTR_ASSERT_SAFE(!rr_graph.edge_is_configurable(RRNodeId(node), iedge)); - - int to_node = size_t(rr_graph.edge_sink_node(RRNodeId(node), iedge)); - - if (!trace_nodes.count(to_node)) { - unvisited_non_configurable_edges.push_back(iedge); - } - } - - if (unvisited_non_configurable_edges.size() == 0) { - //Base case: leaf node with no non-configurable edges - if (depth > 0) { //Arrived via non-configurable edge - VTR_ASSERT(!trace_nodes.count(node)); - head = alloc_trace_data(); - head->index = node; - head->iswitch = -1; - head->next = nullptr; - tail = head; - - trace_nodes.insert(node); - } - - } else { - //Recursive case: intermediate node with non-configurable edges - for (auto iedge : unvisited_non_configurable_edges) { - int to_node = size_t(rr_graph.edge_sink_node(RRNodeId(node), iedge)); - int iswitch = rr_graph.edge_switch(RRNodeId(node), iedge); - - VTR_ASSERT(!trace_nodes.count(to_node)); - trace_nodes.insert(node); - - //Recurse - t_trace* subtree_head = nullptr; - t_trace* subtree_tail = nullptr; - std::tie(subtree_head, subtree_tail) = add_trace_non_configurable_recurr(to_node, trace_nodes, depth + 1); - - if (subtree_head && subtree_tail) { - //Add the non-empty sub-tree - - //Duplicate the original head as the new tail (for the new branch) - t_trace* intermediate_head = alloc_trace_data(); - intermediate_head->index = node; - intermediate_head->iswitch = iswitch; - intermediate_head->next = nullptr; - - intermediate_head->next = subtree_head; - - if (!head) { //First subtree becomes head - head = intermediate_head; - } else { //Later subtrees added to tail - VTR_ASSERT(tail); - tail->next = intermediate_head; - } - - tail = subtree_tail; - } else { - VTR_ASSERT(subtree_head == nullptr && subtree_tail == nullptr); - } - } - } - - return {head, tail}; -} - /* The routine sets the path_cost to HUGE_POSITIVE_FLOAT for * * all channel segments touched by previous routing phases. */ void reset_path_costs(const std::vector& visited_rr_nodes) { @@ -808,69 +508,6 @@ void mark_remaining_ends(ParentNetId net_id, const std::vector& remaining_s } } -void drop_traceback_tail(ParentNetId net_id) { - /* Removes the tail node from the routing traceback and updates - * it with the previous node from the traceback. - * This funtion is primarily called to remove the virtual clock - * sink from the routing traceback and replace it with the clock - * network root. */ - auto& route_ctx = g_vpr_ctx.mutable_routing(); - - auto* tail_ptr = route_ctx.trace[net_id].tail; - auto node = tail_ptr->index; - route_ctx.trace_nodes[net_id].erase(node); - auto* trace_ptr = route_ctx.trace[net_id].head; - while (trace_ptr != nullptr) { - t_trace* next_ptr = trace_ptr->next; - if (next_ptr == tail_ptr) { - trace_ptr->iswitch = tail_ptr->iswitch; - trace_ptr->next = nullptr; - route_ctx.trace[net_id].tail = trace_ptr; - break; - } - trace_ptr = next_ptr; - } - free_trace_data(tail_ptr); -} - -void free_traceback(ParentNetId net_id) { - /* Puts the entire traceback (old routing) for this net on the free list * - * and sets the route_ctx.trace_head pointers etc. for the net to NULL. */ - - auto& route_ctx = g_vpr_ctx.mutable_routing(); - - if (route_ctx.trace.empty()) { - return; - } - - if (route_ctx.trace[net_id].head == nullptr) { - return; - } - - free_traceback(route_ctx.trace[net_id].head); - - route_ctx.trace[net_id].head = nullptr; - route_ctx.trace[net_id].tail = nullptr; - route_ctx.trace_nodes[net_id].clear(); -} - -void free_traceback(t_trace* tptr) { - while (tptr != nullptr) { - t_trace* tempptr = tptr->next; - free_trace_data(tptr); - tptr = tempptr; - } -} - -/* Allocates data structures into which the key routing data can be saved, * - * allowing the routing to be recovered later (e.g. after a another routing * - * is attempted). */ -vtr::vector alloc_saved_routing(const Netlist<>& net_list) { - vtr::vector best_routing(net_list.nets().size()); - - return (best_routing); -} - //Calculates how many (and allocates space for) OPINs which must be reserved to //respect 'instance' equivalence. // @@ -938,25 +575,6 @@ static t_clb_opins_used alloc_and_load_clb_opins_used_locally() { /*the trace lists are only freed after use by the timing-driven placer */ /*Do not free them after use by the router, since stats, and draw */ /*routines use the trace values */ -void free_trace_structs(const Netlist<>& net_list) { - auto& route_ctx = g_vpr_ctx.mutable_routing(); - - if (route_ctx.trace.empty()) { - return; - } - - for (auto net_id : net_list.nets()) { - free_traceback(net_id); - - if (route_ctx.trace[net_id].head) { - free(route_ctx.trace[net_id].head); - free(route_ctx.trace[net_id].tail); - } - route_ctx.trace[net_id].head = nullptr; - route_ctx.trace[net_id].tail = nullptr; - } -} - void free_route_structs() { /* Frees the temporary storage needed only during the routing. The * * final routing result is not freed. */ @@ -968,17 +586,6 @@ void free_route_structs() { } } -/* Frees the data structures needed to save a routing. */ -void free_saved_routing(const Netlist<>& net_list, - vtr::vector& best_routing) { - for (auto net_id : net_list.nets()) { - if (best_routing[net_id] != nullptr) { - free(best_routing[net_id]); - best_routing[net_id] = nullptr; - } - } -} - void alloc_and_load_rr_node_route_structs() { /* Allocates some extra information about each rr_node that is used only * * during routing. */ @@ -1311,187 +918,6 @@ void add_to_mod_list(int inode, std::vector& modified_rr_node_inf) { } } -t_trace* -alloc_trace_data() { - t_trace* temp_ptr; - - if (trace_free_head == nullptr) { /* No elements on the free list */ - trace_free_head = (t_trace*)vtr::chunk_malloc(sizeof(t_trace), &trace_ch); - trace_free_head->next = nullptr; - } - temp_ptr = trace_free_head; - temp_ptr->net_pin_index = OPEN; //default - trace_free_head = trace_free_head->next; - num_trace_allocated++; - return (temp_ptr); -} - -void free_trace_data(t_trace* tptr) { - /* Puts the traceback structure pointed to by tptr on the free list. */ - - tptr->next = trace_free_head; - trace_free_head = tptr; - num_trace_allocated--; -} - -void print_route(const Netlist<>& net_list, - FILE* fp, - const vtr::vector& tracebacks, - bool is_flat) { - if (tracebacks.empty()) return; //Only if routing exists - - auto& place_ctx = g_vpr_ctx.placement(); - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - auto& route_ctx = g_vpr_ctx.mutable_routing(); - - for (auto net_id : net_list.nets()) { - if (!net_list.net_is_ignored(net_id)) { - fprintf(fp, "\n\nNet %zu (%s)\n\n", size_t(net_id), net_list.net_name(net_id).c_str()); - if (net_list.net_sinks(net_id).size() == false) { - fprintf(fp, "\n\nUsed in local cluster only, reserved one CLB pin\n\n"); - } else { - t_trace* tptr = route_ctx.trace[net_id].head; - - while (tptr != nullptr) { - int inode = tptr->index; - auto rr_node = RRNodeId(inode); - t_rr_type rr_type = rr_graph.node_type(rr_node); - int ilow = rr_graph.node_xlow(rr_node); - int jlow = rr_graph.node_ylow(rr_node); - - fprintf(fp, "Node:\t%d\t%6s (%d,%d) ", inode, - rr_graph.node_type_string(rr_node), ilow, jlow); - - if ((ilow != rr_graph.node_xhigh(rr_node)) - || (jlow != rr_graph.node_yhigh(rr_node))) - fprintf(fp, "to (%d,%d) ", rr_graph.node_xhigh(rr_node), - rr_graph.node_yhigh(rr_node)); - - switch (rr_type) { - case IPIN: - case OPIN: - if (is_io_type(device_ctx.grid.get_physical_type(ilow, jlow))) { - fprintf(fp, " Pad: "); - } else { /* IO Pad. */ - fprintf(fp, " Pin: "); - } - break; - - case CHANX: - case CHANY: - fprintf(fp, " Track: "); - break; - - case SOURCE: - case SINK: - if (is_io_type(device_ctx.grid.get_physical_type(ilow, jlow))) { - fprintf(fp, " Pad: "); - } else { /* IO Pad. */ - fprintf(fp, " Class: "); - } - break; - - default: - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, - "in print_route: Unexpected traceback element type: %d (%s).\n", - rr_type, rr_graph.node_type_string(rr_node)); - break; - } - - fprintf(fp, "%d ", rr_graph.node_ptc_num(rr_node)); - - auto physical_tile = device_ctx.grid.get_physical_type(ilow, jlow); - if (!is_io_type(physical_tile) && (rr_type == IPIN || rr_type == OPIN)) { - int pin_num = rr_graph.node_pin_num(rr_node); - int xoffset = device_ctx.grid.get_width_offset(ilow, jlow); - int yoffset = device_ctx.grid.get_height_offset(ilow, jlow); - const t_sub_tile* sub_tile; - int sub_tile_rel_cap; - std::tie(sub_tile, sub_tile_rel_cap) = get_sub_tile_from_pin_physical_num(physical_tile, pin_num); - int sub_tile_offset = sub_tile->capacity.low + sub_tile_rel_cap; - - ClusterBlockId iblock = place_ctx.grid_blocks[ilow - xoffset][jlow - yoffset].blocks[sub_tile_offset]; - VTR_ASSERT(iblock); - const t_pb_graph_pin* pb_pin; - if (is_pin_on_tile(physical_tile, pin_num)) { - pb_pin = get_pb_graph_node_pin_from_block_pin(iblock, pin_num); - } else { - pb_pin = get_pb_pin_from_pin_physical_num(physical_tile, pin_num); - } - const t_pb_type* pb_type = pb_pin->parent_node->pb_type; - fprintf(fp, " %s.%s[%d] ", pb_type->name, pb_pin->port->name, pb_pin->pin_number); - } - - /* Uncomment line below if you're debugging and want to see the switch types * - * used in the routing. */ - fprintf(fp, "Switch: %d", tptr->iswitch); - - //Save net pin index for sinks - if (rr_type == SINK) { - fprintf(fp, " Net_pin_index: %d", tptr->net_pin_index); - } - - fprintf(fp, "\n"); - - tptr = tptr->next; - } - } - } else { /* Global net. Never routed. */ - fprintf(fp, "\n\nNet %zu (%s): global net connecting:\n\n", size_t(net_id), - net_list.net_name(net_id).c_str()); - - for (auto pin_id : net_list.net_pins(net_id)) { - ParentBlockId block_id = net_list.pin_block(pin_id); - int iclass = get_block_pin_class_num(block_id, pin_id, is_flat); - t_block_loc blk_loc; - blk_loc = get_block_loc(block_id, is_flat); - fprintf(fp, "Block %s (#%zu) at (%d,%d), Pin class %d.\n", - net_list.block_name(block_id).c_str(), - size_t(block_id), - blk_loc.loc.x, - blk_loc.loc.y, - iclass); - } - } - } -} - -/* Prints out the routing to file route_file. */ -void print_route(const Netlist<>& net_list, - const char* placement_file, - const char* route_file, - bool is_flat) { - FILE* fp; - - fp = fopen(route_file, "w"); - - auto& place_ctx = g_vpr_ctx.placement(); - auto& device_ctx = g_vpr_ctx.device(); - auto& route_ctx = g_vpr_ctx.mutable_routing(); - - fprintf(fp, "Placement_File: %s Placement_ID: %s\n", placement_file, place_ctx.placement_id.c_str()); - - fprintf(fp, "Array size: %zu x %zu logic blocks.\n", device_ctx.grid.width(), device_ctx.grid.height()); - fprintf(fp, "\nRouting:"); - - print_route(net_list, fp, route_ctx.trace, is_flat); - - fclose(fp); - - if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_MEM)) { - fp = vtr::fopen(getEchoFileName(E_ECHO_MEM), "w"); - fprintf(fp, "\nNum_trace_allocated: %d\n", - num_trace_allocated); - fprintf(fp, "Num_linked_f_pointer_allocated: %d\n", - num_linked_f_pointer_allocated); - fclose(fp); - } - - //Save the digest of the route file - route_ctx.routing_id = vtr::secure_digest_file(route_file); -} - //To ensure the router can only swap pins which are actually logically equivalent, some block output pins must be //reserved in certain cases. // @@ -1610,124 +1036,6 @@ static void adjust_one_rr_occ_and_acc_cost(int inode, int add_or_sub, float acc_ } } -void free_chunk_memory_trace() { - if (trace_ch.chunk_ptr_head != nullptr) { - free_chunk_memory(&trace_ch); - trace_ch.chunk_ptr_head = nullptr; - trace_free_head = nullptr; - } -} - -// connection based overhaul (more specificity than nets) -// utility and debugging functions ----------------------- -void print_traceback(ParentNetId net_id) { - // linearly print linked list - auto& route_ctx = g_vpr_ctx.routing(); - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - - VTR_LOG("traceback %zu: ", size_t(net_id)); - t_trace* head = route_ctx.trace[net_id].head; - while (head) { - int inode{head->index}; - if (rr_graph.node_type(RRNodeId(inode)) == SINK) - VTR_LOG("%d(sink)(%d)->", inode, route_ctx.rr_node_route_inf[inode].occ()); - else - VTR_LOG("%d(%d)->", inode, route_ctx.rr_node_route_inf[inode].occ()); - head = head->next; - } - VTR_LOG("\n"); -} - -void print_traceback(const t_trace* trace) { - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - auto& route_ctx = g_vpr_ctx.routing(); - const t_trace* prev = nullptr; - while (trace) { - int inode = trace->index; - VTR_LOG("%d (%s)", inode, rr_node_typename[rr_graph.node_type(RRNodeId(inode))]); - - if (trace->iswitch == OPEN) { - VTR_LOG(" !"); //End of branch - } - - if (prev && prev->iswitch != OPEN && !rr_graph.rr_switch_inf(RRSwitchId(prev->iswitch)).configurable()) { - VTR_LOG("*"); //Reached non-configurably - } - - if (route_ctx.rr_node_route_inf[inode].occ() > rr_graph.node_capacity(RRNodeId(inode))) { - VTR_LOG(" x"); //Overused - } - VTR_LOG("\n"); - prev = trace; - trace = trace->next; - } - VTR_LOG("\n"); -} - -bool validate_traceback(t_trace* trace) { - std::set seen_rr_nodes; - - return validate_traceback_recurr(trace, seen_rr_nodes); -} - -bool validate_traceback_recurr(t_trace* trace, std::set& seen_rr_nodes) { - if (!trace) { - return true; - } - - seen_rr_nodes.insert(trace->index); - - t_trace* next = trace->next; - - if (next) { - if (trace->iswitch == OPEN) { //End of a branch - - //Verify that the next element (branch point) has been already seen in the traceback so far - if (!seen_rr_nodes.count(next->index)) { - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Traceback branch point %d not found", next->index); - } else { - //Recurse along the new branch - return validate_traceback_recurr(next, seen_rr_nodes); - } - } else { //Midway along branch - - //Check there is an edge connecting trace and next - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - bool found = false; - for (t_edge_size iedge = 0; iedge < rr_graph.num_edges(RRNodeId(trace->index)); ++iedge) { - int to_node = size_t(rr_graph.edge_sink_node(RRNodeId(trace->index), iedge)); - - if (to_node == next->index) { - found = true; - - //Verify that the switch matches - int rr_iswitch = rr_graph.edge_switch(RRNodeId(trace->index), iedge); - if (trace->iswitch != rr_iswitch) { - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Traceback mismatched switch type: traceback %d rr_graph %d (RR nodes %d -> %d)\n", - trace->iswitch, rr_iswitch, - trace->index, to_node); - } - break; - } - } - - if (!found) { - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Traceback no RR edge between RR nodes %d -> %d\n", trace->index, next->index); - } - - //Recurse - return validate_traceback_recurr(next, seen_rr_nodes); - } - } - - VTR_ASSERT(!next); - return true; //End of traceback -} - //Print information about an invalid routing, caused by overused routing resources void print_invalid_routing_info(const Netlist<>& net_list, bool is_flat) { auto& device_ctx = g_vpr_ctx.device(); @@ -1738,11 +1046,11 @@ void print_invalid_routing_info(const Netlist<>& net_list, bool is_flat) { std::multimap rr_node_nets; for (auto net_id : net_list.nets()) { - t_trace* tptr = route_ctx.trace[net_id].head; + if (!route_ctx.route_trees[net_id]) + continue; - while (tptr != nullptr) { - rr_node_nets.emplace(tptr->index, net_id); - tptr = tptr->next; + for (auto& rt_node : route_ctx.route_trees[net_id].value().all_nodes()) { + rr_node_nets.emplace(size_t(rt_node.inode), net_id); } } @@ -1754,7 +1062,7 @@ void print_invalid_routing_info(const Netlist<>& net_list, bool is_flat) { int occ = route_ctx.rr_node_route_inf[inode].occ(); int cap = rr_graph.node_capacity(rr_id); if (occ > cap) { - VTR_LOG(" %s is overused (occ=%d capacity=%d)\n", describe_rr_node(rr_graph, device_ctx.grid, device_ctx.rr_indexed_data, inode, is_flat).c_str(), occ, cap); + VTR_LOG(" %s is overused (occ=%d capacity=%d)\n", describe_rr_node(rr_graph, device_ctx.grid, device_ctx.rr_indexed_data, size_t(inode), is_flat).c_str(), occ, cap); auto range = rr_node_nets.equal_range(inode); for (auto itr = range.first; itr != range.second; ++itr) { @@ -1833,34 +1141,6 @@ void print_rr_node_route_inf_dot() { VTR_LOG("}\n"); } -static bool validate_trace_nodes(t_trace* head, const std::unordered_set& trace_nodes) { - //Verifies that all nodes in the traceback 'head' are conatined in 'trace_nodes' - - if (!head) { - return true; - } - - std::vector missing_from_trace_nodes; - for (t_trace* tptr = head; tptr != nullptr; tptr = tptr->next) { - if (!trace_nodes.count(tptr->index)) { - missing_from_trace_nodes.push_back(tptr->index); - } - } - - if (!missing_from_trace_nodes.empty()) { - std::string msg = vtr::string_fmt( - "The following %zu nodes were found in traceback" - " but were missing from trace_nodes: %s\n", - missing_from_trace_nodes.size(), - vtr::join(missing_from_trace_nodes, ", ").c_str()); - - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, msg.c_str()); - return false; - } - - return true; -} - // True if router will use a lookahead. // // This controls whether the router lookahead cache will be primed outside of diff --git a/vpr/src/route/route_common.h b/vpr/src/route/route_common.h index b43456f2a72..9df96d7f807 100644 --- a/vpr/src/route/route_common.h +++ b/vpr/src/route/route_common.h @@ -16,20 +16,14 @@ t_bb load_net_route_bb(const Netlist<>& net_list, ParentNetId net_id, int bb_factor); -void pathfinder_update_path_occupancy(t_trace* route_segment_start, int add_or_sub); - void pathfinder_update_single_node_occupancy(int inode, int add_or_sub); void pathfinder_update_acc_cost_and_overuse_info(float acc_fac, OveruseInfo& overuse_info); -float update_pres_fac(float new_pres_fac); +/** Update pathfinder cost of all nodes rooted at rt_node, including rt_node itself */ +void pathfinder_update_cost_from_route_tree(const RouteTreeNode& root, int add_or_sub); -/* Pass in the hptr starting at a SINK with target_net_pin_index, which is the net pin index corresonding * - * to the sink (ranging from 1 to fanout). Returns a pointer to the first "new" node in the traceback * - * (node not previously in trace). */ -t_trace* update_traceback(t_heap* hptr, - int target_net_pin_index, - ParentNetId net_id); +float update_pres_fac(float new_pres_fac); void reset_path_costs(const std::vector& visited_rr_nodes); @@ -97,12 +91,6 @@ void mark_ends(const Netlist<>& net_list, ParentNetId net_id); void mark_remaining_ends(ParentNetId net_id, const std::vector& remaining_sinks); -void free_traceback(ParentNetId net_id); - -void drop_traceback_tail(ParentNetId net_id); - -void free_traceback(t_trace* tptr); - void add_to_mod_list(int inode, std::vector& modified_rr_node_inf); void init_route_structs(const Netlist<>& net_list, @@ -114,23 +102,12 @@ void alloc_and_load_rr_node_route_structs(); void reset_rr_node_route_structs(); -void free_trace_structs(const Netlist<>& net_list); - void reserve_locally_used_opins(HeapInterface* heap, float pres_fac, float acc_fac, bool rip_up_local_opins, bool is_flat); -void free_chunk_memory_trace(); - -bool validate_traceback(t_trace* trace); -void print_traceback(ParentNetId net_id); -void print_traceback(const t_trace* trace); - void print_rr_node_route_inf(); void print_rr_node_route_inf_dot(); void print_invalid_routing_info(const Netlist<>& net_list, bool is_flat); -t_trace* alloc_trace_data(); -void free_trace_data(t_trace* trace); - bool router_needs_lookahead(enum e_router_algorithm router_algorithm); std::string describe_unrouteable_connection(const int source_node, const int sink_node, bool is_flat); diff --git a/vpr/src/route/route_export.h b/vpr/src/route/route_export.h index 4b85fe13731..6c1d1e547db 100644 --- a/vpr/src/route/route_export.h +++ b/vpr/src/route/route_export.h @@ -40,28 +40,12 @@ t_clb_opins_used alloc_route_structs(); void free_route_structs(); -vtr::vector alloc_saved_routing(const Netlist<>& net_list); - -void free_saved_routing(const Netlist<>& net_list, - vtr::vector& best_routing); - -void save_routing(const Netlist<>& net_list, - vtr::vector& best_routing, +void save_routing(vtr::vector>& best_routing, const t_clb_opins_used& clb_opins_used_locally, t_clb_opins_used& saved_clb_opins_used_locally); -void restore_routing(const Netlist<>& net_list, - vtr::vector& best_routing, +void restore_routing(vtr::vector>& best_routing, t_clb_opins_used& clb_opins_used_locally, const t_clb_opins_used& saved_clb_opins_used_locally); void get_serial_num(const Netlist<>& net_list); - -void print_route(const Netlist<>& net_list, - const char* place_file, - const char* route_file, - bool is_flat); -void print_route(const Netlist<>& net_list, - FILE* fp, - const vtr::vector& tracebacks, - bool is_flat); diff --git a/vpr/src/route/route_timing.cpp b/vpr/src/route/route_timing.cpp index 620094beac7..d119403cb8e 100644 --- a/vpr/src/route/route_timing.cpp +++ b/vpr/src/route/route_timing.cpp @@ -5,7 +5,9 @@ #include #include #include +#include +#include "netlist_fwd.h" #include "vtr_assert.h" #include "vtr_log.h" #include "vtr_time.h" @@ -15,9 +17,9 @@ #include "vpr_error.h" #include "globals.h" +#include "read_route.h" #include "route_export.h" #include "route_common.h" -#include "route_tree_timing.h" #include "route_timing.h" #include "net_delay.h" #include "stats.h" @@ -82,7 +84,7 @@ bool f_router_debug = false; //Count the number of times the router has failed static int num_routing_failed = 0; -/******************** Subroutines local to route_timing.c ********************/ +/******************** Subroutines local to route_timing.cpp ********************/ template static bool timing_driven_route_sink(ConnectionRouter& router, @@ -92,8 +94,8 @@ static bool timing_driven_route_sink(ConnectionRouter& router, int target_pin, const t_conn_cost_params cost_params, const t_router_opts& router_opts, - t_rt_node* rt_root, - t_rt_node** rt_node_of_sink, + RouteTree& tree, + std::vector>& rt_node_of_sink, SpatialRouteTreeLookup& spatial_rt_lookup, RouterStats& router_stats, route_budgets& budgeting_inf, @@ -108,30 +110,32 @@ static bool timing_driven_pre_route_to_clock_root(ConnectionRouter& router, int sink_node, const t_conn_cost_params cost_params, int high_fanout_threshold, - t_rt_node* rt_root, + RouteTree& tree, SpatialRouteTreeLookup& spatial_rt_lookup, RouterStats& router_stats, bool is_flat); -void disable_expansion_and_remove_sink_from_route_tree_nodes(t_rt_node* node); - -static t_rt_node* setup_routing_resources(int itry, - ParentNetId net_id, - const Netlist<>& net_list, - unsigned num_sinks, - int min_incremental_reroute_fanout, - CBRR& connections_inf, - t_rt_node** rt_node_of_sink, - const t_router_opts& router_opts, - bool ripup_high_fanout_nets, - bool is_flat); +static void setup_routing_resources(int itry, + ParentNetId net_id, + const Netlist<>& net_list, + unsigned num_sinks, + int min_incremental_reroute_fanout, + CBRR& connections_inf, + std::vector>& rt_node_of_sink, + const t_router_opts& router_opts, + bool ripup_high_fanout_nets); static bool timing_driven_check_net_delays(const Netlist<>& net_list, - NetPinsMatrix& net_delay, - bool is_flat); + NetPinsMatrix& net_delay); + +static void update_net_delays_from_route_tree(float* net_delay, + const Netlist<>& net_list, + std::vector>& rt_node_of_sink, + ParentNetId inet, + TimingInfo* timing_info, + NetPinTimingInvalidator* pin_timing_invalidator); static bool should_route_net(ParentNetId net_id, - const vtr::vector& net_trace, CBRR& connections_inf, bool if_force_reroute); @@ -182,9 +186,10 @@ static bool is_high_fanout(int fanout, int fanout_threshold); static size_t dynamic_update_bounding_boxes(const std::vector& updated_nets, const Netlist<>& net_list, int high_fanout_threshold); -static t_bb calc_current_bb(const t_trace* head); -static bool is_better_quality_routing(const vtr::vector& best_routing, +static t_bb calc_current_bb(const RouteTree& tree); + +static bool is_better_quality_routing(const vtr::vector>& best_routing, const RoutingMetrics& best_routing_metrics, const WirelengthInfo& wirelength_info, std::shared_ptr timing_info); @@ -200,9 +205,10 @@ static void generate_route_timing_reports(const t_router_opts& router_opts, const RoutingDelayCalculator& delay_calc, bool is_flat); +static void update_rr_route_inf_from_tree(const RouteTreeNode& rt_node); + static void prune_unused_non_configurable_nets(CBRR& connections_inf, - const Netlist<>& net_list, - bool is_flat); + const Netlist<>& net_list); static void init_net_delay_from_lookahead(const RouterLookahead& router_lookahead, const Netlist<>& net_list, @@ -428,7 +434,7 @@ bool try_timing_driven_route_tmpl(const Netlist<>& net_list, /* * Best result so far */ - vtr::vector best_routing; + vtr::vector> best_routing; t_clb_opins_used best_clb_opins_used_locally; RoutingMetrics best_routing_metrics; int legal_convergence_count = 0; @@ -498,7 +504,7 @@ bool try_timing_driven_route_tmpl(const Netlist<>& net_list, RouterStats router_stats; init_route_stats(router_stats); - timing_driven_route_structs route_structs(std::max(get_max_pins_per_net(net_list) - 1, 0)); + timing_driven_route_structs route_structs(net_list); float prev_iter_cumm_time = 0; vtr::Timer iteration_timer; int num_net_bounding_boxes_updated = 0; @@ -613,7 +619,7 @@ bool try_timing_driven_route_tmpl(const Netlist<>& net_list, critical_path = timing_info->least_slack_critical_path(); - VTR_ASSERT_SAFE(timing_driven_check_net_delays(net_list, net_delay, is_flat)); + VTR_ASSERT_SAFE(timing_driven_check_net_delays(net_list, net_delay)); if (itry == 1) { generate_route_timing_reports(router_opts, analysis_opts, *timing_info, *delay_calc, is_flat); @@ -651,14 +657,14 @@ bool try_timing_driven_route_tmpl(const Netlist<>& net_list, if (is_better_quality_routing(best_routing, best_routing_metrics, wirelength_info, timing_info)) { //Save routing - best_routing = router_ctx.trace; + best_routing = router_ctx.route_trees; best_clb_opins_used_locally = router_ctx.clb_opins_used_locally; routing_is_successful = true; //Update best metrics if (timing_info) { - timing_driven_check_net_delays(net_list, net_delay, is_flat); + timing_driven_check_net_delays(net_list, net_delay); best_routing_metrics.sTNS = timing_info->setup_total_negative_slack(); best_routing_metrics.sWNS = timing_info->setup_worst_negative_slack(); @@ -884,13 +890,15 @@ bool try_timing_driven_route_tmpl(const Netlist<>& net_list, /* Restore congestion from best route */ for (auto net_id : net_list.nets()) { - pathfinder_update_path_occupancy(route_ctx.trace[net_id].head, -1); - pathfinder_update_path_occupancy(best_routing[net_id].head, 1); + if (route_ctx.route_trees[net_id]) + pathfinder_update_cost_from_route_tree(route_ctx.route_trees[net_id]->root(), -1); + if (best_routing[net_id]) + pathfinder_update_cost_from_route_tree(best_routing[net_id]->root(), 1); } - router_ctx.trace = best_routing; + router_ctx.route_trees = best_routing; router_ctx.clb_opins_used_locally = best_clb_opins_used_locally; - prune_unused_non_configurable_nets(connections_inf, net_list, is_flat); + prune_unused_non_configurable_nets(connections_inf, net_list); if (timing_info) { VTR_LOG("Critical path: %g ns\n", 1e9 * best_routing_metrics.critical_path.delay()); @@ -948,8 +956,8 @@ bool try_timing_driven_route_net(ConnectionRouter& router, const t_router_opts& router_opts, CBRR& connections_inf, RouterStats& router_stats, - float* pin_criticality, - t_rt_node** rt_node_of_sink, + std::vector& pin_criticality, + std::vector>& rt_node_of_sink, NetPinsMatrix& net_delay, const ClusteredPinAtomPinsLookup& netlist_pin_lookup, std::shared_ptr timing_info, @@ -976,7 +984,7 @@ bool try_timing_driven_route_net(ConnectionRouter& router, is_routed = true; } else if (net_list.net_is_ignored(net_id)) { /* Skip ignored nets. */ is_routed = true; - } else if (!(reroute_for_hold) && !should_route_net(net_id, route_ctx.trace, connections_inf, true)) { + } else if (!(reroute_for_hold) && !should_route_net(net_id, connections_inf, true)) { is_routed = true; } else { // track time spent vs fanout @@ -1016,57 +1024,6 @@ bool try_timing_driven_route_net(ConnectionRouter& router, return (is_routed); } -/* - * NOTE: - * Suggest using a timing_driven_route_structs struct. Memory is managed for you - */ -void alloc_timing_driven_route_structs(float** pin_criticality_ptr, - int** sink_order_ptr, - t_rt_node*** rt_node_of_sink_ptr, - int max_sinks) { - /* Allocates all the structures needed only by the timing-driven router. */ - *pin_criticality_ptr = new float[max_sinks + 1]; /* First sink is pin #1.*/ - *sink_order_ptr = new int[max_sinks + 1]; - *rt_node_of_sink_ptr = new t_rt_node*[max_sinks + 1]; - - /* Element 0 should be an invalid value so we are likely to crash if we accidentally use it. */ - (*pin_criticality_ptr)[0] = -1; - (*sink_order_ptr)[0] = -1; - (*rt_node_of_sink_ptr)[0] = nullptr; - - alloc_route_tree_timing_structs(); -} - -/* - * NOTE: - * Suggest using a timing_driven_route_structs struct. Memory is managed for you - */ -void free_timing_driven_route_structs(float* pin_criticality, int* sink_order, t_rt_node** rt_node_of_sink) { - /* Frees all the structures needed only by the timing-driven router. */ - - // coverity[offset_free : Intentional] - delete[] pin_criticality; - // coverity[offset_free : Intentional] - delete[] sink_order; - // coverity[offset_free : Intentional] - delete[] rt_node_of_sink; - - free_route_tree_timing_structs(); -} - -timing_driven_route_structs::timing_driven_route_structs(int max_sinks) { - alloc_timing_driven_route_structs(&pin_criticality, - &sink_order, - &rt_node_of_sink, - max_sinks); -} - -timing_driven_route_structs::~timing_driven_route_structs() { - free_timing_driven_route_structs(pin_criticality, - sink_order, - rt_node_of_sink); -} - int get_max_pins_per_net(const Netlist<>& net_list) { int max_pins_per_net = 0; for (auto net_id : net_list.nets()) { @@ -1077,18 +1034,6 @@ int get_max_pins_per_net(const Netlist<>& net_list) { return (max_pins_per_net); } -struct Criticality_comp { - const float* criticality; - - Criticality_comp(const float* calculated_criticalities) - : criticality{calculated_criticalities} { - } - - bool operator()(int a, int b) const { - return criticality[a] > criticality[b]; - } -}; - template bool timing_driven_route_net(ConnectionRouter& router, const Netlist<>& net_list, @@ -1098,8 +1043,8 @@ bool timing_driven_route_net(ConnectionRouter& router, const t_router_opts& router_opts, CBRR& connections_inf, RouterStats& router_stats, - float* pin_criticality, - t_rt_node** rt_node_of_sink, + std::vector& pin_criticality, + std::vector>& rt_node_of_sink, float* net_delay, const ClusteredPinAtomPinsLookup& netlist_pin_lookup, std::shared_ptr timing_info, @@ -1116,23 +1061,25 @@ bool timing_driven_route_net(ConnectionRouter& router, auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; - auto& route_ctx = g_vpr_ctx.routing(); + auto& route_ctx = g_vpr_ctx.mutable_routing(); unsigned int num_sinks = net_list.net_sinks(net_id).size(); VTR_LOGV_DEBUG(f_router_debug, "Routing Net %zu (%zu sinks)\n", size_t(net_id), num_sinks); - t_rt_node* rt_root; - rt_root = setup_routing_resources(itry, - net_id, - net_list, - num_sinks, - router_opts.min_incremental_reroute_fanout, - connections_inf, - rt_node_of_sink, - router_opts, - check_hold(router_opts, worst_neg_slack), - is_flat); + setup_routing_resources( + itry, + net_id, + net_list, + num_sinks, + router_opts.min_incremental_reroute_fanout, + connections_inf, + rt_node_of_sink, + router_opts, + check_hold(router_opts, worst_neg_slack)); + + VTR_ASSERT(route_ctx.route_trees[net_id]); + RouteTree& tree = route_ctx.route_trees[net_id].value(); bool high_fanout = is_high_fanout(num_sinks, router_opts.high_fanout_threshold); @@ -1141,7 +1088,7 @@ bool timing_driven_route_net(ConnectionRouter& router, spatial_route_tree_lookup = build_route_tree_spatial_lookup(net_list, route_ctx.route_bb, net_id, - rt_root); + tree.root()); } // after this point the route tree is correct @@ -1167,7 +1114,9 @@ bool timing_driven_route_net(ConnectionRouter& router, } // compare the criticality of different sink nodes - sort(begin(remaining_targets), end(remaining_targets), Criticality_comp{pin_criticality}); + sort(begin(remaining_targets), end(remaining_targets), [&](int a, int b) { + return pin_criticality[a] > pin_criticality[b]; + }); /* Update base costs according to fanout and criticality rules */ update_rr_base_costs(num_sinks); @@ -1197,7 +1146,7 @@ bool timing_driven_route_net(ConnectionRouter& router, sink_node, cost_params, router_opts.high_fanout_threshold, - rt_root, + tree, spatial_route_tree_lookup, router_stats, is_flat)) { @@ -1237,7 +1186,7 @@ bool timing_driven_route_net(ConnectionRouter& router, target_pin, cost_params, router_opts, - rt_root, + tree, rt_node_of_sink, spatial_route_tree_lookup, router_stats, @@ -1273,21 +1222,11 @@ bool timing_driven_route_net(ConnectionRouter& router, } } - // if (!net_list.net_is_ignored(net_id)) { - // for (unsigned ipin = 1; ipin < net_list.net_pins(net_id).size(); ++ipin) { - // if (net_delay[ipin] == 0) { // should be SOURCE->OPIN->IPIN->SINK - // VTR_ASSERT(rr_graph.node_type(RRNodeId(rt_node_of_sink[ipin]->parent_node->parent_node->inode)) == OPIN); - // } - // } - // } - VTR_ASSERT_MSG(g_vpr_ctx.routing().rr_node_route_inf[rt_root->inode].occ() <= rr_graph.node_capacity(RRNodeId(rt_root->inode)), "SOURCE should never be congested"); + VTR_ASSERT_MSG(g_vpr_ctx.routing().rr_node_route_inf[size_t(tree.root().inode)].occ() <= rr_graph.node_capacity(tree.root().inode), "SOURCE should never be congested"); - // route tree is not kept persistent since building it from the traceback the next iteration takes almost 0 time VTR_LOGV_DEBUG(f_router_debug, "Routed Net %zu (%zu sinks)\n", size_t(net_id), num_sinks); - - free_route_tree(rt_root); - router.empty_rcv_route_tree_set(); - return (true); + router.empty_rcv_route_tree_set(); // ? + return true; } template @@ -1297,7 +1236,7 @@ static bool timing_driven_pre_route_to_clock_root(ConnectionRouter& router, int sink_node, const t_conn_cost_params cost_params, int high_fanout_threshold, - t_rt_node* rt_root, + RouteTree& tree, SpatialRouteTreeLookup& spatial_rt_lookup, RouterStats& router_stats, bool is_flat) { @@ -1310,8 +1249,6 @@ static bool timing_driven_pre_route_to_clock_root(ConnectionRouter& router, VTR_LOGV_DEBUG(f_router_debug, "Net %zu pre-route to (%s)\n", size_t(net_id), describe_rr_node(device_ctx.rr_graph, device_ctx.grid, device_ctx.rr_indexed_data, sink_node, is_flat).c_str()); profiling::sink_criticality_start(); - VTR_ASSERT_DEBUG(verify_traceback_route_tree_equivalent(route_ctx.trace[net_id].head, rt_root)); - t_bb bounding_box = route_ctx.route_bb[net_id]; router.clear_modified_rr_node_info(); @@ -1324,7 +1261,7 @@ static bool timing_driven_pre_route_to_clock_root(ConnectionRouter& router, std::unordered_map()); std::tie(found_path, cheapest) = router.timing_driven_route_connection_from_route_tree( - rt_root, + tree.root(), sink_node, cost_params, bounding_box, @@ -1347,45 +1284,31 @@ static bool timing_driven_pre_route_to_clock_root(ConnectionRouter& router, profiling::sink_criticality_end(cost_params.criticality); - /* NB: In the code below I keep two records of the partial routing: the * - * traceback and the route_tree. The route_tree enables fast recomputation * - * of the Elmore delay to each node in the partial routing. The traceback * - * lets me reuse all the routines written for breadth-first routing, which * - * all take a traceback structure as input. */ - /* This is a special pre-route to a sink that does not correspond to any * * netlist pin, but which can be reached from the global clock root drive * * points. Therefore, we can set the net pin index of the sink node to * * OPEN (meaning illegal) as it is not meaningful for this sink. */ + vtr::optional new_branch, new_sink; + std::tie(new_branch, new_sink) = tree.update_from_heap(&cheapest, OPEN, ((high_fanout) ? &spatial_rt_lookup : nullptr), is_flat); + + VTR_ASSERT_DEBUG(!high_fanout || validate_route_tree_spatial_lookup(tree.root(), spatial_rt_lookup)); - t_trace* new_route_start_tptr = update_traceback(&cheapest, - OPEN, - net_id); - VTR_ASSERT_DEBUG(validate_traceback(route_ctx.trace[net_id].head)); - update_route_tree(&cheapest, OPEN, ((high_fanout) ? &spatial_rt_lookup : nullptr), is_flat); - VTR_ASSERT_DEBUG(verify_route_tree(rt_root)); - VTR_ASSERT_DEBUG(verify_traceback_route_tree_equivalent(route_ctx.trace[net_id].head, rt_root)); - VTR_ASSERT_DEBUG(!high_fanout || validate_route_tree_spatial_lookup(rt_root, spatial_rt_lookup)); if (f_router_debug) { std::string msg = vtr::string_fmt("Routed Net %zu connection to RR node %d successfully", size_t(net_id), sink_node); update_screen(ScreenUpdatePriority::MAJOR, msg.c_str(), ROUTING, nullptr); } - pathfinder_update_path_occupancy(new_route_start_tptr, 1); + + if (new_branch) + pathfinder_update_cost_from_route_tree(new_branch.value(), 1); // need to guarantee ALL nodes' path costs are HUGE_POSITIVE_FLOAT at the start of routing to a sink // do this by resetting all the path_costs that have been touched while routing to the current sink router.reset_path_costs(); - // Post route trace back and route tree clean up: - // - remove sink from trace back and route tree - // - fix routing for all nodes leading to the sink + // Post route cleanup: + // - remove sink from route tree and fix routing for all nodes leading to the sink ("freeze") // - free up virtual sink occupancy - disable_expansion_and_remove_sink_from_route_tree_nodes(rt_root); - VTR_LOGV_DEBUG(f_router_debug, "Traceback tail before update %d \n", - route_ctx.trace[net_id].tail->index); - drop_traceback_tail(net_id); - VTR_LOGV_DEBUG(f_router_debug, "Updated traceback ptrs: %d %d \n", - route_ctx.trace[net_id].head->index, route_ctx.trace[net_id].tail->index); + tree.freeze(); m_route_ctx.rr_node_route_inf[sink_node].set_occ(0); // routed to a sink successfully @@ -1400,15 +1323,15 @@ static bool timing_driven_route_sink(ConnectionRouter& router, int target_pin, const t_conn_cost_params cost_params, const t_router_opts& router_opts, - t_rt_node* rt_root, - t_rt_node** rt_node_of_sink, + RouteTree& tree, + std::vector>& rt_node_of_sink, SpatialRouteTreeLookup& spatial_rt_lookup, RouterStats& router_stats, route_budgets& budgeting_inf, const RoutingPredictor& routing_predictor, const std::vector>& choking_spots, bool is_flat) { - /* Build a path from the existing route tree rooted at rt_root to the target_node + /* Build a path from the existing route tree to the target_node * add this branch to the existing route tree and update pathfinder costs and rr_node_route_inf to reflect this */ const auto& device_ctx = g_vpr_ctx.device(); auto& route_ctx = g_vpr_ctx.mutable_routing(); @@ -1418,8 +1341,6 @@ static bool timing_driven_route_sink(ConnectionRouter& router, int sink_node = route_ctx.net_rr_terminals[net_id][target_pin]; VTR_LOGV_DEBUG(f_router_debug, "Net %zu Target %d (%s)\n", size_t(net_id), itarget, describe_rr_node(device_ctx.rr_graph, device_ctx.grid, device_ctx.rr_indexed_data, sink_node, is_flat).c_str()); - VTR_ASSERT_DEBUG(verify_traceback_route_tree_equivalent(route_ctx.trace[net_id].head, rt_root)); - router.clear_modified_rr_node_info(); bool found_path; @@ -1439,7 +1360,7 @@ static bool timing_driven_route_sink(ConnectionRouter& router, //However, if the current sink is 'critical' from a timing perspective, we put the entire route tree back onto //the heap to ensure it has more flexibility to find the best path. if (high_fanout && !sink_critical && !net_is_global && !net_is_clock && -routing_predictor.get_slope() > router_opts.high_fanout_max_slope) { - std::tie(found_path, cheapest) = router.timing_driven_route_connection_from_route_tree_high_fanout(rt_root, + std::tie(found_path, cheapest) = router.timing_driven_route_connection_from_route_tree_high_fanout(tree.root(), sink_node, cost_params, bounding_box, @@ -1447,7 +1368,7 @@ static bool timing_driven_route_sink(ConnectionRouter& router, router_stats, conn_params); } else { - std::tie(found_path, cheapest) = router.timing_driven_route_connection_from_route_tree(rt_root, + std::tie(found_path, cheapest) = router.timing_driven_route_connection_from_route_tree(tree.root(), sink_node, cost_params, bounding_box, @@ -1471,22 +1392,14 @@ static bool timing_driven_route_sink(ConnectionRouter& router, profiling::sink_criticality_end(cost_params.criticality); - /* NB: In the code below I keep two records of the partial routing: the * - * traceback and the route_tree. The route_tree enables fast recomputation * - * of the Elmore delay to each node in the partial routing. The traceback * - * lets me reuse all the routines written for breadth-first routing, which * - * all take a traceback structure as input. */ - int inode = cheapest.index; route_ctx.rr_node_route_inf[inode].target_flag--; /* Connected to this SINK. */ - t_trace* new_route_start_tptr = update_traceback(&cheapest, target_pin, net_id); - VTR_ASSERT_DEBUG(validate_traceback(route_ctx.trace[net_id].head)); + vtr::optional new_branch, new_sink; + std::tie(new_branch, new_sink) = tree.update_from_heap(&cheapest, target_pin, ((high_fanout) ? &spatial_rt_lookup : nullptr), is_flat); + + VTR_ASSERT_DEBUG(!high_fanout || validate_route_tree_spatial_lookup(tree.root(), spatial_rt_lookup)); - rt_node_of_sink[target_pin] = update_route_tree(&cheapest, target_pin, ((high_fanout) ? &spatial_rt_lookup : nullptr), is_flat); - VTR_ASSERT_DEBUG(verify_route_tree(rt_root)); - VTR_ASSERT_DEBUG(verify_traceback_route_tree_equivalent(route_ctx.trace[net_id].head, rt_root)); - VTR_ASSERT_DEBUG(!high_fanout || validate_route_tree_spatial_lookup(rt_root, spatial_rt_lookup)); if (f_router_debug) { std::string msg = vtr::string_fmt("Routed Net %zu connection %d to RR node %d successfully", size_t(net_id), itarget, sink_node); update_screen(ScreenUpdatePriority::MAJOR, msg.c_str(), ROUTING, nullptr); @@ -1498,7 +1411,11 @@ static bool timing_driven_route_sink(ConnectionRouter& router, } } - pathfinder_update_path_occupancy(new_route_start_tptr, 1); + rt_node_of_sink[target_pin] = new_sink; + + /* update global occupancy from the new branch */ + if (new_branch) + pathfinder_update_cost_from_route_tree(new_branch.value(), 1); // need to guarantee ALL nodes' path costs are HUGE_POSITIVE_FLOAT at the start of routing to a sink // do this by resetting all the path_costs that have been touched while routing to the current sink @@ -1508,39 +1425,39 @@ static bool timing_driven_route_sink(ConnectionRouter& router, return true; } -static t_rt_node* setup_routing_resources(int itry, - ParentNetId net_id, - const Netlist<>& net_list, - unsigned num_sinks, - int min_incremental_reroute_fanout, - CBRR& connections_inf, - t_rt_node** rt_node_of_sink, - const t_router_opts& router_opts, - bool ripup_high_fanout_nets, - bool is_flat) { +static void setup_routing_resources(int itry, + ParentNetId net_id, + const Netlist<>& net_list, + unsigned num_sinks, + int min_incremental_reroute_fanout, + CBRR& connections_inf, + std::vector>& rt_node_of_sink, + const t_router_opts& router_opts, + bool ripup_high_fanout_nets) { /* Build and return a partial route tree from the legal connections from last iteration. * along the way do: * update pathfinder costs to be accurate to the partial route tree - * update the net's traceback to be accurate to the partial route tree * find and store the pins that still need to be reached in incremental_rerouting_resources.remaining_targets * find and store the rt nodes that have been reached in incremental_rerouting_resources.reached_rt_sinks - * mark the rr_node sinks as targets to be reached */ - - auto& route_ctx = g_vpr_ctx.routing(); + * mark the rr_node sinks as targets to be reached. */ + auto& route_ctx = g_vpr_ctx.mutable_routing(); - t_rt_node* rt_root; + /* "tree" points to this net's spot in the global context here, so re-initializing it etc. changes the global state */ + vtr::optional& tree = route_ctx.route_trees[net_id]; // for nets below a certain size (min_incremental_reroute_fanout), rip up any old routing // otherwise, we incrementally reroute by reusing legal parts of the previous iteration - // convert the previous iteration's traceback into the starting route tree for this iteration if ((int)num_sinks < min_incremental_reroute_fanout || itry == 1 || ripup_high_fanout_nets) { profiling::net_rerouted(); - // rip up the whole net - pathfinder_update_path_occupancy(route_ctx.trace[net_id].head, -1); - free_traceback(net_id); + /* rip up the whole net */ + if (tree) + pathfinder_update_cost_from_route_tree(tree.value().root(), -1); + tree = vtr::nullopt; + + /* re-initialize net */ + tree = RouteTree(net_id); - rt_root = init_route_tree_to_source(net_id); for (unsigned int sink_pin = 1; sink_pin <= num_sinks; ++sink_pin) connections_inf.toreach_rr_sink(sink_pin); // since all connections will be rerouted for this net, clear all of net's forced reroute flags @@ -1551,129 +1468,73 @@ static t_rt_node* setup_routing_resources(int itry, // of their versions that act on node indices directly like mark_remaining_ends mark_ends(net_list, net_id); } else { - auto& reached_rt_sinks = connections_inf.get_reached_rt_sinks(); + auto& reached_sinks = connections_inf.get_reached_rt_sinks(); auto& remaining_targets = connections_inf.get_remaining_targets(); profiling::net_rebuild_start(); - // convert the previous iteration's traceback into a route tree - rt_root = traceback_to_route_tree(net_id, is_flat); - - //Sanity check that route tree and traceback are equivalent before pruning - VTR_ASSERT_DEBUG(verify_traceback_route_tree_equivalent(route_ctx.trace[net_id].head, rt_root)); + if (!tree) + tree = RouteTree(net_id); - // check for edge correctness - VTR_ASSERT_SAFE(is_valid_skeleton_tree(rt_root)); + /* copy the existing routing + * prune_route_tree depends on global occ, so we can't subtract before pruning + * OPT: to skip this copy, return a "diff" from RouteTree::prune */ + RouteTree tree2 = tree.value(); // Skip this check if RCV is enabled, as RCV can use another method to cause reroutes - VTR_ASSERT_SAFE(should_route_net(net_id, route_ctx.trace, connections_inf, true) || router_opts.routing_budgets_algorithm == YOYO); + VTR_ASSERT_SAFE(should_route_net(net_id, connections_inf, true) || router_opts.routing_budgets_algorithm == YOYO); - //Prune the branches of the tree that don't legally lead to sinks - rt_root = prune_route_tree(rt_root, connections_inf); + // Prune the copy (using congestion data before subtraction) + vtr::optional pruned_tree2 = tree2.prune(connections_inf); - //Now that the tree has been pruned, we can free the old traceback - // NOTE: this must happen *after* pruning since it changes the - // recorded congestion - pathfinder_update_path_occupancy(route_ctx.trace[net_id].head, -1); - free_traceback(net_id); + // Subtract congestion using the non-pruned original + pathfinder_update_cost_from_route_tree(tree.value().root(), -1); - if (rt_root) { //Partially pruned + if (pruned_tree2) { //Partially pruned profiling::route_tree_preserved(); - //Since we have a valid partial routing (to at least one SINK) - //we need to make sure the traceback is synchronized to the route tree - traceback_from_route_tree(net_id, rt_root, reached_rt_sinks.size()); - - //Sanity check the traceback for self-consistency - VTR_ASSERT_DEBUG(validate_traceback(route_ctx.trace[net_id].head)); - - //Sanity check that route tree and traceback are equivalent after pruning - VTR_ASSERT_DEBUG(verify_traceback_route_tree_equivalent(route_ctx.trace[net_id].head, rt_root)); - - // put the updated occupancies of the route tree nodes back into pathfinder - pathfinder_update_path_occupancy(route_ctx.trace[net_id].head, 1); - - } else { //Fully destroyed + // Add back congestion for the pruned route tree + pathfinder_update_cost_from_route_tree(pruned_tree2.value().root(), 1); + // pruned_tree2 is no longer required -> we can move rather than copy + tree = std::move(pruned_tree2.value()); + } else { // Fully destroyed profiling::route_tree_pruned(); - //Initialize only to source - rt_root = init_route_tree_to_source(net_id); - - //NOTE: We leave the traceback uninitialized, so update_traceback() - // will correctly add the SOURCE node when the branch to - // the first SINK is found. - VTR_ASSERT(route_ctx.trace[net_id].head == nullptr); - VTR_ASSERT(route_ctx.trace[net_id].tail == nullptr); - VTR_ASSERT(route_ctx.trace_nodes[net_id].empty()); + // Initialize only to source + tree = RouteTree(net_id); } - //Update R/C - load_new_subtree_R_upstream(rt_root); - load_new_subtree_C_downstream(rt_root); - - VTR_ASSERT(reached_rt_sinks.size() + remaining_targets.size() == num_sinks); - - //Record current routing - add_route_tree_to_rr_node_lookup(rt_root); + VTR_ASSERT(reached_sinks.size() + remaining_targets.size() == num_sinks); // give lookup on the reached sinks - for (t_rt_node* sink_node : reached_rt_sinks) { - rt_node_of_sink[sink_node->net_pin_index] = sink_node; + for (RRNodeId sink_rr_node : reached_sinks) { + auto& sink_node = tree.value().find_by_rr_id(sink_rr_node).value(); + rt_node_of_sink[sink_node.net_pin_index] = sink_node; } profiling::net_rebuild_end(num_sinks, remaining_targets.size()); + // still need to calculate the tree's time delay (0 Tarrival means from SOURCE) + tree.value().reload_timing(); + // check for R_upstream C_downstream and edge correctness - VTR_ASSERT_SAFE(is_valid_route_tree(rt_root)); + VTR_ASSERT_SAFE(tree.value().is_valid()); + // congestion should've been pruned away - VTR_ASSERT_SAFE(is_uncongested_route_tree(rt_root)); + VTR_ASSERT_SAFE(tree.value().is_uncongested()); // mark remaining ends mark_remaining_ends(net_id, remaining_targets); - // still need to calculate the tree's time delay (0 Tarrival means from SOURCE) - load_route_tree_Tdel(rt_root, 0); - // mark the lookup (rr_node_route_inf) for existing tree elements as NO_PREVIOUS so add_to_path stops when it reaches one of them - load_route_tree_rr_route_inf(rt_root); + update_rr_route_inf_from_tree(tree.value().root()); } // completed constructing the partial route tree and updated all other data structures to match - return rt_root; -} - -void disable_expansion_and_remove_sink_from_route_tree_nodes(t_rt_node* rt_node) { - /* Remove sink in route tree and mark all nodes - * leading to the sink as unexpandable. - */ - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - t_rt_node* child_node; - t_linked_rt_edge* linked_rt_edge; - linked_rt_edge = rt_node->u.child_list; - - while (linked_rt_edge != nullptr) { - child_node = linked_rt_edge->child; - if (rr_graph.node_type(RRNodeId(child_node->inode)) == SINK) { - VTR_LOGV_DEBUG(f_router_debug, - "Removing sink %d from route tree\n", child_node->inode); - rt_node->u.child_list = nullptr; - rt_node->u.next = nullptr; - free(child_node); - break; - } else { - rt_node->re_expand = false; - VTR_LOGV_DEBUG(f_router_debug, - "unexpanding: %d in route tree\n", rt_node->inode); - } - disable_expansion_and_remove_sink_from_route_tree_nodes(child_node); - linked_rt_edge = linked_rt_edge->next; - } } +/** Change the base costs of rr_nodes according to # of fanouts */ void update_rr_base_costs(int fanout) { - /* Changes the base costs of different types of rr_nodes according to the * - * criticality, fanout, etc. of the current net being routed (net_id). */ auto& device_ctx = g_vpr_ctx.mutable_device(); float factor; @@ -1691,7 +1552,25 @@ void update_rr_base_costs(int fanout) { } } -static bool timing_driven_check_net_delays(const Netlist<>& net_list, NetPinsMatrix& net_delay, bool is_flat) { +/** Traverses down a route tree and updates rr_node_inf for all nodes + * to reflect that these nodes have already been routed to */ +static void update_rr_route_inf_from_tree(const RouteTreeNode& rt_node) { + auto& route_ctx = g_vpr_ctx.mutable_routing(); + + for (auto& child : rt_node.child_nodes()) { + RRNodeId inode = child.inode; + route_ctx.rr_node_route_inf[size_t(inode)].prev_node = NO_PREVIOUS; + route_ctx.rr_node_route_inf[size_t(inode)].prev_edge = RREdgeId::INVALID(); + + // path cost should be unset + VTR_ASSERT(std::isinf(route_ctx.rr_node_route_inf[size_t(inode)].path_cost)); + VTR_ASSERT(std::isinf(route_ctx.rr_node_route_inf[size_t(inode)].backward_path_cost)); + + update_rr_route_inf_from_tree(child); + } +} + +static bool timing_driven_check_net_delays(const Netlist<>& net_list, NetPinsMatrix& net_delay) { constexpr float ERROR_TOL = 0.0001; /* Checks that the net delays computed incrementally during timing driven * @@ -1700,7 +1579,7 @@ static bool timing_driven_check_net_delays(const Netlist<>& net_list, NetPinsMat unsigned int ipin; auto net_delay_check = make_net_pins_matrix(net_list); - load_net_delay_from_routing(net_list, net_delay_check, is_flat); + load_net_delay_from_routing(net_list, net_delay_check); for (auto net_id : net_list.nets()) { for (ipin = 1; ipin < net_list.net_pins(net_id).size(); ipin++) { @@ -1726,46 +1605,60 @@ static bool timing_driven_check_net_delays(const Netlist<>& net_list, NetPinsMat return true; } +/* Goes through all the sinks of this net and copies their delay values from + * the route_tree to the net_delay array. */ +static void update_net_delays_from_route_tree(float* net_delay, + const Netlist<>& net_list, + std::vector>& rt_node_of_sink, + ParentNetId inet, + TimingInfo* timing_info, + NetPinTimingInvalidator* pin_timing_invalidator) { + for (unsigned int isink = 1; isink < net_list.net_pins(inet).size(); isink++) { + float new_delay = rt_node_of_sink[isink]->Tdel; + + if (pin_timing_invalidator && new_delay != net_delay[isink]) { + //Delay changed, invalidate for incremental timing update + VTR_ASSERT_SAFE(timing_info); + ParentPinId pin = net_list.net_pin(inet, isink); + pin_timing_invalidator->invalidate_connection(pin, timing_info); + } + + net_delay[isink] = new_delay; + } +} + /* Detect if net should be routed or not */ static bool should_route_net(ParentNetId net_id, - const vtr::vector& net_trace, CBRR& connections_inf, bool if_force_reroute) { auto& route_ctx = g_vpr_ctx.routing(); auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; - t_trace* tptr = net_trace[net_id].head; - - if (tptr == nullptr) { + if (!route_ctx.route_trees[net_id]) { /* No routing yet. */ return true; } - for (;;) { - int inode = tptr->index; - int occ = route_ctx.rr_node_route_inf[inode].occ(); - int capacity = rr_graph.node_capacity(RRNodeId(inode)); + /* Walk over all rt_nodes in the net */ + for (auto& rt_node : route_ctx.route_trees[net_id]->all_nodes()) { + RRNodeId inode = rt_node.inode; + int occ = route_ctx.rr_node_route_inf[size_t(inode)].occ(); + int capacity = rr_graph.node_capacity(inode); if (occ > capacity) { return true; /* overuse detected */ } - if (tptr->iswitch == OPEN) { //End of a branch + if (rt_node.is_leaf()) { //End of a branch // even if net is fully routed, not complete if parts of it should get ripped up (EXPERIMENTAL) if (if_force_reroute) { - if (connections_inf.should_force_reroute_connection(inode)) { + if (connections_inf.should_force_reroute_connection(size_t(inode))) { return true; } } - tptr = tptr->next; /* Skip next segment (duplicate of original branch node). */ - if (tptr == nullptr) - break; } - - tptr = tptr->next; - - } /* End while loop -- did an entire traceback. */ + } VTR_ASSERT(connections_inf.get_remaining_targets().empty()); @@ -2001,28 +1894,28 @@ static bool is_high_fanout(int fanout, int fanout_threshold) { return true; } -//In heavily congested designs a static bounding box (BB) can -//become problematic for routability (it effectively enforces a -//hard blockage restricting where a net can route). +// In heavily congested designs a static bounding box (BB) can +// become problematic for routability (it effectively enforces a +// hard blockage restricting where a net can route). // -//For instance, the router will try to route non-critical connections -//away from congested regions, but may end up hitting the edge of the -//bounding box. Limiting how far out-of-the-way it can be routed, and -//preventing congestion from resolving. +// For instance, the router will try to route non-critical connections +// away from congested regions, but may end up hitting the edge of the +// bounding box. Limiting how far out-of-the-way it can be routed, and +// preventing congestion from resolving. // -//To alleviate this, we dynamically expand net bounding boxes if the net's -//*current* routing uses RR nodes 'close' to the edge of it's bounding box. +// To alleviate this, we dynamically expand net bounding boxes if the net's +// *current* routing uses RR nodes 'close' to the edge of it's bounding box. // -//The result is that connections trying to move out of the way and hitting -//their BB will have their bounding boxes will expand slowly in that direction. -//This helps spread out regions of heavy congestion (over several routing -//iterations). +// The result is that connections trying to move out of the way and hitting +// their BB will have their bounding boxes will expand slowly in that direction. +// This helps spread out regions of heavy congestion (over several routing +// iterations). // -//By growing the BBs slowly and only as needed we minimize the size of the BBs. -//This helps keep the router's graph search fast. +// By growing the BBs slowly and only as needed we minimize the size of the BBs. +// This helps keep the router's graph search fast. // -//Typically, only a small minority of nets (typically > 10%) have their BBs updated -//each routing iteration. +// Typically, only a small minority of nets (typically > 10%) have their BBs updated +// each routing iteration. size_t dynamic_update_bounding_boxes(const std::vector& updated_nets, const Netlist<>& net_list, int high_fanout_threshold) { @@ -2047,9 +1940,8 @@ size_t dynamic_update_bounding_boxes(const std::vector& updated_net size_t num_bb_updated = 0; for (ParentNetId net : updated_nets) { - t_trace* routing_head = route_ctx.trace[net].head; - - if (routing_head == nullptr) continue; //Skip if no routing + if (!route_ctx.route_trees[net]) + continue; // Skip if no routing //We do not adjust the bounding boxes of high fanout nets, since they //use different bounding boxes based on the target location. @@ -2057,8 +1949,7 @@ size_t dynamic_update_bounding_boxes(const std::vector& updated_net //This ensures that the delta values calculated below are always non-negative if (is_high_fanout(net_list.net_sinks(net).size(), high_fanout_threshold)) continue; - t_bb curr_bb = calc_current_bb(routing_head); - + t_bb curr_bb = calc_current_bb(route_ctx.route_trees[net].value()); t_bb& router_bb = route_ctx.route_bb[net]; //Calculate the distances between the net's used RR nodes and @@ -2106,7 +1997,7 @@ size_t dynamic_update_bounding_boxes(const std::vector& updated_net } //Returns the bounding box of a net's used routing resources -static t_bb calc_current_bb(const t_trace* head) { +static t_bb calc_current_bb(const RouteTree& tree) { auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; auto& grid = device_ctx.grid; @@ -2117,16 +2008,15 @@ static t_bb calc_current_bb(const t_trace* head) { bb.xmax = 0; bb.ymax = 0; - for (const t_trace* elem = head; elem != nullptr; elem = elem->next) { - const t_rr_node& node = device_ctx.rr_graph.rr_nodes()[elem->index]; + for (auto& rt_node : tree.all_nodes()) { //The router interprets RR nodes which cross the boundary as being //'within' of the BB. Only those which are *strictly* out side the //box are excluded, hence we use the nodes xhigh/yhigh for xmin/xmax, //and xlow/ylow for xmax/ymax calculations - bb.xmin = std::min(bb.xmin, rr_graph.node_xhigh(node.id())); - bb.ymin = std::min(bb.ymin, rr_graph.node_yhigh(node.id())); - bb.xmax = std::max(bb.xmax, rr_graph.node_xlow(node.id())); - bb.ymax = std::max(bb.ymax, rr_graph.node_ylow(node.id())); + bb.xmin = std::min(bb.xmin, rr_graph.node_xhigh(rt_node.inode)); + bb.ymin = std::min(bb.ymin, rr_graph.node_yhigh(rt_node.inode)); + bb.xmax = std::max(bb.xmax, rr_graph.node_xlow(rt_node.inode)); + bb.ymax = std::max(bb.ymax, rr_graph.node_ylow(rt_node.inode)); } VTR_ASSERT(bb.xmin <= bb.xmax); @@ -2186,15 +2076,15 @@ bool should_setup_lower_bound_connection_delays(int itry, const t_router_opts& / return false; } -static bool is_better_quality_routing(const vtr::vector& best_routing, +static bool is_better_quality_routing(const vtr::vector>& best_routing, const RoutingMetrics& best_routing_metrics, const WirelengthInfo& wirelength_info, std::shared_ptr timing_info) { if (best_routing.empty()) { - return true; //First legal routing + return true; // First legal routing } - //Rank first based on sWNS, followed by other timing metrics + // Rank first based on sWNS, followed by other timing metrics if (timing_info) { if (timing_info->setup_worst_negative_slack() > best_routing_metrics.sWNS) { return true; @@ -2221,7 +2111,7 @@ static bool is_better_quality_routing(const vtr::vector timing_info, const RoutingMetrics& best_routing_metrics) { - //Give-up on reconvergent routing if the CPD improvement after the - //first iteration since convergence is small, compared to the best - //CPD seen so far + // Give-up on reconvergent routing if the CPD improvement after the + // first iteration since convergence is small, compared to the best + // CPD seen so far if (itry_since_last_convergence == 1) { float cpd_ratio = timing_info->setup_worst_negative_slack() / best_routing_metrics.sWNS; - //Give up if we see less than a 1% CPD improvement, - //after reducing pres_fac. Typically larger initial - //improvements are needed to see an actual improvement - //in final legal routing quality. + // Give up if we see less than a 1% CPD improvement, + // after reducing pres_fac. Typically larger initial + // improvements are needed to see an actual improvement + // in final legal routing quality. if (cpd_ratio >= router_opts.reconvergence_cpd_threshold) { VTR_LOG("Giving up routing since additional routing convergences seem unlikely to improve quality (CPD ratio: %g)\n", cpd_ratio); - return true; //Potential CPD improvement is small, don't spend run-time trying to improve it + return true; // Potential CPD improvement is small, don't spend run-time trying to improve it } } - return false; //Don't give up + return false; // Don't give up } static void generate_route_timing_reports(const t_router_opts& router_opts, @@ -2265,50 +2155,31 @@ static void generate_route_timing_reports(const t_router_opts& router_opts, } // If a route is ripped up during routing, non-configurable sets are left -// behind. As a result, the final routing may have stubs at -// non-configurable sets. This function tracks non-configurable set usage, +// behind. As a result, the final routing may have stubs at +// non-configurable sets. This function tracks non-configurable set usage, // and if the sets are unused, prunes them. static void prune_unused_non_configurable_nets(CBRR& connections_inf, - const Netlist<>& net_list, - bool is_flat) { + const Netlist<>& net_list) { auto& device_ctx = g_vpr_ctx.device(); - auto& route_ctx = g_vpr_ctx.routing(); + auto& route_ctx = g_vpr_ctx.mutable_routing(); std::vector non_config_node_set_usage(device_ctx.rr_non_config_node_sets.size(), 0); for (auto net_id : net_list.nets()) { - connections_inf.prepare_routing_for_net(net_id); - connections_inf.clear_force_reroute_for_net(); - - std::fill(non_config_node_set_usage.begin(), non_config_node_set_usage.end(), 0); - t_rt_node* rt_root = traceback_to_route_tree(net_id, &non_config_node_set_usage, is_flat); - if (rt_root == nullptr) { + if (!route_ctx.route_trees[net_id]) continue; - } - - //Sanity check that route tree and traceback are equivalent before pruning - VTR_ASSERT(verify_traceback_route_tree_equivalent( - route_ctx.trace[net_id].head, rt_root)); - - // check for edge correctness - VTR_ASSERT_SAFE(is_valid_skeleton_tree(rt_root)); + RouteTree& tree = route_ctx.route_trees[net_id].value(); - //Prune the branches of the tree that don't legally lead to sinks - rt_root = prune_route_tree(rt_root, connections_inf, - &non_config_node_set_usage); - - // Free old traceback. - free_traceback(net_id); + connections_inf.prepare_routing_for_net(net_id); + connections_inf.clear_force_reroute_for_net(); - // Update traceback with pruned tree. - auto& reached_rt_sinks = connections_inf.get_reached_rt_sinks(); - traceback_from_route_tree(net_id, rt_root, reached_rt_sinks.size()); - VTR_ASSERT(verify_traceback_route_tree_equivalent(route_ctx.trace[net_id].head, rt_root)); + std::vector usage = tree.get_non_config_node_set_usage(); - free_route_tree(rt_root); + // Prune the branches of the tree that don't legally lead to sinks + tree.prune(connections_inf, &usage); } } -//Initializes net_delay based on best-case delay estimates from the router lookahead +// Initializes net_delay based on best-case delay estimates from the router lookahead static void init_net_delay_from_lookahead(const RouterLookahead& router_lookahead, const Netlist<>& net_list, const vtr::vector>& net_rr_terminals, @@ -2316,7 +2187,7 @@ static void init_net_delay_from_lookahead(const RouterLookahead& router_lookahea const RRGraphView& rr_graph, bool is_flat) { t_conn_cost_params cost_params; - cost_params.criticality = 1.; //Ensures lookahead returns delay value + cost_params.criticality = 1.; // Ensures lookahead returns delay value for (auto net_id : net_list.nets()) { if (net_list.net_is_ignored(net_id)) continue; @@ -2466,8 +2337,8 @@ vtr::vector>> set_net } #ifndef NO_GRAPHICS -//updates router iteration information and checks for router iteration and net id breakpoints -//stops after the specified router iteration or net id is encountered +// updates router iteration information and checks for router iteration and net id breakpoints +// stops after the specified router iteration or net id is encountered void update_router_info_and_check_bp(bp_router_type type, int net_id) { t_draw_state* draw_state = get_draw_state_vars(); if (draw_state->list_of_breakpoints.size() != 0) { diff --git a/vpr/src/route/route_timing.h b/vpr/src/route/route_timing.h index 543529df236..6f4943eca4f 100644 --- a/vpr/src/route/route_timing.h +++ b/vpr/src/route/route_timing.h @@ -2,6 +2,7 @@ #include #include #include "connection_based_routing.h" +#include "netlist.h" #include "vpr_types.h" #include "vpr_utils.h" @@ -39,9 +40,9 @@ bool try_timing_driven_route_net(ConnectionRouter& router, const t_router_opts& router_opts, CBRR& connections_inf, RouterStats& router_stats, - float* pin_criticality, - t_rt_node** rt_node_of_sink, - NetPinsMatrix& net_delay, + std::vector& pin_criticality, + std::vector>& rt_node_of_sink, + ClbNetPinsMatrix& net_delay, const ClusteredPinAtomPinsLookup& netlist_pin_lookup, std::shared_ptr timing_info, NetPinTimingInvalidator* pin_timing_invalidator, @@ -61,8 +62,8 @@ bool timing_driven_route_net(ConnectionRouter& router, const t_router_opts& router_opts, CBRR& connections_inf, RouterStats& router_stats, - float* pin_criticality, - t_rt_node** rt_node_of_sink, + std::vector& pin_criticality, + std::vector>& rt_node_of_sink, float* net_delay, const ClusteredPinAtomPinsLookup& netlist_pin_lookup, std::shared_ptr timing_info, @@ -73,33 +74,30 @@ bool timing_driven_route_net(ConnectionRouter& router, const std::vector>& choking_spots, bool is_flat); -void alloc_timing_driven_route_structs(float** pin_criticality_ptr, - int** sink_order_ptr, - t_rt_node*** rt_node_of_sink_ptr, - int max_sinks); - -void free_timing_driven_route_structs(float* pin_criticality, int* sink_order, t_rt_node** rt_node_of_sink); - void enable_router_debug(const t_router_opts& router_opts, ParentNetId net, int sink_rr, int router_iteration, ConnectionRouterInterface* router); bool is_iteration_complete(bool routing_is_feasible, const t_router_opts& router_opts, int itry, std::shared_ptr timing_info, bool rcv_finished); bool should_setup_lower_bound_connection_delays(int itry, const t_router_opts& router_opts); -std::vector timing_driven_find_all_shortest_paths_from_route_tree(t_rt_node* rt_root, - const t_conn_cost_params cost_params, - t_bb bounding_box, - std::vector& modified_rr_node_inf, - RouterStats& router_stats); +void update_rr_base_costs(int fanout); + +/* Data while timing driven route is active */ +class timing_driven_route_structs { + public: + std::vector pin_criticality; /* [1..max_pins_per_net-1] */ + std::vector sink_order; /* [1..max_pins_per_net-1] */ + std::vector> rt_node_of_sink; /* [1..max_pins_per_net-1] */ -struct timing_driven_route_structs { - // data while timing driven route is active - float* pin_criticality; /* [1..max_pins_per_net-1] */ - int* sink_order; /* [1..max_pins_per_net-1] */ - t_rt_node** rt_node_of_sink; /* [1..max_pins_per_net-1] */ + timing_driven_route_structs(const Netlist<>& net_list) { + int max_sinks = std::max(get_max_pins_per_net(net_list) - 1, 0); + pin_criticality.resize(max_sinks + 1); + sink_order.resize(max_sinks + 1); + rt_node_of_sink.resize(max_sinks + 1); - timing_driven_route_structs(int max_sinks); - ~timing_driven_route_structs(); + /* Set element 0 to invalid values */ + pin_criticality[0] = std::numeric_limits::quiet_NaN(); + sink_order[0] = -1; + rt_node_of_sink[0] = vtr::nullopt; + } }; - -void update_rr_base_costs(int fanout); diff --git a/vpr/src/route/route_traceback.cpp b/vpr/src/route/route_traceback.cpp deleted file mode 100644 index 7f4ad67f72b..00000000000 --- a/vpr/src/route/route_traceback.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "route_traceback.h" -#include "route_common.h" -#include "vpr_types.h" //For t_trace - -t_traceback::t_traceback(const t_traceback& other) { - VTR_ASSERT((other.head == nullptr && other.tail == nullptr) || (other.head && other.tail && other.tail->next == nullptr)); - - //Deep-copy of traceback - t_trace* prev = nullptr; - for (t_trace* other_curr = other.head; other_curr; other_curr = other_curr->next) { - //VTR_LOG("Copying trace %p node: %d switch: %d pin(for sink): %d\n", other_curr, other_curr->index, other_curr->iswitch. other_curr->net_pin_index); - t_trace* curr = alloc_trace_data(); - - curr->index = other_curr->index; - curr->net_pin_index = other_curr->net_pin_index; - curr->iswitch = other_curr->iswitch; - - if (prev) { - prev->next = curr; - } else { - head = curr; - } - prev = curr; - } - - //We may have gotten the last t_trace element from a free list, - //make sure it's next ptr gets set to null - if (prev) { - prev->next = nullptr; - } - - tail = prev; -} - -t_traceback::~t_traceback() { - free_traceback(head); -} - -t_traceback::t_traceback(t_traceback&& other) - : t_traceback() { - //Copy-swap - swap(*this, other); -} - -t_traceback t_traceback::operator=(t_traceback other) { - //Copy-swap - swap(*this, other); - return *this; -} - -void swap(t_traceback& first, t_traceback& second) { - using std::swap; - - swap(first.head, second.head); - swap(first.tail, second.tail); -} diff --git a/vpr/src/route/route_traceback.h b/vpr/src/route/route_traceback.h deleted file mode 100644 index ddf7d056932..00000000000 --- a/vpr/src/route/route_traceback.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef VPR_TRACEBACK_H -#define VPR_TRACEBACK_H - -struct t_trace; //Forward declaration - -struct t_traceback { - t_traceback() = default; - ~t_traceback(); - - t_traceback(const t_traceback&); - t_traceback(t_traceback&&); - t_traceback operator=(t_traceback); - - friend void swap(t_traceback& first, t_traceback& second); - - t_trace* head = nullptr; - t_trace* tail = nullptr; -}; - -#endif diff --git a/vpr/src/route/route_tree.cpp b/vpr/src/route/route_tree.cpp new file mode 100644 index 00000000000..033039d9b6f --- /dev/null +++ b/vpr/src/route/route_tree.cpp @@ -0,0 +1,844 @@ +#include "route_tree.h" +#include "globals.h" +#include "netlist_fwd.h" +#include "route_timing.h" +#include "rr_graph_fwd.h" +#include "vtr_math.h" + +/* Construct a new RouteTreeNode. + * Doesn't add the node to parent's child_nodes! (see add_child) */ +RouteTreeNode::RouteTreeNode(RRNodeId _inode, RRSwitchId _parent_switch, RouteTreeNode* parent) + : inode(_inode) + , parent_switch(_parent_switch) + , _parent(parent) { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + + re_expand = true; + net_pin_index = OPEN; + C_downstream = rr_graph.node_C(_inode); + R_upstream = rr_graph.node_R(_inode); + Tdel = 0.5 * R_upstream * C_downstream; + + _next = nullptr; + _prev = nullptr; + _subtree_end = nullptr; + _is_leaf = true; +} + +/** Print information about this subtree to stdout. */ +void RouteTreeNode::print(void) const { + print_x(0); +} + +/** Helper function for print. */ +void RouteTreeNode::print_x(int depth) const { + std::string indent; + for (int i = 0; i < depth; ++i) { + indent += " "; + } + + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + VTR_LOG("%srt_node: %d (%s) \t ipin: %d \t R: %g \t C: %g \t delay: %g \t", + indent.c_str(), + inode, + rr_graph.node_type_string(inode), + net_pin_index, + R_upstream, + C_downstream, + Tdel); + + if (_parent) { + VTR_LOG("parent: %d \t parent_switch: %d", _parent->inode, parent_switch); + bool parent_edge_configurable = rr_graph.rr_switch_inf(parent_switch).configurable(); + if (!parent_edge_configurable) { + VTR_LOG("*"); + } + } + + auto& route_ctx = g_vpr_ctx.routing(); + if (route_ctx.rr_node_route_inf[size_t(inode)].occ() > rr_graph.node_capacity(inode)) { + VTR_LOG(" x"); + } + + VTR_LOG("\n"); + + for (auto& child : child_nodes()) { + child.print_x(depth + 1); + } +} + +/* Construct a top-level route tree. */ +RouteTree::RouteTree(RRNodeId _inode) { + _root = new RouteTreeNode(_inode, RRSwitchId::INVALID(), nullptr); + _rr_node_to_rt_node[_inode] = _root; +} + +RouteTree::RouteTree(ParentNetId _inet) { + auto& route_ctx = g_vpr_ctx.routing(); + RRNodeId inode = RRNodeId(route_ctx.net_rr_terminals[_inet][0]); + _root = new RouteTreeNode(inode, RRSwitchId::INVALID(), nullptr); + _rr_node_to_rt_node[inode] = _root; +} + +/** Make a copy of rhs and return it. + * Traverse it as a tree so we can keep parent & child ptrs valid. */ +RouteTreeNode* RouteTree::copy_tree(const RouteTreeNode* rhs) { + RouteTreeNode* root = new RouteTreeNode(rhs->inode, RRSwitchId::INVALID(), nullptr); + _rr_node_to_rt_node[root->inode] = root; + copy_tree_x(root, *rhs); + return root; +} + +/* Helper for copy_list: copy child nodes of rhs into lhs */ +void RouteTree::copy_tree_x(RouteTreeNode* lhs, const RouteTreeNode& rhs) { + for (auto& rchild : rhs.child_nodes()) { + RouteTreeNode* child = new RouteTreeNode(rchild); + child->_is_leaf = true; + add_node(lhs, child); + copy_tree_x(child, rchild); + } +} + +/* Copy constructor */ +RouteTree::RouteTree(const RouteTree& rhs) { + _root = copy_tree(rhs._root); +} + +/* Move constructor: + * Take over rhs' linked list & set it to null so it doesn't get freed. + * Refs should stay valid after this? */ +RouteTree::RouteTree(RouteTree&& rhs) { + _root = rhs._root; + rhs._root = nullptr; + _rr_node_to_rt_node = std::move(rhs._rr_node_to_rt_node); +} + +/* Copy assignment: free list, clear lookup, reload list. */ +RouteTree& RouteTree::operator=(const RouteTree& rhs) { + if (this == &rhs) + return *this; + free_list(_root); + _rr_node_to_rt_node.clear(); + _root = copy_tree(rhs._root); + return *this; +} + +/* Move assignment: + * Free my list, take over rhs' linked list & set it to null so it doesn't get freed. + * Also ~steal~ acquire ownership of node lookup from rhs. + * Refs should stay valid after this? */ +RouteTree& RouteTree::operator=(RouteTree&& rhs) { + if (this == &rhs) + return *this; + free_list(_root); + _root = rhs._root; + rhs._root = nullptr; + _rr_node_to_rt_node = std::move(rhs._rr_node_to_rt_node); + return *this; +} + +/** Reload timing values (R_upstream, C_downstream, Tdel). + * Can take a RouteTreeNode& to do an incremental update. + * Note that update_from_heap already calls this. */ +void RouteTree::reload_timing(vtr::optional from_node) { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + + if (!from_node) + from_node = *_root; + + // Propagate R_upstream down into the new subtree + load_new_subtree_R_upstream(*from_node); + + // Propagate C_downstream up from new subtree sinks to subtree root + load_new_subtree_C_downstream(*from_node); + + // Propagate C_downstream up from the subtree root + RouteTreeNode& unbuffered_subtree_rt_root = update_unbuffered_ancestors_C_downstream(*from_node); + + float Tdel_start = 0; + + if (unbuffered_subtree_rt_root._parent) { + RouteTreeNode& subtree_parent_rt_node = *unbuffered_subtree_rt_root._parent; + Tdel_start = subtree_parent_rt_node.Tdel; + RRSwitchId iswitch = unbuffered_subtree_rt_root.parent_switch; + /* TODO Just a note (no action needed for this PR): In future, we need to consider APIs that returns + * the Tdel for a routing trace in RRGraphView.*/ + Tdel_start += rr_graph.rr_switch_inf(iswitch).R * unbuffered_subtree_rt_root.C_downstream; + Tdel_start += rr_graph.rr_switch_inf(iswitch).Tdel; + } + + load_route_tree_Tdel(unbuffered_subtree_rt_root, Tdel_start); +} + +/** Sets the R_upstream values of all the nodes in the new path to the + * correct value by traversing down to SINK from from_node. */ +void RouteTree::load_new_subtree_R_upstream(RouteTreeNode& rt_node) { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + + RouteTreeNode* parent_rt_node = rt_node._parent; + RRNodeId inode = rt_node.inode; + + //Calculate upstream resistance + float R_upstream = 0.; + if (parent_rt_node) { + RRSwitchId iswitch = rt_node.parent_switch; + bool switch_buffered = rr_graph.rr_switch_inf(iswitch).buffered(); + + if (!switch_buffered) { + R_upstream += parent_rt_node->R_upstream; //Parent upstream R + } + R_upstream += rr_graph.rr_switch_inf(iswitch).R; //Parent switch R + } + R_upstream += rr_graph.node_R(inode); //Current node R + + rt_node.R_upstream = R_upstream; + + //Update children + for (RouteTreeNode& child : rt_node._child_nodes()) { + load_new_subtree_R_upstream(child); + } +} + +/** Sets the R_upstream values of all the nodes in the new path to the + * correct value by traversing down to SINK from from_node. */ +float RouteTree::load_new_subtree_C_downstream(RouteTreeNode& from_node) { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + + float C_downstream = 0.; + C_downstream += rr_graph.node_C(from_node.inode); + for (RouteTreeNode& child : from_node._child_nodes()) { + /* When switches such as multiplexers and tristate buffers are enabled, their fanout + * produces an "internal capacitance". We account for this internal capacitance of the + * switch by adding it to the total capacitance of the node. */ + C_downstream += rr_graph.rr_switch_inf(child.parent_switch).Cinternal; + float C_downstream_child = load_new_subtree_C_downstream(child); + if (!rr_graph.rr_switch_inf(child.parent_switch).buffered()) { + C_downstream += C_downstream_child; + } + } + + from_node.C_downstream = C_downstream; + return C_downstream; +} + +/** Update the C_downstream values for the ancestors of from_node. Once + * a buffered switch is found amongst the ancestors, no more ancestors are + * affected. Returns the root of the "unbuffered subtree" whose Tdel + * values are affected by the new path's addition. */ +RouteTreeNode& +RouteTree::update_unbuffered_ancestors_C_downstream(RouteTreeNode& from_node) { + if (!from_node.parent()) + return from_node; + + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + + RRSwitchId iswitch = from_node.parent_switch; + + /* Now that a connection has been made between rt_node and its parent we must also consider + * the potential effect of internal capacitance. We will first assume that parent is connected + * by an unbuffered switch, so the ancestors downstream capacitance must be equal to the sum + * of the child's downstream capacitance with the internal capacitance of the switch.*/ + + float C_downstream_addition = from_node.C_downstream + rr_graph.rr_switch_inf(iswitch).Cinternal; + + /* Having set the value of C_downstream_addition, we must check whether the parent switch + * is a buffered or unbuffered switch with the if statement below. If the parent switch is + * a buffered switch, then the parent node's downsteam capacitance is increased by the + * value of the parent switch's internal capacitance in the if statement below. + * Correspondingly, the ancestors' downstream capacitance will be updated by the same + * value through the while loop. Otherwise, if the parent switch is unbuffered, then + * the if statement will be ignored, and the parent and ancestral nodes' downstream + * capacitance will be increased by the sum of the child's downstream capacitance with + * the internal capacitance of the parent switch in the while loop.*/ + + RouteTreeNode* last_node = std::addressof(from_node); + RouteTreeNode* parent_rt_node = from_node._parent; + + if (rr_graph.rr_switch_inf(iswitch).buffered() == true) { + C_downstream_addition = rr_graph.rr_switch_inf(iswitch).Cinternal; + last_node = parent_rt_node; + last_node->C_downstream += C_downstream_addition; + parent_rt_node = last_node->_parent; + iswitch = last_node->parent_switch; + } + + while (parent_rt_node && rr_graph.rr_switch_inf(iswitch).buffered() == false) { + last_node = parent_rt_node; + last_node->C_downstream += C_downstream_addition; + parent_rt_node = last_node->_parent; + iswitch = last_node->parent_switch; + } + + VTR_ASSERT(last_node); + return *last_node; +} + +/** Updates the Tdel values of the subtree rooted at rt_node by + * by calling itself recursively. The C_downstream values of all the nodes + * must be correct before this routine is called. Tarrival is the time at + * at which the signal arrives at this node's *input*. */ +void RouteTree::load_route_tree_Tdel(RouteTreeNode& from_node, float Tarrival) { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + + RRNodeId inode = from_node.inode; + float Tdel, Tchild; + + /* Assuming the downstream connections are, on average, connected halfway + * along a wire segment's length. See discussion in net_delay.cpp if you want + * to change this. */ + Tdel = Tarrival + 0.5 * from_node.C_downstream * rr_graph.node_R(inode); + from_node.Tdel = Tdel; + + /* Now expand the children of this node to load their Tdel values */ + for (RouteTreeNode& child : from_node._child_nodes()) { + RRSwitchId iswitch = child.parent_switch; + + Tchild = Tdel + rr_graph.rr_switch_inf(iswitch).R * child.C_downstream; + Tchild += rr_graph.rr_switch_inf(iswitch).Tdel; /* Intrinsic switch delay. */ + load_route_tree_Tdel(child, Tchild); + } +} + +vtr::optional RouteTree::find_by_rr_id(RRNodeId rr_node) const { + auto it = _rr_node_to_rt_node.find(rr_node); + if (it != _rr_node_to_rt_node.end()) { + return *it->second; + } + return vtr::nullopt; +} + +/** Check the consistency of this route tree. Looks for: + * - invalid parent-child links + * - invalid timing values + * - congested SINKs + * Returns true if OK. */ +bool RouteTree::is_valid(void) const { + return is_valid_x(*_root); +} + +/** Helper for is_valid */ +bool RouteTree::is_valid_x(const RouteTreeNode& rt_node) const { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + auto& route_ctx = g_vpr_ctx.routing(); + + constexpr float CAP_REL_TOL = 1e-6; + constexpr float CAP_ABS_TOL = vtr::DEFAULT_ABS_TOL; + constexpr float RES_REL_TOL = 1e-6; + constexpr float RES_ABS_TOL = vtr::DEFAULT_ABS_TOL; + + RRNodeId inode = rt_node.inode; + RRSwitchId iswitch = rt_node.parent_switch; + if (rt_node.parent()) { + if (rr_graph.rr_switch_inf(iswitch).buffered()) { + float R_upstream_check = rr_graph.node_R(inode) + rr_graph.rr_switch_inf(iswitch).R; + if (!vtr::isclose(rt_node.R_upstream, R_upstream_check, RES_REL_TOL, RES_ABS_TOL)) { + VTR_LOG("%d mismatch R upstream %e supposed %e\n", inode, rt_node.R_upstream, R_upstream_check); + return false; + } + } else { + float R_upstream_check = rr_graph.node_R(inode) + rt_node.parent().value().R_upstream + rr_graph.rr_switch_inf(iswitch).R; + if (!vtr::isclose(rt_node.R_upstream, R_upstream_check, RES_REL_TOL, RES_ABS_TOL)) { + VTR_LOG("%d mismatch R upstream %e supposed %e\n", inode, rt_node.R_upstream, R_upstream_check); + return false; + } + } + } else if (rt_node.R_upstream != rr_graph.node_R(inode)) { + VTR_LOG("%d mismatch R upstream %e supposed %e\n", inode, rt_node.R_upstream, rr_graph.node_R(inode)); + return false; + } + + if (rr_graph.node_type(inode) == SINK) { // sink, must not be congested and must not have fanouts + int occ = route_ctx.rr_node_route_inf[size_t(inode)].occ(); + int capacity = rr_graph.node_capacity(inode); + if (rt_node._next != nullptr && rt_node._next->_parent == &rt_node) { + VTR_LOG("SINK %d has fanouts?\n", inode); + return false; + } + if (!rt_node.is_leaf()) { + VTR_LOG("SINK %d has fanouts?\n", inode); + return false; + } + if (occ > capacity) { + VTR_LOG("SINK %d occ %d > cap %d\n", inode, occ, capacity); + return false; + } + } + + // check downstream C + float C_downstream_children = 0; + for (auto& child : rt_node.child_nodes()) { + if (child._parent != std::addressof(rt_node)) { + VTR_LOG("parent-child relationship not mutually acknowledged by parent %d->%d child %d<-%d\n", + inode, child.inode, + child.inode, rt_node.inode); + return false; + } + C_downstream_children += rr_graph.rr_switch_inf(child.parent_switch).Cinternal; + + if (!rr_graph.rr_switch_inf(child.parent_switch).buffered()) { + C_downstream_children += child.C_downstream; + } + if (!is_valid_x(child)) { + VTR_LOG("subtree %d invalid, propagating up\n", child.inode); + return false; + } + } + + float C_downstream_check = C_downstream_children + rr_graph.node_C(inode); + if (!vtr::isclose(rt_node.C_downstream, C_downstream_check, CAP_REL_TOL, CAP_ABS_TOL)) { + VTR_LOG("%d mismatch C downstream %e supposed %e\n", inode, rt_node.C_downstream, C_downstream_check); + return false; + } + + return true; +} + +/** Check if the tree has any overused nodes (-> the tree is congested). + * Returns true if not congested */ +bool RouteTree::is_uncongested(void) const { + return is_uncongested_x(*_root); +} + +/** Helper for is_uncongested */ +bool RouteTree::is_uncongested_x(const RouteTreeNode& rt_node) const { + auto& route_ctx = g_vpr_ctx.routing(); + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + + RRNodeId inode = rt_node.inode; + if (route_ctx.rr_node_route_inf[size_t(inode)].occ() > rr_graph.node_capacity(RRNodeId(inode))) { + //This node is congested + return false; + } + + for (auto& child : rt_node.child_nodes()) { + if (!is_uncongested_x(child)) { + // The sub-tree connected to this edge is congested + return false; + } + } + + // The sub-tree below the current node is uncongested + return true; +} + +/** Print information about this route tree to stdout. */ +void RouteTree::print(void) const { + _root->print(); +} + +/** Add the most recently finished wire segment to the routing tree, and + * update the Tdel, etc. numbers for the rest of the routing tree. hptr + * is the heap pointer of the SINK that was reached, and target_net_pin_index + * is the net pin index corresponding to the SINK that was reached. This routine + * returns a tuple: RouteTreeNode of the branch it adds to the route tree and + * RouteTreeNode of the SINK it adds to the routing. */ +std::tuple, vtr::optional> +RouteTree::update_from_heap(t_heap* hptr, int target_net_pin_index, SpatialRouteTreeLookup* spatial_rt_lookup, bool is_flat) { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + + //Create a new subtree from the target in hptr to existing routing + vtr::optional start_of_new_subtree_rt_node, sink_rt_node; + std::tie(start_of_new_subtree_rt_node, sink_rt_node) = add_subtree_from_heap(hptr, target_net_pin_index, is_flat); + + if (!start_of_new_subtree_rt_node) + return {vtr::nullopt, *sink_rt_node}; + + /* Reload timing values */ + reload_timing(start_of_new_subtree_rt_node); + + if (spatial_rt_lookup) { + update_route_tree_spatial_lookup_recur(*start_of_new_subtree_rt_node, *spatial_rt_lookup); + } + + /* if the new branch is the only child of its parent and the parent is a SOURCE, + * it is the first time we are creating this tree, so include the parent in the new branch return + * so that it can be included in occupancy calculation. + * TODO: probably this should be cleaner */ + RouteTreeNode* parent = start_of_new_subtree_rt_node->_parent; + if (start_of_new_subtree_rt_node->_next_sibling == parent->_subtree_end && rr_graph.node_type(parent->inode) == SOURCE) + return {*parent, *sink_rt_node}; + + return {*start_of_new_subtree_rt_node, *sink_rt_node}; +} + +/* Adds the most recent wire segment, ending at the SINK indicated by hptr, + * to the routing tree. target_net_pin_index is the net pin index corresponding + * to the SINK indicated by hptr. Returns the first (most upstream) new rt_node, + * and the rt_node of the new SINK. Traverses up from SINK */ +std::tuple, vtr::optional> +RouteTree::add_subtree_from_heap(t_heap* hptr, int target_net_pin_index, bool is_flat) { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + auto& route_ctx = g_vpr_ctx.routing(); + + RRNodeId sink_inode = RRNodeId(hptr->index); + + /* Walk rr_node_route_inf up until we reach an existing RouteTreeNode */ + std::vector new_branch_inodes; + std::vector new_branch_iswitches; + + std::unordered_set all_visited; + std::unordered_set main_branch_visited; + + /* We need this information to build the branch: + * node -> switch -> node -> ... -> switch -> sink. + * Here we create two vectors: + * new_branch_inodes: [sink, nodeN-1, nodeN-2, ... node 1] of length N + * and new_branch_iswitches: [N-1->sink, N-2->N-1, ... 2->1, 1->found_node] of length N */ + RREdgeId edge = hptr->prev_edge(); + RRNodeId new_inode = RRNodeId(hptr->prev_node()); + RRSwitchId new_iswitch = RRSwitchId(rr_graph.rr_nodes().edge_switch(edge)); + + /* build a path, looking up rr nodes and switches from rr_node_route_inf */ + new_branch_inodes.push_back(sink_inode); + while (!_rr_node_to_rt_node.count(new_inode)) { + new_branch_inodes.push_back(new_inode); + new_branch_iswitches.push_back(new_iswitch); + edge = route_ctx.rr_node_route_inf[size_t(new_inode)].prev_edge; + new_inode = RRNodeId(route_ctx.rr_node_route_inf[size_t(new_inode)].prev_node); + new_iswitch = RRSwitchId(rr_graph.rr_nodes().edge_switch(edge)); + } + new_branch_iswitches.push_back(new_iswitch); + + /* Build the new tree branch starting from the existing node we found */ + RouteTreeNode* last_node = _rr_node_to_rt_node[new_inode]; + all_visited.insert(last_node->inode); + + /* In the code below I'm marking SINKs and IPINs as not to be re-expanded. + * It makes the code more efficient (though not vastly) to prune this way + * when there aren't route-throughs or ipin doglegs. + * --- + * Walk through new_branch_iswitches and corresponding new_branch_inodes. */ + for (int i = new_branch_inodes.size() - 1; i >= 0; i--) { + RouteTreeNode* new_node = new RouteTreeNode(new_branch_inodes[i], new_branch_iswitches[i], last_node); + add_node(last_node, new_node); + + e_rr_type node_type = rr_graph.node_type(new_branch_inodes[i]); + // If is_flat is enabled, IPINs should be added, since they are used for intra-cluster routing + if (node_type == IPIN && !is_flat) { + new_node->re_expand = false; + } else if (node_type == SINK) { + new_node->re_expand = false; + new_node->net_pin_index = target_net_pin_index; // net pin index is invalid for non-SINK nodes + } else { + new_node->re_expand = true; + } + + last_node = new_node; + + main_branch_visited.insert(new_branch_inodes[i]); + all_visited.insert(new_branch_inodes[i]); + } + + // Expand (recursively) each of the main-branch nodes adding any + // non-configurably connected nodes + // Sink is not included, so no need to pass in the node's ipin value. + for (RRNodeId rr_node : main_branch_visited) { + add_non_configurable_nodes(_rr_node_to_rt_node.at(rr_node), false, all_visited, is_flat); + } + + /* the first and last nodes we added. + * vec[size-1] works, because new_branch_inodes is guaranteed to contain at least [sink, found_node] */ + vtr::optional downstream_rt_node = *_rr_node_to_rt_node.at(new_branch_inodes[new_branch_inodes.size() - 1]); + vtr::optional sink_rt_node = *_rr_node_to_rt_node.at(new_branch_inodes[0]); + + return {downstream_rt_node, sink_rt_node}; +} + +/** Add non-configurable nodes reachable from rt_node to the tree. + * We keep non-configurable nodes in the routing if a connected non-conf + * node is in use. This function finds those nodes and adds them to the tree */ +void RouteTree::add_non_configurable_nodes(RouteTreeNode* rt_node, + bool reached_by_non_configurable_edge, + std::unordered_set& visited, + bool is_flat) { + RRNodeId rr_node = rt_node->inode; + + if (visited.count(rr_node) && reached_by_non_configurable_edge) + return; + + visited.insert(rr_node); + + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + + for (int iedge : rr_graph.non_configurable_edges(rr_node)) { + // Recursive case: expand children + VTR_ASSERT(!rr_graph.edge_is_configurable(rr_node, iedge)); + RRNodeId to_rr_node = rr_graph.edge_sink_node(rr_node, iedge); + + if (_rr_node_to_rt_node.count(to_rr_node)) // TODO: not 100% sure about this + continue; + + RRSwitchId edge_switch(rr_graph.edge_switch(rr_node, iedge)); + + RouteTreeNode* new_node = new RouteTreeNode(to_rr_node, edge_switch, rt_node); + add_node(rt_node, new_node); + + new_node->net_pin_index = OPEN; + if (rr_graph.node_type(to_rr_node) == IPIN && !is_flat) { + new_node->re_expand = false; + } else { + new_node->re_expand = true; + } + + add_non_configurable_nodes(new_node, true, visited, is_flat); + } +} + +/** Prune a route tree of illegal branches - when there is at least 1 congested node on the path to a sink + * Returns nullopt if the entire tree has been pruned. + * + * Note: does not update R_upstream/C_downstream */ +vtr::optional +RouteTree::prune(CBRR& connections_inf, std::vector* non_config_node_set_usage) { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + auto& route_ctx = g_vpr_ctx.routing(); + + VTR_ASSERT_MSG(rr_graph.node_type(root().inode) == SOURCE, "Root of route tree must be SOURCE"); + + VTR_ASSERT_MSG(route_ctx.rr_node_route_inf[size_t(root().inode)].occ() <= rr_graph.node_capacity(root().inode), + "Route tree root/SOURCE should never be congested"); + + auto pruned_node = prune_x(*_root, connections_inf, false, non_config_node_set_usage); + if (pruned_node) + return *this; + else + return vtr::nullopt; +} + +/** Helper for prune. + * Recursively traverse the route tree rooted at node and remove any congested subtrees. + * Returns nullopt if pruned */ +vtr::optional +RouteTree::prune_x(RouteTreeNode& rt_node, CBRR& connections_inf, bool force_prune, std::vector* non_config_node_set_usage) { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + auto& route_ctx = g_vpr_ctx.routing(); + bool congested = (route_ctx.rr_node_route_inf[size_t(rt_node.inode)].occ() > rr_graph.node_capacity(rt_node.inode)); + + int node_set = -1; + auto itr = device_ctx.rr_node_to_non_config_node_set.find(size_t(rt_node.inode)); + if (itr != device_ctx.rr_node_to_non_config_node_set.end()) { + node_set = itr->second; + } + + if (congested) { + //This connection is congested -- prune it + force_prune = true; + } + + if (connections_inf.should_force_reroute_connection(size_t(rt_node.inode))) { + //Forcibly re-route (e.g. to improve delay) + force_prune = true; + } + + // Recursively prune child nodes + bool all_children_pruned = true; + remove_child_if(rt_node, [&](auto& child) { + vtr::optional child_maybe = prune_x(child, connections_inf, force_prune, non_config_node_set_usage); + + if (child_maybe.has_value()) { // Not pruned + all_children_pruned = false; + return false; + } else { // Pruned + // After removing a child node, check if non_config_node_set_usage + // needs an update. + if (non_config_node_set_usage != nullptr && node_set != -1 && rr_graph.rr_switch_inf(child.parent_switch).configurable()) { + (*non_config_node_set_usage)[node_set] -= 1; + VTR_ASSERT((*non_config_node_set_usage)[node_set] >= 0); + } + return true; + } + }); + + if (rr_graph.node_type(rt_node.inode) == SINK) { + if (!force_prune) { + //Valid path to sink + + //Record sink as reachable + connections_inf.reached_rt_sink(rt_node.inode); + + return rt_node; // Not pruned + } else { + //Record as not reached + connections_inf.toreach_rr_sink(rt_node.net_pin_index); + + return vtr::nullopt; // Pruned + } + } else if (all_children_pruned) { + //This node has no children + // + // This can happen in three scenarios: + // 1) This node is being pruned. As a result any child nodes + // (subtrees) will have been pruned. + // + // 2) This node was reached by a non-configurable edge but + // was otherwise unused (forming a 'stub' off the main + // branch). + // + // 3) This node is uncongested, but all its connected sub-trees + // have been pruned. + // + // We take the corresponding actions: + // 1) Prune the node. + // + // 2) Prune the node only if the node set is unused or if force_prune + // is set. + // + // 3) Prune the node. + // + // This avoids the creation of unused 'stubs'. (For example if + // we left the stubs in and they were subsequently not used + // they would uselessly consume routing resources). + VTR_ASSERT(rt_node.is_leaf()); + + bool reached_non_configurably = false; + if (rt_node.parent()) { + reached_non_configurably = !rr_graph.rr_switch_inf(rt_node.parent_switch).configurable(); + + if (reached_non_configurably) { + // Check if this non-configurable node set is in use. + VTR_ASSERT(node_set != -1); + if (non_config_node_set_usage != nullptr && (*non_config_node_set_usage)[node_set] == 0) { + force_prune = true; + } + } + } + + if (reached_non_configurably && !force_prune) { + return rt_node; //Not pruned + } else { + return vtr::nullopt; //Pruned + } + + } else { + // If this node is: + // 1. Part of a non-configurable node set + // 2. The first node in the tree that is part of the non-configurable + // node set + // + // -- and -- + // + // 3. The node set is not active + // + // Then prune this node. + // + if (non_config_node_set_usage != nullptr && node_set != -1 && rr_graph.rr_switch_inf(rt_node.parent_switch).configurable() && (*non_config_node_set_usage)[node_set] == 0) { + // This node should be pruned, re-prune edges once more. + // + // If the following is true: + // + // - The node set is unused + // (e.g. (*non_config_node_set_usage)[node_set] == 0) + // - This particular node still had children + // (which is true by virtue of being in this else statement) + // + // Then that indicates that the node set became unused during the + // pruning. One or more of the children of this node will be + // pruned if prune_route_tree_recurr is called again, and + // eventually the whole node will be prunable. + // + // Consider the following graph: + // + // 1 -> 2 + // 2 -> 3 [non-configurable] + // 2 -> 4 [non-configurable] + // 3 -> 5 + // 4 -> 6 + // + // Assume that nodes 5 and 6 do not connect to a sink, so they + // will be pruned (as normal). When prune_route_tree_recurr + // visits 2 for the first time, node 3 or 4 will remain. This is + // because when prune_route_tree_recurr visits 3 or 4, it will + // not have visited 4 or 3 (respectively). As a result, either + // node 3 or 4 will not be pruned on the first pass, because the + // node set usage count will be > 0. However after + // prune_route_tree_recurr visits 2, 3 and 4, the node set usage + // will be 0, so everything can be pruned. + return prune_x(rt_node, connections_inf, /*force_prune=*/false, non_config_node_set_usage); + } + + //An unpruned intermediate node + VTR_ASSERT(!force_prune); + + return rt_node; //Not pruned + } +} + +/** Remove all sinks and mark the remaining nodes as un-expandable. + * This is used after routing a clock net. + * TODO: is this function doing anything? Try running without it */ +void RouteTree::freeze(void) { + return freeze_x(*_root); +} + +/** Helper function for freeze. */ +void RouteTree::freeze_x(RouteTreeNode& rt_node) { + auto& device_ctx = g_vpr_ctx.device(); + const auto& rr_graph = device_ctx.rr_graph; + + remove_child_if(rt_node, [&](RouteTreeNode& child) { + if (rr_graph.node_type(child.inode) == SINK) { + VTR_LOGV_DEBUG(f_router_debug, + "Removing sink %d from route tree\n", child.inode); + return true; + } else { + rt_node.re_expand = false; + VTR_LOGV_DEBUG(f_router_debug, + "unexpanding: %d in route tree\n", rt_node.inode); + freeze_x(child); + return false; + } + }); +} + +/* For each non-configurable node set, count the cases in the route tree where: + * 1. the node is a member of a nonconf set + * if not SINK: + * 2. and there *is* an outgoing edge (we are not at the end of a stub) + * 3. and that outgoing edge is a configurable switch + * if SINK: + * 2. and the incoming edge is a configurable switch + * (Note: the old code's comments mention that a "non-configurable edge" + * "to" a sink is a usage of the set, but the code used to check if the + * edge "from" the SINK, which shouldn't exist, was "configurable". This + * might be some faulty code / unnecessary check carried over.) */ +std::vector RouteTree::get_non_config_node_set_usage(void) const { + auto& device_ctx = g_vpr_ctx.device(); + std::vector usage(device_ctx.rr_non_config_node_sets.size(), 0); + + const auto& rr_to_nonconf = device_ctx.rr_node_to_non_config_node_set; + + for (auto& rt_node : all_nodes()) { + auto it = rr_to_nonconf.find(size_t(rt_node.inode)); + if (it == rr_to_nonconf.end()) + continue; + + if (device_ctx.rr_graph.node_type(rt_node.inode) == SINK) { + if (device_ctx.rr_graph.rr_switch_inf(rt_node.parent_switch).configurable()) { + usage[it->second] += 1; + } + continue; + } + + for (auto& child : rt_node.child_nodes()) { + if (device_ctx.rr_graph.rr_switch_inf(child.parent_switch).configurable()) { + usage[it->second] += 1; + } + } + } + + return usage; +} diff --git a/vpr/src/route/route_tree.h b/vpr/src/route/route_tree.h new file mode 100644 index 00000000000..3a1db5a0c6e --- /dev/null +++ b/vpr/src/route/route_tree.h @@ -0,0 +1,519 @@ +#pragma once + +/** + * @file + * @brief This file defines the RouteTree and RouteTreeNode, used to keep partial or full routing + * state and timing information for a net. + * + * Overview + * ======== + * A RouteTree holds a root RouteTreeNode and exposes top level operations on the tree, such as + * RouteTree::update_from_heap() and RouteTree::prune(). + * + * Routing itself is not done using this representation. The route tree is pushed to the heap with + * ConnectionRouterInterface::timing_driven_route_connection_from_route_tree() and the newly found path + * is committed via RouteTree::update_from_heap(). The timing data is updated with RouteTree::reload_timing() where required. + * + * Each net in the netlist given to the router has a single RouteTree, which is kept in RoutingContext::route_trees. + * + * Usage + * ===== + * + * A RouteTree either requires a RRNodeId or a ParentNetId (as the source node) to construct: + * + * RouteTree tree(inet); + * // ... + * + * RouteTrees cannot be manually updated. The only way to extend them is to first route a connection and then update from the resulting heap. + * + * std::tie(found_path, cheapest) = router.timing_driven_route_connection_from_route_tree(tree.root(), ...); + * if (found_path) + * std::tie(std::ignore, rt_node_of_sink) = tree.update_from_heap(&cheapest, ...); + * + * Congested paths in a tree can be pruned using RouteTree::prune(). Note that updates to a tree require an update to the global occupancy state via + * pathfinder_update_cost_from_route_tree(). In addition, RouteTree::prune() depends on this global data to find congestions, so the flow to + * prune a tree looks like this: + * + * RouteTree tree2 = tree; + * // Prune the copy (using congestion data before subtraction) + * vtr::optional pruned_tree2 = tree2.prune(connections_inf); + * + * // Subtract congestion using the non-pruned original + * pathfinder_update_cost_from_route_tree(tree.root(), -1); + * + * if (pruned_tree2) { // Partially pruned + * // Add back congestion for the pruned route tree + * pathfinder_update_cost_from_route_tree(pruned_tree2.value().root(), 1); + * ... + * } else { // Fully destroyed + * ... + * + * Most usage of RouteTree outside of the router requires iterating through existing routing. Both RouteTree and RouteTreeNode exposes functions to + * traverse the tree. + * + * To iterate over all nodes in the tree: + * + * RouteTree& tree = route_ctx.route_trees[inet].value(); + * + * for (auto& node: tree.all_nodes()) { + * // ... + * } + * + * This will walk the tree in depth-first order. Breadth-first traversal would require recursion: + * + * const RouteTreeNode& root = tree.root(); + * + * for (auto& child: root.child_nodes()) { + * // recurse... + * } + * + * To walk a node's subtree in depth-first order: + * + * for (auto& node: root.all_nodes()) { // doesn't include root! + * // ... + * } + * + * RouteTree::find_by_rr_id() finds the RouteTreeNode for a given RRNodeId. Note that RRNodeId isn't a unique + * key for SINK nodes and therefore an external lookup (generated from sink nodes returned by RouteTree::update_from_heap()) + * or a search may be required to find a certain SINK. + * + * When the occupancy and timing data is up to date, a tree can be sanity checked using RouteTree::is_valid(). + */ + +#include +#include +#include +#include + +#include "connection_based_routing_fwd.h" +#include "route_tree_fwd.h" +#include "vtr_assert.h" +#include "spatial_route_tree_lookup.h" +#include "vtr_optional.h" + +/** + * @brief A single route tree node + * + * Structure describing one node in a RouteTree. */ +class RouteTreeNode { + friend class RouteTree; + + public: + RouteTreeNode() = delete; + RouteTreeNode(const RouteTreeNode&) = default; + RouteTreeNode(RouteTreeNode&&) = default; + RouteTreeNode& operator=(const RouteTreeNode&) = default; + RouteTreeNode& operator=(RouteTreeNode&&) = default; + + /** This struct makes little sense outside the context of a RouteTree. + * This constructor is only public for compatibility purposes. */ + RouteTreeNode(RRNodeId inode, RRSwitchId parent_switch, RouteTreeNode* parent); + + /** ID of the rr_node that corresponds to this node. */ + RRNodeId inode; + /** Switch type driving this node (by its parent). */ + RRSwitchId parent_switch; + /** Should this node be put on the heap as part of the partial + * routing to act as a source for subsequent connections? */ + bool re_expand; + + private: + /** Is this a leaf? add_node and remove_child_if should be keeping this valid. + * Needed to know if _next points to the first child */ + bool _is_leaf = true; + + public: + /** Time delay for the signal to get from the net source to this node. + * Includes the time to go through this node. */ + float Tdel; + /** Total upstream resistance from this node to the net + * source, including any device_ctx.rr_nodes[].R of this node. */ + float R_upstream; + /** Total downstream capacitance from this node. That is, + * the total C of the subtree rooted at the current node, + * including the C of the current node. */ + float C_downstream; + /** Net pin index associated with the node. This value + * ranges from 1 to fanout [1..num_pins-1]. For cases when + * different speed paths are taken to the same SINK for + * different pins, inode cannot uniquely identify each SINK, + * so the net pin index guarantees an unique identification + * for each SINK node. For non-SINK nodes and for SINK + * nodes with no associated net pin index, (i.e. special + * SINKs like the source of a clock tree which do not + * correspond to an actual netlist connection), the value + * for this member should be set to OPEN (-1). */ + int net_pin_index; + + /** Iterator implementation for child_nodes(). Walks using + * _next_sibling ptrs. At the end of the child list, the ptr + * points up to where the parent's subtree ends, so we know where + * to stop */ + template + class RTIterator { + public: + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = RouteTreeNode; + using pointer = RouteTreeNode*; + using reference = ref; + + constexpr RTIterator(RouteTreeNode* p) + : _p(p) {} + + constexpr reference operator*() const { + return const_cast(*_p); + } + inline RTIterator& operator++() { + _p = _p->_next_sibling; + return *this; + } + inline RTIterator operator++(int) { + RTIterator tmp = *this; + ++(*this); + return tmp; + } + constexpr bool operator==(const RTIterator& rhs) { return _p == rhs._p; } + constexpr bool operator!=(const RTIterator& rhs) { return _p != rhs._p; } + + private: + /** My current node */ + RouteTreeNode* _p; + }; + + /** Recursive iterator implementation for a RouteTreeNode. + * This walks over all child nodes of a given node without a stack + * or recursion: we keep the nodes in depth-first order in the + * linked list managed by RouteTree. Nodes know where their subtree + * ends, so we can just walk the _next ptrs until we find that */ + template + class RTRecIterator { + public: + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = RouteTreeNode; + using pointer = RouteTreeNode*; + using reference = ref; + + constexpr RTRecIterator(RouteTreeNode* p) + : _p(p) {} + + constexpr reference operator*() const { + return const_cast(*_p); + } + inline RTRecIterator& operator++() { + _p = _p->_next; + return *this; + } + inline RTRecIterator operator++(int) { + RTRecIterator tmp = *this; + ++(*this); + return tmp; + } + constexpr bool operator==(const RTRecIterator& rhs) { return _p == rhs._p; } + constexpr bool operator!=(const RTRecIterator& rhs) { return _p != rhs._p; } + + private: + /** My current node */ + RouteTreeNode* _p; + }; + + /** Provide begin and end fns when iterating on this tree. + * .child_nodes() returns Iterable while .all_nodes() returns Iterable */ + template + class Iterable { + public: + constexpr Iterable(RouteTreeNode* node1, RouteTreeNode* node2) + : _node1(node1) + , _node2(node2) {} + constexpr Iterator begin() const { return Iterator(_node1); } + constexpr Iterator end() const { return Iterator(_node2); } + + private: + RouteTreeNode* _node1; + RouteTreeNode* _node2; + }; + + /* Shorthands for different iterator types */ + using iterator = RTIterator; + using const_iterator = RTIterator; + using rec_iterator = RTRecIterator; + using const_rec_iterator = RTRecIterator; + + /* Shorthands for "iterable" types */ + template + using iterable = Iterable>; + template + using rec_iterable = Iterable>; + + /** Traverse child nodes. */ + constexpr iterable child_nodes(void) const { + return iterable(_next, _subtree_end); + } + + /** Get parent node if exists. (nullopt if not) */ + constexpr vtr::optional parent(void) const { + return _parent ? vtr::optional(*_parent) : vtr::nullopt; + } + + /** Traverse the subtree under this node in depth-first order. Doesn't include this node. */ + constexpr rec_iterable all_nodes(void) const { + return rec_iterable(_next, _subtree_end); + } + + /** Print information about this subtree to stdout. */ + void print(void) const; + + /** Is this node a leaf? + * + * True if the next node after this is not its child (we jumped up to the next branch) + * or if it's null. The RouteTree functions keep the books for this. */ + constexpr bool is_leaf(void) const { return _is_leaf; } + + /** Equality operator. For now, just compare the addresses */ + friend bool operator==(const RouteTreeNode& lhs, const RouteTreeNode& rhs) { + return &lhs == &rhs; + } + friend bool operator!=(const RouteTreeNode& lhs, const RouteTreeNode& rhs) { + return !(lhs == rhs); + } + + private: + void print_x(int depth) const; + + /** Traverse child nodes, mutable reference */ + constexpr iterable _child_nodes(void) const { + return iterable(_next, _subtree_end); + } + + /** Traverse subtree, mutable reference */ + constexpr rec_iterable _all_nodes(void) const { + return rec_iterable(_next, _subtree_end); + } + + /** Ptr to parent */ + RouteTreeNode* _parent = nullptr; + + /** In the RouteTree, nodes are stored as a linked list in depth-first order. + * This points to the next element in that list. If proper ordering is kept, + * this also points to the first child of this node (given that it's not a leaf) */ + RouteTreeNode* _next = nullptr; + + /** Having a doubly linked list helps maintain the ordering of nodes */ + RouteTreeNode* _prev = nullptr; + + /** Here is the awkward part: these two pointers can become a union. + * 1. If there is a next sibling, _subtree_end is equal to it: when we finish walking the subtree we arrive at the next sibling. + * 2. If there is not, _subtree_end is equal to the parent's _subtree_end. + * So there is never a case where _next_sibling and _subtree_end are both valid and pointing at different things. + * Then, the only question is where to stop when walking _next_sibling ptrs, since the final sibling won't point at nullptr. + * But it will point at parent's _subtree_end, so we can use that as the limiting case */ + union { + /* Ptr to "next sibling": used when traversing child nodes */ + RouteTreeNode* _next_sibling = nullptr; + /* Where does the subtree under this node end in the flattened linked list? Needed when recursively iterating */ + RouteTreeNode* _subtree_end; + }; +}; + +/** fwd definition for compatibility class in old_traceback.h */ +class TracebackCompat; + +/** + * @brief Top level route tree used in timing analysis and keeping routing state. + * + * Contains the root node and a lookup from RRNodeIds to RouteTreeNode&s in the tree. */ +class RouteTree { + friend class TracebackCompat; + + public: + RouteTree() = delete; + RouteTree(const RouteTree&); + RouteTree(RouteTree&&); + RouteTree& operator=(const RouteTree&); + RouteTree& operator=(RouteTree&&); + + /** Return a RouteTree initialized to inode. */ + RouteTree(RRNodeId inode); + /** Return a RouteTree initialized to the source of nets[inet]. */ + RouteTree(ParentNetId inet); + + ~RouteTree() { + free_list(_root); + } + + /** Add the most recently finished wire segment to the routing tree, and + * update the Tdel, etc. numbers for the rest of the routing tree. hptr + * is the heap pointer of the SINK that was reached, and target_net_pin_index + * is the net pin index corresponding to the SINK that was reached. This routine + * returns a tuple: RouteTreeNode of the branch it adds to the route tree and + * RouteTreeNode of the SINK it adds to the routing. */ + std::tuple, vtr::optional> + update_from_heap(t_heap* hptr, int target_net_pin_index, SpatialRouteTreeLookup* spatial_rt_lookup, bool is_flat); + + /** Reload timing values (R_upstream, C_downstream, Tdel). + * Can take a RouteTreeNode& to do an incremental update. + * Note that update_from_heap already does this, but prune() doesn't */ + void reload_timing(vtr::optional from_node = vtr::nullopt); + + /** Get the RouteTreeNode corresponding to the RRNodeId. Returns nullopt if not found. */ + vtr::optional find_by_rr_id(RRNodeId rr_node) const; + + /** Check the consistency of this route tree. Looks for: + * - invalid parent-child links + * - invalid timing values + * - congested SINKs + * Returns true if OK. */ + bool is_valid(void) const; + + /** Check if the tree has any overused nodes (-> the tree is congested). + * Returns true if not congested. */ + bool is_uncongested(void) const; + + /** Print information about this route tree to stdout. */ + void print(void) const; + + /** Prune overused nodes from the tree. + * Also prune unused non-configurable nodes if non_config_node_set_usage is provided (see get_non_config_node_set_usage) + * Returns nullopt if the entire tree is pruned. */ + vtr::optional prune(CBRR& connections_inf, std::vector* non_config_node_set_usage = nullptr); + + /** Remove all sinks and mark the remaining nodes as un-expandable. + * This is used after routing a clock net. + * TODO: is this function doing anything? Try running without it */ + void freeze(void); + + /** Count configurable edges to non-configurable node sets. (rr_nonconf_node_sets index -> int) + * Required when using prune() to remove non-configurable nodes. */ + std::vector get_non_config_node_set_usage(void) const; + + /* Wrapper for the recursive iterator. */ + using iterator = RouteTreeNode::RTRecIterator; + using iterable = RouteTreeNode::Iterable; + + /** Get an iterable for all nodes in this RouteTree. */ + constexpr iterable all_nodes(void) const { return iterable(_root, nullptr); } + + /** Get a reference to the root RouteTreeNode. */ + constexpr const RouteTreeNode& root(void) const { return *_root; } /* this file is 90% const and 10% code */ + + private: + std::tuple, vtr::optional> + add_subtree_from_heap(t_heap* hptr, int target_net_pin_index, bool is_flat); + + void add_non_configurable_nodes(RouteTreeNode* rt_node, + bool reached_by_non_configurable_edge, + std::unordered_set& visited, + bool is_flat); + + void load_new_subtree_R_upstream(RouteTreeNode& from_node); + float load_new_subtree_C_downstream(RouteTreeNode& from_node); + RouteTreeNode& update_unbuffered_ancestors_C_downstream(RouteTreeNode& from_node); + void load_route_tree_Tdel(RouteTreeNode& from_node, float Tarrival); + + bool is_valid_x(const RouteTreeNode& rt_node) const; + bool is_uncongested_x(const RouteTreeNode& rt_node) const; + + vtr::optional + prune_x(RouteTreeNode& rt_node, + CBRR& connections_inf, + bool force_prune, + std::vector* non_config_node_set_usage); + + void freeze_x(RouteTreeNode& rt_node); + + /** Add node to parent, set up prev/next links, set up parent-child links and update lookup. */ + inline void add_node(RouteTreeNode* parent, RouteTreeNode* node) { + node->_prev = parent; + node->_next = parent->_next; + if (parent->_next) + parent->_next->_prev = node; + + node->_parent = parent; + /* If parent is a leaf, its _next ptr isn't a child node. Update _subtree_end + * Otherwise, update the sibling ptr, since a sibling exists, it's the end of our subtree. + * These two pointers are a union, see RouteTreeNode definition for details */ + if (parent->is_leaf()) { + node->_subtree_end = parent->_subtree_end; + } else { + node->_next_sibling = parent->_next; + } + parent->_next = node; + _rr_node_to_rt_node[node->inode] = node; + + /* Now it's a branch */ + parent->_is_leaf = false; + } + + /** Free all nodes after node */ + inline void free_list(RouteTreeNode* node) { + RouteTreeNode* p = node; + while (p) { + RouteTreeNode* x = p; + p = p->_next; + free_node(x); + } + } + + /** Make a copy of rhs and return it. Updates _rr_node_to_rt_node */ + RouteTreeNode* copy_tree(const RouteTreeNode* rhs); + + /** Helper for copy_list: copy child nodes of rhs into lhs */ + void copy_tree_x(RouteTreeNode* lhs, const RouteTreeNode& rhs); + + /** Free a node. Only keeps the linked list valid (not the tree ptrs) */ + inline void free_node(RouteTreeNode* node) { + if (node->_prev) + node->_prev->_next = node->_next; + if (node->_next) + node->_next->_prev = node->_prev; + delete node; + } + + /** Iterate through parent's child nodes and remove if p returns true. + * Updates tree ptrs and _rr_node_to_rt_node. */ + inline void remove_child_if(RouteTreeNode& parent, const std::function& p) { + RouteTreeNode* curr = parent._next; + RouteTreeNode* prev = nullptr; + RouteTreeNode* end = parent._subtree_end; + while (curr != end) { + if (p(*curr)) { + _rr_node_to_rt_node.erase(curr->inode); + /* Here is a tricky part: when updating prev's _next_sibling, we need to also + * update the _subtree_end/_next_sibling ptrs of prev's subtree. We know that + * _next_sibling points to _subtree_end in the last ("rightmost") node of every + * level. _subtree_end's _prev will take us to the rightmost leaf in the subtree, + * and from there we can walk up via _parent ptrs and update along the way */ + if (prev) { + RouteTreeNode* q = prev->_subtree_end->_prev; + while (q != prev) { + q->_subtree_end = curr->_subtree_end; + q = q->_parent; + } + prev->_next_sibling = curr->_next_sibling; + } + RouteTreeNode* x = curr; + curr = curr->_next_sibling; + free_node(x); + } else { + prev = curr; + curr = curr->_next_sibling; + } + } + /* did this node become a leaf? */ + if (parent._next == nullptr || parent._next->_parent != &parent) + parent._is_leaf = true; + } + + /** Root node. + * This is also the internal node list via the ptrs in RouteTreeNode. */ + RouteTreeNode* _root; + + /** Lookup from RRNodeIds to RouteTreeNodes in the tree. + * In some cases the same SINK node is put into the tree multiple times in a + * single route. To model this, we are putting in separate rt_nodes in the route + * tree if we go to the same SINK more than once. rr_node_to_rt_node[inode] will + * therefore store the last rt_node created of all the SINK nodes with the same + * index "inode". */ + std::unordered_map _rr_node_to_rt_node; +}; diff --git a/vpr/src/route/route_tree_fwd.h b/vpr/src/route/route_tree_fwd.h new file mode 100644 index 00000000000..61b61ae739d --- /dev/null +++ b/vpr/src/route/route_tree_fwd.h @@ -0,0 +1,6 @@ +#pragma once + +/* Forward declarations for RouteTree */ + +class RouteTree; +class RouteTreeNode; diff --git a/vpr/src/route/route_tree_timing.cpp b/vpr/src/route/route_tree_timing.cpp deleted file mode 100644 index 10305028341..00000000000 --- a/vpr/src/route/route_tree_timing.cpp +++ /dev/null @@ -1,1635 +0,0 @@ -#include -#include -#include - -#include "vtr_assert.h" -#include "vtr_log.h" -#include "vtr_memory.h" -#include "vtr_math.h" - -#include "vpr_types.h" -#include "vpr_utils.h" -#include "vpr_error.h" - -#include "globals.h" -#include "route_common.h" -#include "route_tree_timing.h" - -/* This module keeps track of the partial routing tree for timing-driven * - * routing. The normal traceback structure doesn't provide enough info * - * about the partial routing during timing-driven routing, so the routines * - * in this module are used to keep a tree representation of the partial * - * routing during timing-driven routing. This allows rapid incremental * - * timing analysis. */ - -/********************** Variables local to this module ***********************/ - -/* Array below allows mapping from any rr_node to any rt_node currently in - * the rt_tree. */ - -/* In some cases the same SINK node is put into the tree multiple times in a * - * single route. To model this, we are putting in separate rt_nodes in the route * - * tree if we go to the same SINK more than once. rr_node_to_rt_node[inode] will * - * therefore store the last rt_node created of all the SINK nodes with the same * - * index "inode". This is okay because the mapping is only used in this file to * - * quickly figure out where rt_nodes that we are branching off of (for nets with * - * fanout > 1) are, and we will never branch off a SINK. */ -static std::vector rr_node_to_rt_node; /* [0..device_ctx.rr_nodes.size()-1] */ - -/* Frees lists for fast addition and deletion of nodes and edges. */ - -static t_rt_node* rt_node_free_list = nullptr; -static t_linked_rt_edge* rt_edge_free_list = nullptr; - -/********************** Subroutines local to this module *********************/ - -static t_rt_node* alloc_rt_node(); - -static void free_rt_node(t_rt_node** rt_node); - -static t_linked_rt_edge* alloc_linked_rt_edge(); - -static void free_linked_rt_edge(t_linked_rt_edge* rt_edge); - -static t_rt_node* add_subtree_to_route_tree(t_heap* hptr, - int target_net_pin_index, - t_rt_node** sink_rt_node_ptr, - bool is_flat); - -static t_rt_node* add_non_configurable_to_route_tree(const int rr_node, - const bool reached_by_non_configurable_edge, - std::unordered_set& visited, - bool is_flat); - -static t_rt_node* update_unbuffered_ancestors_C_downstream(t_rt_node* start_of_new_subtree_rt_node); - -bool verify_route_tree_recurr(t_rt_node* node, std::set& seen_nodes); - -static t_rt_node* prune_route_tree_recurr(t_rt_node* node, - CBRR& connections_inf, - bool force_prune, - std::vector* non_config_node_set_usage); - -static t_trace* traceback_to_route_tree_branch(t_trace* trace, std::map& rr_node_to_rt, std::vector* non_config_node_set_usage, bool is_flat); - -static std::pair traceback_from_route_tree_recurr(t_trace* head, t_trace* tail, const t_rt_node* node); - -//static ClusterPinId get_connected_cluster_pin_id(const ClusteredPinAtomPinsLookup& pin_look_up, ParentPinId pin, bool is_flat); - -void collect_route_tree_connections(const t_rt_node* node, std::multiset>& connections); - -/************************** Subroutine definitions ***************************/ - -constexpr float epsilon = 1e-15; -static bool equal_approx(float a, float b) { - return fabs(a - b) < epsilon; -} - -bool alloc_route_tree_timing_structs(bool exists_ok) { - /* Allocates any structures needed to build the routing trees. */ - - auto& device_ctx = g_vpr_ctx.device(); - bool route_tree_structs_are_allocated = (rr_node_to_rt_node.size() == size_t(device_ctx.rr_graph.num_nodes()) - || rt_node_free_list != nullptr); - if (route_tree_structs_are_allocated) { - if (exists_ok) { - return false; - } else { - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, - "in alloc_route_tree_timing_structs: old structures already exist.\n"); - } - } - - rr_node_to_rt_node = std::vector(device_ctx.rr_graph.num_nodes(), nullptr); - - return true; -} - -void free_route_tree_timing_structs() { - /* Frees the structures needed to build routing trees, and really frees - * (i.e. deletes) all the data on the free lists. */ - - t_rt_node *rt_node, *next_node; - t_linked_rt_edge *rt_edge, *next_edge; - - rr_node_to_rt_node.clear(); - - rt_node = rt_node_free_list; - - while (rt_node != nullptr) { - next_node = rt_node->u.next; - delete rt_node; - rt_node = next_node; - } - - rt_node_free_list = nullptr; - - rt_edge = rt_edge_free_list; - - while (rt_edge != nullptr) { - next_edge = rt_edge->next; - delete rt_edge; - rt_edge = next_edge; - } - - rt_edge_free_list = nullptr; -} - -static t_rt_node* -alloc_rt_node() { - /* Allocates a new rt_node, from the free list if possible, from the free - * store otherwise. */ - - t_rt_node* rt_node; - - rt_node = rt_node_free_list; - - if (rt_node != nullptr) { - rt_node_free_list = rt_node->u.next; - } else { - rt_node = new t_rt_node; - } - - return (rt_node); -} - -/* After putting the rt_node to the free list, rt_node pointer would be set to - * nullptr to make sure that it does not get double frees if a caller tries to - * free the routing tree twice */ -static void free_rt_node(t_rt_node** rt_node) { - /* Adds rt_node to the proper free list. */ - - (*rt_node)->u.next = rt_node_free_list; - rt_node_free_list = (*rt_node); - (*rt_node) = nullptr; -} - -static t_linked_rt_edge* -alloc_linked_rt_edge() { - /* Allocates a new linked_rt_edge, from the free list if possible, from the - * free store otherwise. */ - - t_linked_rt_edge* linked_rt_edge; - - linked_rt_edge = rt_edge_free_list; - - if (linked_rt_edge != nullptr) { - rt_edge_free_list = linked_rt_edge->next; - } else { - linked_rt_edge = new t_linked_rt_edge; - } - - VTR_ASSERT(linked_rt_edge != nullptr); - return (linked_rt_edge); -} - -/* Adds the rt_edge to the rt_edge free list. */ -static void free_linked_rt_edge(t_linked_rt_edge* rt_edge) { - rt_edge->next = rt_edge_free_list; - rt_edge_free_list = rt_edge; -} - -/* Initializes the routing tree to just the net source, and returns the root - * node of the rt_tree (which is just the net source). */ -t_rt_node* init_route_tree_to_source(const ParentNetId& inet) { - t_rt_node* rt_root; - int inode; - - auto& route_ctx = g_vpr_ctx.routing(); - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - - rt_root = alloc_rt_node(); - rt_root->u.child_list = nullptr; - rt_root->parent_node = nullptr; - rt_root->parent_switch = OPEN; - rt_root->re_expand = true; - - inode = route_ctx.net_rr_terminals[inet][0]; /* Net source */ - - rt_root->inode = inode; - rt_root->net_pin_index = OPEN; - rt_root->C_downstream = rr_graph.node_C(RRNodeId(inode)); - rt_root->R_upstream = rr_graph.node_R(RRNodeId(inode)); - rt_root->Tdel = 0.5 * rr_graph.node_R(RRNodeId(inode)) * rr_graph.node_C(RRNodeId(inode)); - rr_node_to_rt_node[inode] = rt_root; - - return (rt_root); -} - -/* Adds the most recently finished wire segment to the routing tree, and - * updates the Tdel, etc. numbers for the rest of the routing tree. hptr - * is the heap pointer of the SINK that was reached, and target_net_pin_index - * is the net pin index corresponding to the SINK that was reached. This routine - * returns a pointer to the rt_node of the SINK that it adds to the routing. */ -t_rt_node* update_route_tree(t_heap* hptr, int target_net_pin_index, SpatialRouteTreeLookup* spatial_rt_lookup, bool is_flat) { - t_rt_node *start_of_new_subtree_rt_node, *sink_rt_node; - t_rt_node *unbuffered_subtree_rt_root, *subtree_parent_rt_node; - float Tdel_start; - short iswitch; - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - //Create a new subtree from the target in hptr to existing routing - start_of_new_subtree_rt_node = add_subtree_to_route_tree(hptr, - target_net_pin_index, - &sink_rt_node, - is_flat); - - //Propagate R_upstream down into the new subtree - load_new_subtree_R_upstream(start_of_new_subtree_rt_node); - - //Propagate C_downstream up from new subtree sinks to subtree root - load_new_subtree_C_downstream(start_of_new_subtree_rt_node); - - //Propagate C_downstream up from the subtree root - unbuffered_subtree_rt_root = update_unbuffered_ancestors_C_downstream(start_of_new_subtree_rt_node); - - subtree_parent_rt_node = unbuffered_subtree_rt_root->parent_node; - - if (subtree_parent_rt_node != nullptr) { /* Parent exists. */ - Tdel_start = subtree_parent_rt_node->Tdel; - iswitch = unbuffered_subtree_rt_root->parent_switch; - /*TODO Just a note (no action needed for this PR):In future, we need to consider APIs that returns - * the Tdel for a routing trace in RRGraphView.*/ - Tdel_start += rr_graph.rr_switch_inf(RRSwitchId(iswitch)).R * unbuffered_subtree_rt_root->C_downstream; - Tdel_start += rr_graph.rr_switch_inf(RRSwitchId(iswitch)).Tdel; - } else { /* Subtree starts at SOURCE */ - Tdel_start = 0.; - } - - load_route_tree_Tdel(unbuffered_subtree_rt_root, Tdel_start); - - if (spatial_rt_lookup) { - update_route_tree_spatial_lookup_recur(start_of_new_subtree_rt_node, *spatial_rt_lookup); - } - - return (sink_rt_node); -} - -/* Records all nodes from the current routing (rt_tree) into the rr_node_to_rt_node - * lookup, which maps the node's corresponding rr_node index (inode) to the node - * itself. This is done recursively, starting from the root of the tree to its leafs - * (SINKs) in a depth-first traversal. The rt_node we are currently processing has - * either not been added to the routing for this net before or if it was added, the - * rr_node_to_rt_node mapping structure should point back at the rt_node itself so - * we are just branching off that point. Exceptions are the SINK nodes, some - * netlists and input pin equivalence can lead to us routing to the same SINK more - * than once on a net (resulting in different rt_nodes sharing the same rr_node index). - * Hence for SINKs we assert on a weaker condition that if this SINK is already in the - * rt_tree, the rr_node_to_rt_node mapping structure points to a legal rt_node (but - * not necessarily the only one) containing the SINK */ -void add_route_tree_to_rr_node_lookup(t_rt_node* node) { - if (node) { - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - if (rr_graph.node_type(RRNodeId(node->inode)) == SINK) { - VTR_ASSERT(rr_node_to_rt_node[node->inode] == nullptr || rr_node_to_rt_node[node->inode]->inode == node->inode); - } else { - VTR_ASSERT(rr_node_to_rt_node[node->inode] == nullptr || rr_node_to_rt_node[node->inode] == node); - } - - rr_node_to_rt_node[node->inode] = node; - - for (auto edge = node->u.child_list; edge != nullptr; edge = edge->next) { - add_route_tree_to_rr_node_lookup(edge->child); - } - } -} - -/* Adds the most recent wire segment, ending at the SINK indicated by hptr, - * to the routing tree. target_net_pin_index is the net pin index correspinding - * to the SINK indicated by hptr. Returns the first (most upstream) new rt_node, - * and (via a pointer) the rt_node of the new SINK. Traverses up from SINK */ -static t_rt_node* -add_subtree_to_route_tree(t_heap* hptr, - int target_net_pin_index, - t_rt_node** sink_rt_node_ptr, - bool is_flat) { - t_rt_node *rt_node, *downstream_rt_node, *sink_rt_node; - t_linked_rt_edge* linked_rt_edge; - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - auto& route_ctx = g_vpr_ctx.routing(); - - int inode = hptr->index; - - //if (rr_graph.node_type(RRNodeId(inode)) != SINK) { - //VPR_FATAL_ERROR(VPR_ERROR_ROUTE, - //"in add_subtree_to_route_tree. Expected type = SINK (%d).\n" - //"Got type = %d.", SINK, rr_graph.node_type(RRNodeId(inode))); - //} - - sink_rt_node = alloc_rt_node(); - sink_rt_node->u.child_list = nullptr; - sink_rt_node->inode = inode; - sink_rt_node->net_pin_index = target_net_pin_index; //hptr is the heap pointer of the SINK that was reached, which corresponds to the target pin - rr_node_to_rt_node[inode] = sink_rt_node; - - /* In the code below I'm marking SINKs and IPINs(If flat routing is not enabled) as not to be re-expanded. - * It makes the code more efficient (though not vastly) to prune this way - * when there aren't route-throughs or ipin doglegs. */ - - sink_rt_node->re_expand = false; - - /* Now do it's predecessor. */ - - downstream_rt_node = sink_rt_node; - - std::unordered_set main_branch_visited; //does not include sink - std::unordered_set all_visited; //does not include sink - inode = hptr->prev_node(); - RREdgeId edge = hptr->prev_edge(); - short iswitch = rr_graph.rr_nodes().edge_switch(edge); - - /* For all "new" nodes in the main path */ - // inode is node index of previous node - // NO_PREVIOUS tags a previously routed node - - while (rr_node_to_rt_node[inode] == nullptr) { //Not connected to existing routing - main_branch_visited.insert(inode); - all_visited.insert(inode); - - linked_rt_edge = alloc_linked_rt_edge(); - linked_rt_edge->child = downstream_rt_node; - - // Also mark downstream_rt_node->inode as visited to prevent - // add_non_configurable_to_route_tree from potentially adding - // downstream_rt_node to the rt tree twice. - all_visited.insert(downstream_rt_node->inode); - linked_rt_edge->iswitch = iswitch; - linked_rt_edge->next = nullptr; - - rt_node = alloc_rt_node(); - downstream_rt_node->parent_node = rt_node; - downstream_rt_node->parent_switch = iswitch; - - rt_node->u.child_list = linked_rt_edge; - rt_node->inode = inode; - rt_node->net_pin_index = OPEN; //net pin index is invalid for non-SINK nodes - - rr_node_to_rt_node[inode] = rt_node; - // If is_flat is enabled, IPINs should be added, since they are used for intra-cluster routing - if (rr_graph.node_type(RRNodeId(inode)) == IPIN && !is_flat) { - rt_node->re_expand = false; - } else { - rt_node->re_expand = true; - } - - downstream_rt_node = rt_node; - edge = route_ctx.rr_node_route_inf[inode].prev_edge; - inode = route_ctx.rr_node_route_inf[inode].prev_node; - iswitch = rr_graph.rr_nodes().edge_switch(edge); - } - - //Inode is now the branch point to the old routing; do not need - //to alloc another node since the old routing has done so already - rt_node = rr_node_to_rt_node[inode]; - VTR_ASSERT_MSG(rt_node, "Previous routing branch should exist"); - - linked_rt_edge = alloc_linked_rt_edge(); - linked_rt_edge->child = downstream_rt_node; - linked_rt_edge->iswitch = iswitch; - linked_rt_edge->next = rt_node->u.child_list; //Add to head - rt_node->u.child_list = linked_rt_edge; - - downstream_rt_node->parent_node = rt_node; - downstream_rt_node->parent_switch = iswitch; - - //Expand (recursively) each of the main-branch nodes adding any - //non-configurably connected nodes - //Sink is not included, so no need to pass in the node's ipin value. - for (int rr_node : main_branch_visited) { - add_non_configurable_to_route_tree(rr_node, false, all_visited, is_flat); - } - - *sink_rt_node_ptr = sink_rt_node; - return (downstream_rt_node); -} - -static t_rt_node* add_non_configurable_to_route_tree(const int rr_node, - const bool reached_by_non_configurable_edge, - std::unordered_set& visited, - bool is_flat) { - t_rt_node* rt_node = nullptr; - - if (!visited.count(rr_node) || !reached_by_non_configurable_edge) { - visited.insert(rr_node); - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - - rt_node = rr_node_to_rt_node[rr_node]; - - if (!reached_by_non_configurable_edge) { //An existing main branch node - VTR_ASSERT(rt_node); - } else { - VTR_ASSERT(reached_by_non_configurable_edge); - - if (!rt_node) { - rt_node = alloc_rt_node(); - rt_node->u.child_list = nullptr; - rt_node->inode = rr_node; - rt_node->net_pin_index = OPEN; - - if (rr_graph.node_type(RRNodeId(rr_node)) == IPIN && !is_flat) { - rt_node->re_expand = false; - } else { - rt_node->re_expand = true; - } - } else { - VTR_ASSERT(rt_node->inode == rr_node); - } - } - for (int iedge : rr_graph.non_configurable_edges(RRNodeId(rr_node))) { - //Recursive case: expand children - VTR_ASSERT(!rr_graph.edge_is_configurable(RRNodeId(rr_node), iedge)); - int to_rr_node = size_t(rr_graph.edge_sink_node(RRNodeId(rr_node), iedge)); - - //Recurse - t_rt_node* child_rt_node = add_non_configurable_to_route_tree(to_rr_node, - true, - visited, - is_flat); - - if (!child_rt_node) continue; - int iswitch = rr_graph.edge_switch(RRNodeId(rr_node), iedge); - - //Create the edge - t_linked_rt_edge* linked_rt_edge = alloc_linked_rt_edge(); - linked_rt_edge->child = child_rt_node; - linked_rt_edge->iswitch = iswitch; - - //Add edge at head of parent linked list - linked_rt_edge->next = rt_node->u.child_list; - rt_node->u.child_list = linked_rt_edge; - - //Update child to parent ref - child_rt_node->parent_node = rt_node; - child_rt_node->parent_switch = iswitch; - } - rr_node_to_rt_node[rr_node] = rt_node; - } - - return rt_node; -} - -void load_new_subtree_R_upstream(t_rt_node* rt_node) { - /* Sets the R_upstream values of all the nodes in the new path to the - * correct value by traversing down to SINK from the start of the new path. */ - - if (!rt_node) { - return; - } - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - - t_rt_node* parent_rt_node = rt_node->parent_node; - int inode = rt_node->inode; - - //Calculate upstream resistance - float R_upstream = 0.; - if (parent_rt_node) { - int iswitch = rt_node->parent_switch; - bool switch_buffered = rr_graph.rr_switch_inf(RRSwitchId(iswitch)).buffered(); - - if (!switch_buffered) { - R_upstream += parent_rt_node->R_upstream; //Parent upstream R - } - R_upstream += rr_graph.rr_switch_inf(RRSwitchId(iswitch)).R; //Parent switch R - } - R_upstream += rr_graph.node_R(RRNodeId(inode)); //Current node R - - rt_node->R_upstream = R_upstream; - - //Update children - for (t_linked_rt_edge* edge = rt_node->u.child_list; edge != nullptr; edge = edge->next) { - load_new_subtree_R_upstream(edge->child); //Recurse - } -} - -float load_new_subtree_C_downstream(t_rt_node* rt_node) { - float C_downstream = 0.; - - if (rt_node) { - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - C_downstream += rr_graph.node_C(RRNodeId(rt_node->inode)); - for (t_linked_rt_edge* edge = rt_node->u.child_list; edge != nullptr; edge = edge->next) { - /*Similar to net_delay.cpp, this for loop traverses a rc subtree, whose edges represent enabled switches. - * When switches such as multiplexers and tristate buffers are enabled, their fanout - * produces an "internal capacitance". We account for this internal capacitance of the - * switch by adding it to the total capacitance of the node.*/ - - C_downstream += rr_graph.rr_switch_inf(RRSwitchId(edge->iswitch)).Cinternal; - - float C_downstream_child = load_new_subtree_C_downstream(edge->child); - - if (!rr_graph.rr_switch_inf(RRSwitchId(edge->iswitch)).buffered()) { - C_downstream += C_downstream_child; - } - } - - rt_node->C_downstream = C_downstream; - } - - return C_downstream; -} - -static t_rt_node* -update_unbuffered_ancestors_C_downstream(t_rt_node* start_of_new_path_rt_node) { - /* Updates the C_downstream values for the ancestors of the new path. Once - * a buffered switch is found amongst the ancestors, no more ancestors are - * affected. Returns the root of the "unbuffered subtree" whose Tdel - * values are affected by the new path's addition. */ - - t_rt_node *rt_node, *parent_rt_node; - short iswitch; - float C_downstream_addition; - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - rt_node = start_of_new_path_rt_node; - parent_rt_node = rt_node->parent_node; - iswitch = rt_node->parent_switch; - - /* Now that a connection has been made between rt_node and its parent we must also consider - * the potential effect of internal capacitance. We will first assume that parent is connected - * by an unbuffered switch, so the ancestors downstream capacitance must be equal to the sum - * of the child's downstream capacitance with the internal capacitance of the switch.*/ - - C_downstream_addition = rt_node->C_downstream + rr_graph.rr_switch_inf(RRSwitchId(iswitch)).Cinternal; - - /* Having set the value of C_downstream_addition, we must check whethere the parent switch - * is a buffered or unbuffered switch with the if statement below. If the parent switch is - * a buffered switch, then the parent node's downsteam capacitance is increased by the - * value of the parent switch's internal capacitance in the if statement below. - * Correspondingly, the ancestors' downstream capacitance will be updated by the same - * value through the while loop. Otherwise, if the parent switch is unbuffered, then - * the if statement will be ignored, and the parent and ancestral nodes' downstream - * capacitance will be increased by the sum of the child's downstream capacitance with - * the internal capacitance of the parent switch in the while loop/.*/ - - if (parent_rt_node != nullptr && rr_graph.rr_switch_inf(RRSwitchId(iswitch)).buffered() == true) { - C_downstream_addition = rr_graph.rr_switch_inf(RRSwitchId(iswitch)).Cinternal; - rt_node = parent_rt_node; - rt_node->C_downstream += C_downstream_addition; - parent_rt_node = rt_node->parent_node; - iswitch = rt_node->parent_switch; - } - - while (parent_rt_node != nullptr && rr_graph.rr_switch_inf(RRSwitchId(iswitch)).buffered() == false) { - rt_node = parent_rt_node; - rt_node->C_downstream += C_downstream_addition; - parent_rt_node = rt_node->parent_node; - iswitch = rt_node->parent_switch; - } - return (rt_node); -} - -void load_route_tree_Tdel(t_rt_node* subtree_rt_root, float Tarrival) { - /* Updates the Tdel values of the subtree rooted at subtree_rt_root by - * by calling itself recursively. The C_downstream values of all the nodes - * must be correct before this routine is called. Tarrival is the time at - * at which the signal arrives at this node's *input*. */ - - int inode; - short iswitch; - t_rt_node* child_node; - t_linked_rt_edge* linked_rt_edge; - float Tdel, Tchild; - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - - inode = subtree_rt_root->inode; - - /* Assuming the downstream connections are, on average, connected halfway - * along a wire segment's length. See discussion in net_delay.c if you want - * to change this. */ - Tdel = Tarrival + 0.5 * subtree_rt_root->C_downstream * rr_graph.node_R(RRNodeId(inode)); - subtree_rt_root->Tdel = Tdel; - - /* Now expand the children of this node to load their Tdel values (depth- - * first pre-order traversal). */ - - linked_rt_edge = subtree_rt_root->u.child_list; - - while (linked_rt_edge != nullptr) { - iswitch = linked_rt_edge->iswitch; - child_node = linked_rt_edge->child; - - Tchild = Tdel + rr_graph.rr_switch_inf(RRSwitchId(iswitch)).R * child_node->C_downstream; - Tchild += rr_graph.rr_switch_inf(RRSwitchId(iswitch)).Tdel; /* Intrinsic switch delay. */ - load_route_tree_Tdel(child_node, Tchild); - - linked_rt_edge = linked_rt_edge->next; - } -} - -void load_route_tree_rr_route_inf(t_rt_node* root) { - /* Traverses down a route tree and updates rr_node_inf for all nodes - * to reflect that these nodes have already been routed to */ - - VTR_ASSERT(root != nullptr); - - auto& route_ctx = g_vpr_ctx.mutable_routing(); - - t_linked_rt_edge* edge{root->u.child_list}; - - for (;;) { - int inode = root->inode; - route_ctx.rr_node_route_inf[inode].prev_node = NO_PREVIOUS; - route_ctx.rr_node_route_inf[inode].prev_edge = RREdgeId::INVALID(); - // path cost should be unset - VTR_ASSERT(std::isinf(route_ctx.rr_node_route_inf[inode].path_cost)); - VTR_ASSERT(std::isinf(route_ctx.rr_node_route_inf[inode].backward_path_cost)); - - // reached a sink - if (!edge) { return; } - // branch point (sibling edges) - else if (edge->next) { - // recursively update for each of its sibling branches - do { - load_route_tree_rr_route_inf(edge->child); - edge = edge->next; - } while (edge); - return; - } - - root = edge->child; - edge = root->u.child_list; - } -} - -bool verify_route_tree(t_rt_node* root) { - std::set seen_nodes; - return verify_route_tree_recurr(root, seen_nodes); -} - -bool verify_route_tree_recurr(t_rt_node* node, std::set& seen_nodes) { - if (seen_nodes.count(node->inode)) { - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Duplicate route tree nodes found for node %d", node->inode); - } - - seen_nodes.insert(node->inode); - - for (t_linked_rt_edge* edge = node->u.child_list; edge != nullptr; edge = edge->next) { - verify_route_tree_recurr(edge->child, seen_nodes); - } - return true; -} - -void free_route_tree(t_rt_node* rt_node) { - if (rt_node == nullptr) { - return; - } - t_linked_rt_edge *rt_edge, *next_edge; - - rt_edge = rt_node->u.child_list; - - while (rt_edge != nullptr) { /* For all children */ - t_rt_node* child_node = rt_edge->child; - free_route_tree(child_node); - next_edge = rt_edge->next; - free_linked_rt_edge(rt_edge); - rt_edge = next_edge; - } - - if (!rr_node_to_rt_node.empty()) { - rr_node_to_rt_node.at(rt_node->inode) = nullptr; - } - - free_rt_node(&rt_node); -} - -void print_route_tree(const t_rt_node* rt_node) { - print_route_tree(rt_node, 0); -} - -void print_route_tree(const t_rt_node* rt_node, int depth) { - std::string indent; - for (int i = 0; i < depth; ++i) { - indent += " "; - } - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - VTR_LOG("%srt_node: %d (%s) \t ipin: %d \t R: %g \t C: %g \t delay: %g", - indent.c_str(), rt_node->inode, rr_graph.node_type_string(RRNodeId(rt_node->inode)), rt_node->net_pin_index, rt_node->R_upstream, rt_node->C_downstream, rt_node->Tdel); - - if (rt_node->parent_switch != OPEN) { - bool parent_edge_configurable = rr_graph.rr_switch_inf(RRSwitchId(rt_node->parent_switch)).configurable(); - if (!parent_edge_configurable) { - VTR_LOG("*"); - } - } - - auto& route_ctx = g_vpr_ctx.routing(); - if (route_ctx.rr_node_route_inf[rt_node->inode].occ() > rr_graph.node_capacity(RRNodeId(rt_node->inode))) { - VTR_LOG(" x"); - } - - VTR_LOG("\n"); - - for (t_linked_rt_edge* rt_edge = rt_node->u.child_list; rt_edge != nullptr; rt_edge = rt_edge->next) { - print_route_tree(rt_edge->child, depth + 1); - } -} - -void update_net_delays_from_route_tree(float* net_delay, - const Netlist<>& net_list, - const t_rt_node* const* rt_node_of_sink, - ParentNetId inet, - TimingInfo* timing_info, - NetPinTimingInvalidator* pin_timing_invalidator) { - /* Goes through all the sinks of this net and copies their delay values from - * the route_tree to the net_delay array. */ - - for (unsigned int isink = 1; isink < net_list.net_pins(inet).size(); isink++) { - float new_delay = rt_node_of_sink[isink]->Tdel; - - if (pin_timing_invalidator && new_delay != net_delay[isink]) { - //Delay changed, invalidate for incremental timing update - VTR_ASSERT_SAFE(timing_info); - ParentPinId pin = net_list.net_pin(inet, isink); - pin_timing_invalidator->invalidate_connection(pin, timing_info); - } - - net_delay[isink] = new_delay; - } -} - -/*************** Conversion between traceback and route tree *******************/ -t_rt_node* traceback_to_route_tree(ParentNetId inet, std::vector* non_config_node_set_usage, bool is_flat) { - return traceback_to_route_tree(g_vpr_ctx.routing().trace[inet].head, non_config_node_set_usage, is_flat); -} - -t_rt_node* traceback_to_route_tree(ParentNetId inet, bool is_flat) { - return traceback_to_route_tree(inet, nullptr, is_flat); -} - -t_rt_node* traceback_to_route_tree(t_trace* head, bool is_flat) { - return traceback_to_route_tree(head, - nullptr, - is_flat); -} - -t_rt_node* traceback_to_route_tree(t_trace* head, - std::vector* non_config_node_set_usage, - bool is_flat) { - /* Builds a skeleton route tree from a traceback - * does not calculate R_upstream, C_downstream, or Tdel at all (left uninitialized) - * returns the root of the converted route tree - * initially points at the traceback equivalent of root */ - - if (head == nullptr) { - return nullptr; - } - - VTR_ASSERT_DEBUG(validate_traceback(head)); - - std::map rr_node_to_rt; - - t_trace* trace = head; - while (trace) { //Each branch - trace = traceback_to_route_tree_branch(trace, rr_node_to_rt, non_config_node_set_usage, is_flat); - } - // Due to the recursive nature of traceback_to_route_tree_branch, - // the source node is not properly configured. - // Here, for the source we set the parent node and switch to be - // nullptr and OPEN respectively. - - rr_node_to_rt[head->index]->parent_node = nullptr; - rr_node_to_rt[head->index]->parent_switch = OPEN; - - return rr_node_to_rt[head->index]; -} - -//Constructs a single branch of a route tree from a traceback -//Note that R_upstream and C_downstream are initialized to NaN -// -//Returns the t_trace defining the start of the next branch -static t_trace* traceback_to_route_tree_branch(t_trace* trace, - std::map& rr_node_to_rt, - std::vector* non_config_node_set_usage, - bool is_flat) { - t_trace* next = nullptr; - - if (trace) { - t_rt_node* node = nullptr; - - int inode = trace->index; - int ipin = trace->net_pin_index; - int iswitch = trace->iswitch; - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - auto itr = rr_node_to_rt.find(trace->index); - - // In some cases, the same sink node is put into the tree multiple times in a single route. - // So it is possible to hit the same node index multiple times during traceback. Create a - // separate rt_node for each sink with the same node index. - if (itr == rr_node_to_rt.end() || rr_graph.node_type(RRNodeId(inode)) == SINK) { - //Create - - //Initialize route tree node - node = alloc_rt_node(); - node->inode = inode; - node->net_pin_index = ipin; - node->u.child_list = nullptr; - - node->R_upstream = std::numeric_limits::quiet_NaN(); - node->C_downstream = std::numeric_limits::quiet_NaN(); - node->Tdel = std::numeric_limits::quiet_NaN(); - - auto node_type = rr_graph.node_type(RRNodeId(inode)); - if ((node_type == IPIN && !is_flat) || node_type == SINK) - node->re_expand = false; - else - node->re_expand = true; - - if (node_type == SINK) { - // A non-configurable edge to a sink is also a usage of the - // set. - auto set_itr = device_ctx.rr_node_to_non_config_node_set.find(inode); - if (non_config_node_set_usage != nullptr && set_itr != device_ctx.rr_node_to_non_config_node_set.end()) { - if (rr_graph.rr_switch_inf(RRSwitchId(iswitch)).configurable()) { - (*non_config_node_set_usage)[set_itr->second] += 1; - } - } - } - - //Save - rr_node_to_rt[inode] = node; - } else { - //Found - node = itr->second; - } - VTR_ASSERT(node); - - next = trace->next; - if (iswitch != OPEN) { - // Keep track of non-configurable set usage by looking for - // configurable edges that extend out of a non-configurable set. - // - // Each configurable edges from the non-configurable set is a - // usage of the set. - auto set_itr = device_ctx.rr_node_to_non_config_node_set.find(inode); - if (non_config_node_set_usage != nullptr && set_itr != device_ctx.rr_node_to_non_config_node_set.end()) { - if (rr_graph.rr_switch_inf(RRSwitchId(iswitch)).configurable()) { - (*non_config_node_set_usage)[set_itr->second] += 1; - } - } - - //Recursively construct the remaining branch - next = traceback_to_route_tree_branch(next, rr_node_to_rt, non_config_node_set_usage, is_flat); - - //Get the created child - itr = rr_node_to_rt.find(trace->next->index); - VTR_ASSERT_MSG(itr != rr_node_to_rt.end(), "Child must exist"); - t_rt_node* child = itr->second; - - //Create the edge - t_linked_rt_edge* edge = alloc_linked_rt_edge(); - edge->iswitch = trace->iswitch; - edge->child = child; - edge->next = nullptr; - - //Insert edge at tail of list - edge->next = node->u.child_list; - node->u.child_list = edge; - - //Set child -> parent ref's - child->parent_node = node; - child->parent_switch = iswitch; - - //Parent and child should be mutual - VTR_ASSERT(node->u.child_list->child == child); - VTR_ASSERT(child->parent_node == node); - } - } - return next; -} - -static std::pair traceback_from_route_tree_recurr(t_trace* head, t_trace* tail, const t_rt_node* node) { - if (node) { - if (node->u.child_list) { - //Recursively add children - for (t_linked_rt_edge* edge = node->u.child_list; edge != nullptr; edge = edge->next) { - t_trace* curr = alloc_trace_data(); - curr->index = node->inode; - curr->net_pin_index = node->net_pin_index; - curr->iswitch = edge->iswitch; - curr->next = nullptr; - - if (tail) { - VTR_ASSERT(tail->next == nullptr); - tail->next = curr; - } - - tail = curr; - - if (!head) { - head = tail; - } - - std::tie(head, tail) = traceback_from_route_tree_recurr(head, tail, edge->child); - } - } else { - //Leaf - t_trace* curr = alloc_trace_data(); - curr->index = node->inode; - curr->net_pin_index = node->net_pin_index; - curr->iswitch = OPEN; - curr->next = nullptr; - - if (tail) { - VTR_ASSERT(tail->next == nullptr); - tail->next = curr; - } - - tail = curr; - - if (!head) { - head = tail; - } - } - } - - return {head, tail}; -} - -//static ClusterPinId get_connected_cluster_pin_id(const ClusteredPinAtomPinsLookup& pin_look_up, ParentPinId pin, bool is_flat) { -// ClusterPinId cluster_pin_id = ClusterPinId::INVALID(); -// if(is_flat) {/* Pin is in atom netlist */ -// AtomPinId atom_pin_id = convert_to_atom_pin_id(pin); -// cluster_pin_id = pin_look_up.connected_clb_pin(atom_pin_id); -// -// -// } else { /* Pin is in cluster netlist */ -// cluster_pin_id = convert_to_cluster_pin_id(pin); -// } -// -// return cluster_pin_id; -//} - -t_trace* traceback_from_route_tree(ParentNetId inet, - const t_rt_node* root, - int num_routed_sinks) { - /* Creates the traceback for net inet from the route tree rooted at root - * properly sets route_ctx.trace_head and route_ctx.trace_tail for this net - * returns the trace head for inet */ - - auto& route_ctx = g_vpr_ctx.mutable_routing(); - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - - t_trace* head; - t_trace* tail; - std::unordered_set nodes; - - std::tie(head, tail) = traceback_from_route_tree_recurr(nullptr, nullptr, root); - - VTR_ASSERT(head); - VTR_ASSERT(tail); - VTR_ASSERT(tail->next == nullptr); - - int num_trace_sinks = 0; - for (t_trace* trace = head; trace != nullptr; trace = trace->next) { - nodes.insert(trace->index); - - //Sanity check that number of sinks match expected - if (rr_graph.node_type(RRNodeId(trace->index)) == SINK) { - num_trace_sinks += 1; - } - } - VTR_ASSERT(num_routed_sinks == num_trace_sinks); - - route_ctx.trace[inet].tail = tail; - route_ctx.trace[inet].head = head; - route_ctx.trace_nodes[inet] = nodes; - - return head; -} - -//Prunes a route tree (recursively) based on congestion and the 'force_prune' argument -// -//Returns true if the current node was pruned -static t_rt_node* prune_route_tree_recurr(t_rt_node* node, - CBRR& connections_inf, - bool force_prune, - std::vector* non_config_node_set_usage) { - //Recursively traverse the route tree rooted at node and remove any congested - //sub-trees - - VTR_ASSERT(node); - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - auto& route_ctx = g_vpr_ctx.routing(); - bool congested = (route_ctx.rr_node_route_inf[node->inode].occ() > rr_graph.node_capacity(RRNodeId(node->inode))); - int node_set = -1; - auto itr = device_ctx.rr_node_to_non_config_node_set.find(node->inode); - if (itr != device_ctx.rr_node_to_non_config_node_set.end()) { - node_set = itr->second; - } - - if (congested) { - //This connection is congested -- prune it - force_prune = true; - } - - if (connections_inf.should_force_reroute_connection(node->inode)) { - //Forcibly re-route (e.g. to improve delay) - force_prune = true; - } - - //Recursively prune children - bool all_children_pruned = true; - t_linked_rt_edge* prev_edge = nullptr; - t_linked_rt_edge* edge = node->u.child_list; - while (edge) { - t_rt_node* child = prune_route_tree_recurr(edge->child, - connections_inf, force_prune, non_config_node_set_usage); - - if (!child) { //Child was pruned - - //Remove the edge - if (edge == node->u.child_list) { //Was Head - node->u.child_list = edge->next; - } else { //Was intermediate - VTR_ASSERT(prev_edge); - prev_edge->next = edge->next; - } - - t_linked_rt_edge* old_edge = edge; - edge = edge->next; - - // After removing an edge, check if non_config_node_set_usage - // needs an update. - if (non_config_node_set_usage != nullptr && node_set != -1 && rr_graph.rr_switch_inf(RRSwitchId(old_edge->iswitch)).configurable()) { - (*non_config_node_set_usage)[node_set] -= 1; - VTR_ASSERT((*non_config_node_set_usage)[node_set] >= 0); - } - - free_linked_rt_edge(old_edge); - - //Note prev_edge is unchanged - - } else { //Child not pruned - all_children_pruned = false; - - //Edge not removed - prev_edge = edge; - edge = edge->next; - } - } - - if (rr_graph.node_type(RRNodeId(node->inode)) == SINK) { - if (!force_prune) { - //Valid path to sink - - //Record sink as reachable - connections_inf.reached_rt_sink(node); - - return node; //Not pruned - } else { - VTR_ASSERT(force_prune); - - //Record as not reached - connections_inf.toreach_rr_sink(node->net_pin_index); - - free_rt_node(&node); - return nullptr; //Pruned - } - } else if (all_children_pruned) { - //This node has no children - // - // This can happen in three scenarios: - // 1) This node is being pruned. As a result any child nodes - // (subtrees) will have been pruned. - // - // 2) This node was reached by a non-configurable edge but - // was otherwise unused (forming a 'stub' off the main - // branch). - // - // 3) This node is uncongested, but all its connected sub-trees - // have been pruned. - // - // We take the corresponding actions: - // 1) Prune the node. - // - // 2) Prune the node only if the node set is unused or if force_prune - // is set. - // - // 3) Prune the node. - // - // This avoid the creation of unused 'stubs'. (For example if - // we left the stubs in and they were subsequently not used - // they would uselessly consume routing resources). - VTR_ASSERT(node->u.child_list == nullptr); - - bool reached_non_configurably = false; - if (node->parent_node) { - reached_non_configurably = !rr_graph.rr_switch_inf(RRSwitchId(node->parent_switch)).configurable(); - - if (reached_non_configurably) { - // Check if this non-configurable node set is in use. - VTR_ASSERT(node_set != -1); - if (non_config_node_set_usage != nullptr && (*non_config_node_set_usage)[node_set] == 0) { - force_prune = true; - } - } - } - - if (reached_non_configurably && !force_prune) { - return node; //Not pruned - } else { - free_rt_node(&node); - return nullptr; //Pruned - } - - } else { - // If this node is: - // 1. Part of a non-configurable node set - // 2. The first node in the tree that is part of the non-configurable - // node set - // - // -- and -- - // - // 3. The node set is not active - // - // Then prune this node. - // - if (non_config_node_set_usage != nullptr && node_set != -1 && rr_graph.rr_switch_inf(RRSwitchId(node->parent_switch)).configurable() && (*non_config_node_set_usage)[node_set] == 0) { - // This node should be pruned, re-prune edges once more. - // - // If the following is true: - // - // - The node set is unused - // (e.g. (*non_config_node_set_usage)[node_set] == 0) - // - This particular node still had children - // (which is true by virtue of being in this else statement) - // - // Then that indicates that the node set became unused during the - // pruning. One or more of the children of this node will be - // pruned if prune_route_tree_recurr is called again, and - // eventually the whole node will be prunable. - // - // Consider the following graph: - // - // 1 -> 2 - // 2 -> 3 [non-configurable] - // 2 -> 4 [non-configurable] - // 3 -> 5 - // 4 -> 6 - // - // Assume that nodes 5 and 6 do not connect to a sink, so they - // will be pruned (as normal). When prune_route_tree_recurr - // visits 2 for the first time, node 3 or 4 will remain. This is - // because when prune_route_tree_recurr visits 3 or 4, it will - // not have visited 4 or 3 (respectively). As a result, either - // node 3 or 4 will not be pruned on the first pass, because the - // node set usage count will be > 0. However after - // prune_route_tree_recurr visits 2, 3 and 4, the node set usage - // will be 0, so everything can be pruned. - return prune_route_tree_recurr(node, connections_inf, - /*force_prune=*/false, non_config_node_set_usage); - } - - //An unpruned intermediate node - VTR_ASSERT(!force_prune); - - return node; //Not pruned - } -} - -t_rt_node* prune_route_tree(t_rt_node* rt_root, CBRR& connections_inf) { - return prune_route_tree(rt_root, connections_inf, nullptr); -} - -t_rt_node* prune_route_tree(t_rt_node* rt_root, - CBRR& connections_inf, - std::vector* non_config_node_set_usage) { - /* Prune a skeleton route tree of illegal branches - when there is at least 1 congested node on the path to a sink - * This is the top level function to be called with the SOURCE node as root. - * Returns true if the entire tree has been pruned. - * - * Note: does not update R_upstream/C_downstream - */ - - VTR_ASSERT(rt_root); - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - auto& route_ctx = g_vpr_ctx.routing(); - - VTR_ASSERT_MSG(rr_graph.node_type(RRNodeId(rt_root->inode)) == SOURCE, "Root of route tree must be SOURCE"); - - VTR_ASSERT_MSG(route_ctx.rr_node_route_inf[rt_root->inode].occ() <= rr_graph.node_capacity(RRNodeId(rt_root->inode)), - "Route tree root/SOURCE should never be congested"); - - return prune_route_tree_recurr(rt_root, connections_inf, false, non_config_node_set_usage); -} - -void pathfinder_update_cost_from_route_tree(const t_rt_node* rt_root, int add_or_sub) { - /* Update pathfinder cost of all nodes rooted at rt_root, including rt_root itself */ - - VTR_ASSERT(rt_root != nullptr); - - t_linked_rt_edge* edge{rt_root->u.child_list}; - - // update every node once, so even do it for sinks and branch points once - for (;;) { - pathfinder_update_single_node_occupancy(rt_root->inode, add_or_sub); - - // reached a sink - if (!edge) { - return; - } - // branch point (sibling edges) - else if (edge->next) { - // recursively update for each of its sibling branches - do { - pathfinder_update_cost_from_route_tree(edge->child, add_or_sub); - edge = edge->next; - } while (edge); - return; - } - - rt_root = edge->child; - edge = rt_root->u.child_list; - } -} - -/***************** Debugging and printing for incremental rerouting ****************/ -template -static void traverse_indented_route_tree(const t_rt_node* rt_root, int branch_level, bool new_branch, Op op, int indent_level) { - /* pretty print the route tree; what's printed depends on the printer Op passed in */ - - // rely on preorder depth first traversal - VTR_ASSERT(rt_root != nullptr); - t_linked_rt_edge* edges = rt_root->u.child_list; - // print branch indent - if (new_branch) VTR_LOG("\n%*s", indent_level * branch_level, " \\ "); - - op(rt_root); - // reached sink, move onto next branch - if (!edges) return; - // branch point, has sibling edge - else if (edges->next) { - bool first_branch = true; - do { - // don't print a new line for the first branch - traverse_indented_route_tree(edges->child, branch_level + 1, !first_branch, op, indent_level); - edges = edges->next; - first_branch = false; - } while (edges); - } - // along a path, just propagate down - else { - traverse_indented_route_tree(edges->child, branch_level + 1, false, op, indent_level); - } -} - -void print_edge(const t_linked_rt_edge* edge) { - VTR_LOG("edges to "); - if (!edge) { - VTR_LOG("null"); - return; - } - while (edge) { - VTR_LOG("%d(%d) ", edge->child->inode, edge->iswitch); - edge = edge->next; - } - VTR_LOG("\n"); -} - -static void print_node(const t_rt_node* rt_node) { - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - - int inode = rt_node->inode; - t_rr_type node_type = rr_graph.node_type(RRNodeId(inode)); - VTR_LOG("%5.1e %5.1e %2d%6s|%-6d-> ", rt_node->C_downstream, rt_node->R_upstream, - rt_node->re_expand, rr_node_typename[node_type], inode); -} - -static void print_node_inf(const t_rt_node* rt_node) { - auto& route_ctx = g_vpr_ctx.routing(); - - int inode = rt_node->inode; - const auto& node_inf = route_ctx.rr_node_route_inf[inode]; - VTR_LOG("%5.1e %5.1e%6d%3d|%-6d-> ", node_inf.path_cost, node_inf.backward_path_cost, - node_inf.prev_node, node_inf.prev_edge, inode); -} - -static void print_node_congestion(const t_rt_node* rt_node) { - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - auto& route_ctx = g_vpr_ctx.routing(); - - int inode = rt_node->inode; - const auto& node_inf = route_ctx.rr_node_route_inf[inode]; - const short& node_capacity = rr_graph.node_capacity(RRNodeId(inode)); - VTR_LOG("%2d %2d|%-6d-> ", node_inf.acc_cost, rt_node->Tdel, - node_inf.occ(), node_capacity, inode); -} - -void print_route_tree_inf(const t_rt_node* rt_root) { - traverse_indented_route_tree(rt_root, 0, false, print_node_inf, 34); - VTR_LOG("\n"); -} - -void print_route_tree_node(const t_rt_node* rt_root) { - traverse_indented_route_tree(rt_root, 0, false, print_node, 34); - VTR_LOG("\n"); -} - -void print_route_tree_congestion(const t_rt_node* rt_root) { - traverse_indented_route_tree(rt_root, 0, false, print_node_congestion, 15); - VTR_LOG("\n"); -} - -/* the following is_* functions are for debugging correctness of pruned route tree - * these should only be called when the debug switch DEBUG_INCREMENTAL_REROUTING is on */ -bool is_equivalent_route_tree(const t_rt_node* root, const t_rt_node* root_clone) { - if (!root && !root_clone) return true; - if (!root || !root_clone) return false; // one of them is null - if ((root->inode != root_clone->inode) || (!equal_approx(root->R_upstream, root_clone->R_upstream)) || (!equal_approx(root->C_downstream, root_clone->C_downstream)) || (!equal_approx(root->Tdel, root_clone->Tdel))) { - VTR_LOG("mismatch i %d|%d R %e|%e C %e|%e T %e %e\n", - root->inode, root_clone->inode, - root->R_upstream, root_clone->R_upstream, - root->C_downstream, root_clone->C_downstream, - root->Tdel, root_clone->Tdel); - return false; - } - t_linked_rt_edge* orig_edge{root->u.child_list}; - t_linked_rt_edge* clone_edge{root_clone->u.child_list}; - while (orig_edge && clone_edge) { - if (orig_edge->iswitch != clone_edge->iswitch) - VTR_LOG("mismatch i %d|%d edge switch %d|%d\n", - root->inode, root_clone->inode, - orig_edge->iswitch, clone_edge->iswitch); - if (!is_equivalent_route_tree(orig_edge->child, clone_edge->child)) return false; // child trees not equivalent - orig_edge = orig_edge->next; - clone_edge = clone_edge->next; - } - if (orig_edge || clone_edge) { - VTR_LOG("one of the trees have an extra edge!\n"); - return false; - } - return true; // passed all tests -} - -// check only the connections are correct, ignore R and C -bool is_valid_skeleton_tree(const t_rt_node* root) { - int inode = root->inode; - t_linked_rt_edge* edge = root->u.child_list; - while (edge) { - if (edge->child->parent_node != root) { - VTR_LOG("parent-child relationship not mutually acknowledged by parent %d->%d child %d<-%d\n", - inode, edge->child->inode, - edge->child->inode, edge->child->parent_node->inode); - return false; - } - if (edge->iswitch != edge->child->parent_switch) { - VTR_LOG("parent(%d)-child(%d) connected switch not equivalent parent %d child %d\n", - inode, edge->child->inode, edge->iswitch, edge->child->parent_switch); - return false; - } - - if (!is_valid_skeleton_tree(edge->child)) { - VTR_LOG("subtree %d invalid, propagating up\n", edge->child->inode); - return false; - } - edge = edge->next; - } - return true; -} - -bool is_valid_route_tree(const t_rt_node* root) { - // check upstream resistance - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - auto& route_ctx = g_vpr_ctx.routing(); - - constexpr float CAP_REL_TOL = 1e-6; - constexpr float CAP_ABS_TOL = vtr::DEFAULT_ABS_TOL; - constexpr float RES_REL_TOL = 1e-6; - constexpr float RES_ABS_TOL = vtr::DEFAULT_ABS_TOL; - - int inode = root->inode; - short iswitch = root->parent_switch; - if (root->parent_node) { - if (rr_graph.rr_switch_inf(RRSwitchId(iswitch)).buffered()) { - float R_upstream_check = rr_graph.node_R(RRNodeId(inode)) + rr_graph.rr_switch_inf(RRSwitchId(iswitch)).R; - if (!vtr::isclose(root->R_upstream, R_upstream_check, RES_REL_TOL, RES_ABS_TOL)) { - VTR_LOG("%d mismatch R upstream %e supposed %e\n", inode, root->R_upstream, R_upstream_check); - return false; - } - } else { - float R_upstream_check = rr_graph.node_R(RRNodeId(inode)) + root->parent_node->R_upstream + rr_graph.rr_switch_inf(RRSwitchId(iswitch)).R; - if (!vtr::isclose(root->R_upstream, R_upstream_check, RES_REL_TOL, RES_ABS_TOL)) { - VTR_LOG("%d mismatch R upstream %e supposed %e\n", inode, root->R_upstream, R_upstream_check); - return false; - } - } - } else if (root->R_upstream != rr_graph.node_R(RRNodeId(inode))) { - VTR_LOG("%d mismatch R upstream %e supposed %e\n", inode, root->R_upstream, rr_graph.node_R(RRNodeId(inode))); - return false; - } - - // check downstream C - t_linked_rt_edge* edge = root->u.child_list; - float C_downstream_children{0}; - // sink, must not be congested - if (!edge) { - int occ = route_ctx.rr_node_route_inf[inode].occ(); - int capacity = rr_graph.node_capacity(RRNodeId(inode)); - if (occ > capacity) { - VTR_LOG("SINK %d occ %d > cap %d\n", inode, occ, capacity); - return false; - } - } - while (edge) { - if (edge->child->parent_node != root) { - VTR_LOG("parent-child relationship not mutually acknowledged by parent %d->%d child %d<-%d\n", - inode, edge->child->inode, - edge->child->inode, edge->child->parent_node->inode); - return false; - } - if (edge->iswitch != edge->child->parent_switch) { - VTR_LOG("parent(%d)-child(%d) connected switch not equivalent parent %d child %d\n", - inode, edge->child->inode, edge->iswitch, edge->child->parent_switch); - return false; - } - - C_downstream_children += rr_graph.rr_switch_inf(RRSwitchId(edge->iswitch)).Cinternal; - - if (!rr_graph.rr_switch_inf(RRSwitchId(edge->iswitch)).buffered()) { - C_downstream_children += edge->child->C_downstream; - } - - if (!is_valid_route_tree(edge->child)) { - VTR_LOG("subtree %d invalid, propagating up\n", edge->child->inode); - return false; - } - edge = edge->next; - } - float C_downstream_check = C_downstream_children + rr_graph.node_C(RRNodeId(inode)); - if (!vtr::isclose(root->C_downstream, C_downstream_check, CAP_REL_TOL, CAP_ABS_TOL)) { - VTR_LOG("%d mismatch C downstream %e supposed %e\n", inode, root->C_downstream, C_downstream_check); - return false; - } - - return true; -} - -//Returns true if the route tree rooted at 'root' is not congested -bool is_uncongested_route_tree(const t_rt_node* root) { - auto& route_ctx = g_vpr_ctx.routing(); - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - - int inode = root->inode; - if (route_ctx.rr_node_route_inf[inode].occ() > rr_graph.node_capacity(RRNodeId(inode))) { - //This node is congested - return false; - } - - for (t_linked_rt_edge* edge = root->u.child_list; edge != nullptr; edge = edge->next) { - if (!is_uncongested_route_tree(edge->child)) { - //The sub-tree connected to this edge is congested - return false; - } - } - - //The sub-tree below the curret node is unconngested - return true; -} - -t_rt_node* -init_route_tree_to_source_no_net(int inode) { - /* Initializes the routing tree to just the net source, and returns the root - * node of the rt_tree (which is just the net source). */ - - t_rt_node* rt_root; - - auto& device_ctx = g_vpr_ctx.device(); - const auto& rr_graph = device_ctx.rr_graph; - - rt_root = alloc_rt_node(); - rt_root->u.child_list = nullptr; - rt_root->parent_node = nullptr; - rt_root->parent_switch = OPEN; - rt_root->re_expand = true; - rt_root->inode = inode; - rt_root->net_pin_index = OPEN; - rt_root->C_downstream = rr_graph.node_C(RRNodeId(inode)); - rt_root->R_upstream = rr_graph.node_R(RRNodeId(inode)); - rt_root->Tdel = 0.5 * rr_graph.node_R(RRNodeId(inode)) * rr_graph.node_C(RRNodeId(inode)); - rr_node_to_rt_node[inode] = rt_root; - - return (rt_root); -} - -bool verify_traceback_route_tree_equivalent(const t_trace* head, const t_rt_node* rt_root) { - //Walk the route tree saving all the used connections - std::multiset> route_tree_connections; - collect_route_tree_connections(rt_root, route_tree_connections); - - //Remove the extra parent connection to root (not included in traceback) - route_tree_connections.erase(std::make_tuple(OPEN, OPEN, rt_root->inode)); - - //Walk the traceback and verify that every connection exists in the route tree set - int prev_node = OPEN; - int prev_switch = OPEN; - int to_node = OPEN; - for (const t_trace* trace = head; trace != nullptr; trace = trace->next) { - to_node = trace->index; - - auto conn = std::make_tuple(prev_node, prev_switch, to_node); - if (prev_switch != OPEN) { - //Not end of branch - if (!route_tree_connections.count(conn)) { - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Route tree missing traceback connection: node %d -> %d (switch %d)\n", - prev_node, to_node, prev_switch); - } else { - route_tree_connections.erase(route_tree_connections.lower_bound(conn)); //Remove the first found connections - } - } - - prev_node = trace->index; - prev_switch = trace->iswitch; - } - - if (!route_tree_connections.empty()) { - std::string msg = "Found route tree connection(s) not in traceback:\n"; - for (auto conn : route_tree_connections) { - std::tie(prev_node, prev_switch, to_node) = conn; - msg += vtr::string_fmt("\tnode %d -> %d (switch %d)\n", prev_node, to_node, prev_switch); - } - - VPR_FATAL_ERROR(VPR_ERROR_ROUTE, msg.c_str()); - } - - return true; -} - -void collect_route_tree_connections(const t_rt_node* node, std::multiset>& connections) { - if (node) { - //Record reaching connection - int prev_node = OPEN; - int prev_switch = OPEN; - int to_node = node->inode; - if (node->parent_node) { - prev_node = node->parent_node->inode; - prev_switch = node->parent_switch; - } - auto conn = std::make_tuple(prev_node, prev_switch, to_node); - connections.insert(conn); - - //Recurse - for (auto edge = node->u.child_list; edge != nullptr; edge = edge->next) { - collect_route_tree_connections(edge->child, connections); - } - } -} - -t_rt_node* find_sink_rt_node(const Netlist<>& net_list, t_rt_node* rt_root, ParentNetId net_id, ParentPinId sink_pin) { - //Given the net_id and the sink_pin, this two-step function finds a pointer to the - //route tree sink corresponding to sink_pin. This function constitutes the first step, - //in which, we loop through the pins of the net and terminate the search once the mapping - //of (net_id, ipin) -> sink_pin is found. Conveniently, the pair (net_id, ipin) can - //be further translated to the index of the routing resource node sink_rr_inode. - //In the second step, we pass the root of the route tree and sink_rr_inode in order to - //recursively traverse the route tree until we reach the sink node that corresponds - //to sink_rr_inode. - - auto& route_ctx = g_vpr_ctx.routing(); - - int ipin = net_list.pin_net_index(sink_pin); - int sink_rr_inode = route_ctx.net_rr_terminals[net_id][ipin]; //obtain the value of the routing resource sink - - t_rt_node* sink_rt_node = find_sink_rt_node_recurr(rt_root, sink_rr_inode); //find pointer to route tree node corresponding to sink_rr_inode - VTR_ASSERT(sink_rt_node); - return sink_rt_node; -} -t_rt_node* find_sink_rt_node_recurr(t_rt_node* node, int sink_rr_inode) { - if (node->inode == sink_rr_inode) { //check if current node matches sink_rr_inode - return node; - } - - for (t_linked_rt_edge* edge = node->u.child_list; edge != nullptr; edge = edge->next) { - t_rt_node* found_node = find_sink_rt_node_recurr(edge->child, sink_rr_inode); //process each of the children - if (found_node && found_node->inode == sink_rr_inode) { - //If the sink has been found downstream in the branch, we would like to immediately exit the search - return found_node; - } - } - return nullptr; //We have not reached the sink node -} diff --git a/vpr/src/route/route_tree_timing.h b/vpr/src/route/route_tree_timing.h deleted file mode 100644 index d22c28a8e81..00000000000 --- a/vpr/src/route/route_tree_timing.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once -#include -#include "route_tree_type.h" -#include "connection_based_routing.h" -#include "route_common.h" -#include "spatial_route_tree_lookup.h" -#include "timing_util.h" - -/**************** Subroutines exported by route_tree_timing.c ***************/ - -//Returns true if allocated -bool alloc_route_tree_timing_structs(bool exists_ok = false); - -void free_route_tree_timing_structs(); - -t_rt_node* init_route_tree_to_source(const ParentNetId& inet); - -/* - * Puts the rt_nodes and edges in the tree rooted at rt_node back on the - * free lists. Recursive, depth-first post-order traversal. - */ -void free_route_tree(t_rt_node* rt_node); -void print_route_tree(const t_rt_node* rt_node); -void print_route_tree(const t_rt_node* rt_node, int depth); - -t_rt_node* update_route_tree(t_heap* hptr, int target_net_pin_index, SpatialRouteTreeLookup* spatial_rt_lookup, bool is_flat); - -void update_net_delays_from_route_tree(float* net_delay, - const Netlist<>& net_list, - const t_rt_node* const* rt_node_of_sink, - ParentNetId inet, - TimingInfo* timing_info, - NetPinTimingInvalidator* pin_timing_invalidator); - -void load_route_tree_Tdel(t_rt_node* rt_root, float Tarrival); -void load_route_tree_rr_route_inf(t_rt_node* root); - -t_rt_node* init_route_tree_to_source_no_net(int inode); - -void add_route_tree_to_rr_node_lookup(t_rt_node* node); - -bool verify_route_tree(t_rt_node* root); -bool verify_traceback_route_tree_equivalent(const t_trace* trace_head, const t_rt_node* rt_root); - -t_rt_node* find_sink_rt_node(const Netlist<>& net_list, t_rt_node* rt_root, ParentNetId net_id, ParentPinId sink_pin); -t_rt_node* find_sink_rt_node_recurr(t_rt_node* node, int sink_inode); - -/********** Incremental reroute ***********/ -// instead of ripping up a net that has some congestion, cut the branches -// that don't legally lead to a sink and start routing with that partial route tree - -void print_edge(const t_linked_rt_edge* edge); -void print_route_tree_node(const t_rt_node* rt_root); -void print_route_tree_inf(const t_rt_node* rt_root); -void print_route_tree_congestion(const t_rt_node* rt_root); - -t_rt_node* traceback_to_route_tree(ParentNetId inet, bool is_flat); - -t_rt_node* traceback_to_route_tree(ParentNetId inet, std::vector* non_config_node_set_usage, bool is_flat); -t_rt_node* traceback_to_route_tree(t_trace* head, bool is_flat); -t_rt_node* traceback_to_route_tree(t_trace* head, - std::vector* non_config_node_set_usage, - bool is_flat); - -t_trace* traceback_from_route_tree(ParentNetId inet, - const t_rt_node* root, - int num_routed_sinks); - -// Prune route tree -// -// Note that non-configurable node will not be pruned unless the node is -// being totally ripped up, or the node is congested. -t_rt_node* prune_route_tree(t_rt_node* rt_root, CBRR& connections_inf); - -// Prune route tree -// -// Note that non-configurable nodes will be pruned if -// non_config_node_set_usage is provided. prune_route_tree will update -// non_config_node_set_usage after pruning. -t_rt_node* prune_route_tree(t_rt_node* rt_root, - CBRR& connections_inf, - std::vector* non_config_node_set_usage); - -void pathfinder_update_cost_from_route_tree(const t_rt_node* rt_root, int add_or_sub); - -bool is_equivalent_route_tree(const t_rt_node* rt_root, const t_rt_node* cloned_rt_root); -bool is_valid_skeleton_tree(const t_rt_node* rt_root); -bool is_valid_route_tree(const t_rt_node* rt_root); -bool is_uncongested_route_tree(const t_rt_node* root); -float load_new_subtree_C_downstream(t_rt_node* rt_node); -void load_new_subtree_R_upstream(t_rt_node* rt_node); diff --git a/vpr/src/route/route_tree_type.h b/vpr/src/route/route_tree_type.h deleted file mode 100644 index edf2cebb393..00000000000 --- a/vpr/src/route/route_tree_type.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -/************** Types and defines exported by route_tree_timing.c ************/ -struct t_rt_node; - -/* Linked list listing the children of an rt_node. * - * child: Pointer to an rt_node (child of the current node). * - * iswitch: Index of the switch type used to connect to the child node. * - * next: Pointer to the next linked_rt_edge in the linked list (allows * - * you to get the next child of the current rt_node). */ -struct t_linked_rt_edge { - t_rt_node* child; - short iswitch; - t_linked_rt_edge* next; -}; - -/* Structure describing one node in a routing tree (used to get net delays * - * incrementally during routing, as pieces are being added). * - * u.child_list: Pointer to a linked list of linked_rt_edge. Each one of * - * the linked list entries gives a child of this node. * - * u.next: Used only when this node is on the free list. Gives the next * - * node on the free list. * - * parent_node: Pointer to the rt_node that is this node's parent (used to * - * make bottom to top traversals). * - * re_expand: Should this node be put on the heap as part of the partial * - * routing to act as a source for subsequent connections? * - * parent_switch: Index of the switch type driving this node (by its * - * parent). * - * inode: index (ID) of the rr_node that corresponds to this rt_node. * - * net_pin_index: Net pin index associated with the rt_node. This value - * ranges from 1 to fanout [1..num_pins-1]. For cases when * - * different speed paths are taken to the same SINK for * - * different pins, inode cannot uniquely identify each SINK, * - * so the net pin index guarantees an unique identification * - * for each SINK rt_node. For non-SINK nodes and for SINK * - * nodes with no associated net pin index, (i.e. special * - * SINKs like the source of a clock tree which do not * - * correspond to an actual netlist connection), the value * - * for this member should be set to OPEN (-1). * - * C_downstream: Total downstream capacitance from this rt_node. That is, * - * the total C of the subtree rooted at the current node, * - * including the C of the current node. * - * R_upstream: Total upstream resistance from this rt_node to the net * - * source, including any device_ctx.rr_nodes[].R of this node. * - * Tdel: Time delay for the signal to get from the net source to this node. * - * Includes the time to go through this node. */ -struct t_rt_node { - union { - t_linked_rt_edge* child_list; - t_rt_node* next; - } u; - t_rt_node* parent_node; - short parent_switch; - bool re_expand; - int inode; - int net_pin_index; - float C_downstream; - float R_upstream; - float Tdel; -}; diff --git a/vpr/src/route/route_util.cpp b/vpr/src/route/route_util.cpp index e1cb1c54349..4c316278ad2 100644 --- a/vpr/src/route/route_util.cpp +++ b/vpr/src/route/route_util.cpp @@ -12,31 +12,28 @@ vtr::Matrix calculate_routing_usage(t_rr_type rr_type, bool is_flat) { vtr::Matrix usage({{device_ctx.grid.width(), device_ctx.grid.height()}}, 0.); //Collect all the in-use RR nodes - std::set rr_nodes; + std::set rr_nodes; for (auto net : cluster_ctx.clb_nlist.nets()) { - auto par_net_id = get_cluster_net_parent_id(g_vpr_ctx.atom().lookup, net, is_flat); - t_trace* tptr = route_ctx.trace[par_net_id].head; - while (tptr != nullptr) { - int inode = tptr->index; + auto parent_id = get_cluster_net_parent_id(g_vpr_ctx.atom().lookup, net, is_flat); - if (rr_graph.node_type(RRNodeId(inode)) == rr_type) { - rr_nodes.insert(inode); + if (!route_ctx.route_trees[parent_id]) + continue; + for (auto& rt_node : route_ctx.route_trees[parent_id].value().all_nodes()) { + if (rr_graph.node_type(rt_node.inode) == rr_type) { + rr_nodes.insert(rt_node.inode); } - tptr = tptr->next; } } //Record number of used resources in each x/y channel - for (int inode : rr_nodes) { - RRNodeId rr_node = RRNodeId(inode); - + for (RRNodeId rr_node : rr_nodes) { if (rr_type == CHANX) { VTR_ASSERT(rr_graph.node_type(rr_node) == CHANX); VTR_ASSERT(rr_graph.node_ylow(rr_node) == rr_graph.node_yhigh(rr_node)); int y = rr_graph.node_ylow(rr_node); for (int x = rr_graph.node_xlow(rr_node); x <= rr_graph.node_xhigh(rr_node); ++x) { - usage[x][y] += route_ctx.rr_node_route_inf[inode].occ(); + usage[x][y] += route_ctx.rr_node_route_inf[size_t(rr_node)].occ(); } } else { VTR_ASSERT(rr_type == CHANY); @@ -45,7 +42,7 @@ vtr::Matrix calculate_routing_usage(t_rr_type rr_type, bool is_flat) { int x = rr_graph.node_xlow(rr_node); for (int y = rr_graph.node_ylow(rr_node); y <= rr_graph.node_yhigh(rr_node); ++y) { - usage[x][y] += route_ctx.rr_node_route_inf[inode].occ(); + usage[x][y] += route_ctx.rr_node_route_inf[size_t(rr_node)].occ(); } } } diff --git a/vpr/src/route/router_delay_profiling.cpp b/vpr/src/route/router_delay_profiling.cpp index 67684b303d6..a2b5faa4b75 100644 --- a/vpr/src/route/router_delay_profiling.cpp +++ b/vpr/src/route/router_delay_profiling.cpp @@ -1,16 +1,13 @@ #include "router_delay_profiling.h" #include "globals.h" -#include "route_tree_type.h" #include "route_common.h" #include "route_timing.h" -#include "route_tree_timing.h" #include "route_export.h" +#include "route_tree.h" #include "rr_graph.h" #include "vtr_time.h" #include "draw.h" -static t_rt_node* setup_routing_resources_no_net(int source_node); - RouterDelayProfiler::RouterDelayProfiler(const Netlist<>& net_list, const RouterLookahead* lookahead, bool is_flat) @@ -45,7 +42,7 @@ bool RouterDelayProfiler::calculate_delay(int source_node, int sink_node, const //rr_graph.node_ylow(RRNodeId(sink_node)), //rr_node_arch_name(sink_node).c_str())); - t_rt_node* rt_root = setup_routing_resources_no_net(source_node); + RouteTree tree((RRNodeId(source_node))); enable_router_debug(router_opts, ParentNetId(), sink_node, 0, &router_); /* Update base costs according to fanout and criticality rules */ @@ -75,7 +72,7 @@ bool RouterDelayProfiler::calculate_delay(int source_node, int sink_node, const false, std::unordered_map()); std::tie(found_path, cheapest) = router_.timing_driven_route_connection_from_route_tree( - rt_root, + tree.root(), sink_node, cost_params, bounding_box, @@ -85,13 +82,13 @@ bool RouterDelayProfiler::calculate_delay(int source_node, int sink_node, const if (found_path) { VTR_ASSERT(cheapest.index == sink_node); - t_rt_node* rt_node_of_sink = update_route_tree(&cheapest, OPEN, nullptr, is_flat_); + vtr::optional rt_node_of_sink; + std::tie(std::ignore, rt_node_of_sink) = tree.update_from_heap(&cheapest, OPEN, nullptr, is_flat_); //find delay *net_delay = rt_node_of_sink->Tdel; - VTR_ASSERT_MSG(route_ctx.rr_node_route_inf[rt_root->inode].occ() <= rr_graph.node_capacity(RRNodeId(rt_root->inode)), "SOURCE should never be congested"); - free_route_tree(rt_root); + VTR_ASSERT_MSG(route_ctx.rr_node_route_inf[size_t(tree.root().inode)].occ() <= rr_graph.node_capacity(tree.root().inode), "SOURCE should never be congested"); } //VTR_LOG("Explored %zu of %zu (%.2f) RR nodes: path delay %g\n", router_stats.heap_pops, device_ctx.rr_nodes.size(), float(router_stats.heap_pops) / device_ctx.rr_nodes.size(), *net_delay); @@ -113,7 +110,7 @@ std::vector calculate_all_path_delays_from_rr_node(int src_rr_node, std::vector path_delays_to(device_ctx.rr_graph.num_nodes(), std::numeric_limits::quiet_NaN()); - t_rt_node* rt_root = setup_routing_resources_no_net(src_rr_node); + RouteTree tree((RRNodeId(src_rr_node))); t_bb bounding_box; bounding_box.xmin = 0; @@ -145,14 +142,12 @@ std::vector calculate_all_path_delays_from_rr_node(int src_rr_node, is_flat); RouterStats router_stats; ConnectionParameters conn_params(ParentNetId::INVALID(), OPEN, false, std::unordered_map()); - std::vector shortest_paths = router.timing_driven_find_all_shortest_paths_from_route_tree(rt_root, + std::vector shortest_paths = router.timing_driven_find_all_shortest_paths_from_route_tree(tree.root(), cost_params, bounding_box, router_stats, conn_params); - free_route_tree(rt_root); - VTR_ASSERT(shortest_paths.size() == device_ctx.rr_graph.num_nodes()); for (int sink_rr_node = 0; sink_rr_node < (int)device_ctx.rr_graph.num_nodes(); ++sink_rr_node) { if (sink_rr_node == src_rr_node) { @@ -163,14 +158,13 @@ std::vector calculate_all_path_delays_from_rr_node(int src_rr_node, VTR_ASSERT(shortest_paths[sink_rr_node].index == sink_rr_node); //Build the routing tree to get the delay - rt_root = setup_routing_resources_no_net(src_rr_node); - t_rt_node* rt_node_of_sink = update_route_tree(&shortest_paths[sink_rr_node], OPEN, nullptr, router_opts.flat_routing); + tree = RouteTree(RRNodeId(src_rr_node)); + vtr::optional rt_node_of_sink; + std::tie(std::ignore, rt_node_of_sink) = tree.update_from_heap(&shortest_paths[sink_rr_node], OPEN, nullptr, router_opts.flat_routing); - VTR_ASSERT(rt_node_of_sink->inode == sink_rr_node); + VTR_ASSERT(rt_node_of_sink->inode == RRNodeId(sink_rr_node)); path_delays_to[sink_rr_node] = rt_node_of_sink->Tdel; - - free_route_tree(rt_root); } } router.reset_path_costs(); @@ -204,20 +198,6 @@ std::vector calculate_all_path_delays_from_rr_node(int src_rr_node, return path_delays_to; } -static t_rt_node* setup_routing_resources_no_net(int source_node) { - /* Build and return a partial route tree from the legal connections from last iteration. - * along the way do: - * update pathfinder costs to be accurate to the partial route tree - * update the net's traceback to be accurate to the partial route tree - * find and store the pins that still need to be reached in incremental_rerouting_resources.remaining_targets - * find and store the rt nodes that have been reached in incremental_rerouting_resources.reached_rt_sinks - * mark the rr_node sinks as targets to be reached */ - - t_rt_node* rt_root = init_route_tree_to_source_no_net(source_node); - - return rt_root; -} - void alloc_routing_structs(t_chan_width chan_width, const t_router_opts& router_opts, t_det_routing_arch* det_routing_arch, @@ -248,13 +228,8 @@ void alloc_routing_structs(t_chan_width chan_width, is_flat); alloc_and_load_rr_node_route_structs(); - - alloc_route_tree_timing_structs(); } -void free_routing_structs(const Netlist<>& net_list) { +void free_routing_structs() { free_route_structs(); - free_trace_structs(net_list); - - free_route_tree_timing_structs(); } diff --git a/vpr/src/route/router_delay_profiling.h b/vpr/src/route/router_delay_profiling.h index 44f4fcde505..ac2b507094b 100644 --- a/vpr/src/route/router_delay_profiling.h +++ b/vpr/src/route/router_delay_profiling.h @@ -34,6 +34,6 @@ void alloc_routing_structs(t_chan_width chan_width, const int num_directs, bool is_flat); -void free_routing_structs(const Netlist<>& net_list); +void free_routing_structs(); #endif /* ROUTER_DELAY_PROFILING_H_ */ diff --git a/vpr/src/route/router_lookahead_map.cpp b/vpr/src/route/router_lookahead_map.cpp index ea031a5496a..d029900f565 100644 --- a/vpr/src/route/router_lookahead_map.cpp +++ b/vpr/src/route/router_lookahead_map.cpp @@ -8,9 +8,9 @@ * multiple interconnected wire types. * * The lookahead in this file performs undirected Dijkstra searches to evaluate many paths through the routing network, - * starting from all the different wire types in the routing architecture. This ensures the lookahead captures the - * effect of inter-wire connectivity. This information is then reduced into a delta_x delta_y based lookup table for - * reach source wire type (f_cost_map). This is used for estimates from CHANX/CHANY -> SINK nodes. See Section 3.2.4 + * starting from all the different wire types in the routing architecture. This ensures the lookahead captures the + * effect of inter-wire connectivity. This information is then reduced into a delta_x delta_y based lookup table for + * reach source wire type (f_cost_map). This is used for estimates from CHANX/CHANY -> SINK nodes. See Section 3.2.4 * in Oleg Petelin's MASc thesis (2016) for more discussion. * * To handle estimates starting from SOURCE/OPIN's the lookahead also creates a small side look-up table of the wire types @@ -1184,7 +1184,7 @@ static void adjust_rr_pin_position(const RRNodeId rr, int& x, int& y) { * | | | | * V +---------+ | * A - * B-----------> + * B-----------> * * So wires are located as follows: * diff --git a/vpr/src/route/spatial_route_tree_lookup.cpp b/vpr/src/route/spatial_route_tree_lookup.cpp index 60e9b0eb721..3d3f7a25460 100644 --- a/vpr/src/route/spatial_route_tree_lookup.cpp +++ b/vpr/src/route/spatial_route_tree_lookup.cpp @@ -5,7 +5,7 @@ SpatialRouteTreeLookup build_route_tree_spatial_lookup(const Netlist<>& net_list, const vtr::vector& net_bound_box, ParentNetId net, - t_rt_node* rt_root) { + const RouteTreeNode& rt_root) { constexpr float BIN_AREA_PER_SINK_FACTOR = 4; auto& device_ctx = g_vpr_ctx.device(); @@ -29,12 +29,12 @@ SpatialRouteTreeLookup build_route_tree_spatial_lookup(const Netlist<>& net_list return spatial_lookup; } -//Adds the sub-tree rooted at rt_node to the spatial look-up -void update_route_tree_spatial_lookup_recur(t_rt_node* rt_node, SpatialRouteTreeLookup& spatial_lookup) { +// Adds the sub-tree rooted at rt_node to the spatial look-up +void update_route_tree_spatial_lookup_recur(const RouteTreeNode& rt_node, SpatialRouteTreeLookup& spatial_lookup) { auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; - RRNodeId rr_node = (RRNodeId)rt_node->inode; + RRNodeId rr_node = (RRNodeId)rt_node.inode; int bin_xlow = grid_to_bin_x(rr_graph.node_xlow(rr_node), spatial_lookup); int bin_ylow = grid_to_bin_y(rr_graph.node_ylow(rr_node), spatial_lookup); @@ -43,10 +43,10 @@ void update_route_tree_spatial_lookup_recur(t_rt_node* rt_node, SpatialRouteTree spatial_lookup[bin_xlow][bin_ylow].push_back(rt_node); - //We current look at the start/end locations of the RR nodes and add the node - //to both bins if they are different + // We currently look at the start/end locations of the RR nodes and add the node + // to both bins if they are different // - //TODO: Depending on bin size, long wires may end up being added only to bins at + // TODO: Depending on bin size, long wires may end up being added only to bins at // their start/end and may pass through bins along their length to which they // are not added. If this becomes an issues, reconsider how we add nodes to // bins @@ -54,9 +54,9 @@ void update_route_tree_spatial_lookup_recur(t_rt_node* rt_node, SpatialRouteTree spatial_lookup[bin_xhigh][bin_yhigh].push_back(rt_node); } - //Recurse - for (t_linked_rt_edge* rt_edge = rt_node->u.child_list; rt_edge != nullptr; rt_edge = rt_edge->next) { - update_route_tree_spatial_lookup_recur(rt_edge->child, spatial_lookup); + // Recurse + for (auto& child : rt_node.child_nodes()) { + update_route_tree_spatial_lookup_recur(child, spatial_lookup); } } @@ -76,10 +76,10 @@ size_t grid_to_bin_y(size_t grid_y, const SpatialRouteTreeLookup& spatial_lookup return grid_y / bin_height; } -bool validate_route_tree_spatial_lookup(t_rt_node* rt_node, const SpatialRouteTreeLookup& spatial_lookup) { +bool validate_route_tree_spatial_lookup(const RouteTreeNode& rt_node, const SpatialRouteTreeLookup& spatial_lookup) { auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; - RRNodeId rr_node = (RRNodeId)rt_node->inode; + RRNodeId rr_node = (RRNodeId)rt_node.inode; int bin_xlow = grid_to_bin_x(rr_graph.node_xlow(rr_node), spatial_lookup); int bin_ylow = grid_to_bin_y(rr_graph.node_ylow(rr_node), spatial_lookup); @@ -92,19 +92,19 @@ bool validate_route_tree_spatial_lookup(t_rt_node* rt_node, const SpatialRouteTr if (std::find(low_bin_rt_nodes.begin(), low_bin_rt_nodes.end(), rt_node) == low_bin_rt_nodes.end()) { valid = false; VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Failed to find route tree node %d at (low coord %d,%d) in spatial lookup [bin %d,%d]", - rt_node->inode, rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node), bin_xlow, bin_ylow); + rt_node.inode, rr_graph.node_xlow(rr_node), rr_graph.node_ylow(rr_node), bin_xlow, bin_ylow); } auto& high_bin_rt_nodes = spatial_lookup[bin_xhigh][bin_yhigh]; if (std::find(high_bin_rt_nodes.begin(), high_bin_rt_nodes.end(), rt_node) == high_bin_rt_nodes.end()) { valid = false; VPR_FATAL_ERROR(VPR_ERROR_ROUTE, "Failed to find route tree node %d at (high coord %d,%d) in spatial lookup [bin %d,%d]", - rt_node->inode, rr_graph.node_xhigh(rr_node), rr_graph.node_yhigh(rr_node), bin_xhigh, bin_yhigh); + rt_node.inode, rr_graph.node_xhigh(rr_node), rr_graph.node_yhigh(rr_node), bin_xhigh, bin_yhigh); } - //Recurse - for (t_linked_rt_edge* rt_edge = rt_node->u.child_list; rt_edge != nullptr; rt_edge = rt_edge->next) { - valid &= validate_route_tree_spatial_lookup(rt_edge->child, spatial_lookup); + // Recurse + for (auto& child : rt_node.child_nodes()) { + valid &= validate_route_tree_spatial_lookup(child, spatial_lookup); } return valid; diff --git a/vpr/src/route/spatial_route_tree_lookup.h b/vpr/src/route/spatial_route_tree_lookup.h index dd5575fcda5..9ac1ac3c23f 100644 --- a/vpr/src/route/spatial_route_tree_lookup.h +++ b/vpr/src/route/spatial_route_tree_lookup.h @@ -6,20 +6,20 @@ #include "vtr_ndmatrix.h" #include "netlist.h" -#include "route_tree_type.h" +#include "route_tree_fwd.h" -typedef vtr::Matrix> SpatialRouteTreeLookup; +typedef vtr::Matrix>> SpatialRouteTreeLookup; SpatialRouteTreeLookup build_route_tree_spatial_lookup(const Netlist<>& net_list, const vtr::vector& net_bound_box, ParentNetId net, - t_rt_node* rt_root); + const RouteTreeNode& rt_root); -void update_route_tree_spatial_lookup_recur(t_rt_node* rt_node, SpatialRouteTreeLookup& spatial_lookup); +void update_route_tree_spatial_lookup_recur(const RouteTreeNode& rt_node, SpatialRouteTreeLookup& spatial_lookup); size_t grid_to_bin_x(size_t grid_x, const SpatialRouteTreeLookup& spatial_lookup); size_t grid_to_bin_y(size_t grid_y, const SpatialRouteTreeLookup& spatial_lookup); -bool validate_route_tree_spatial_lookup(t_rt_node* rt_root, const SpatialRouteTreeLookup& spatial_lookup); +bool validate_route_tree_spatial_lookup(const RouteTreeNode& rt_node, const SpatialRouteTreeLookup& spatial_lookup); #endif diff --git a/vpr/src/timing/VprTimingGraphResolver.cpp b/vpr/src/timing/VprTimingGraphResolver.cpp index 36b8e01c99f..cdc1124ef6e 100644 --- a/vpr/src/timing/VprTimingGraphResolver.cpp +++ b/vpr/src/timing/VprTimingGraphResolver.cpp @@ -124,12 +124,12 @@ std::vector VprTimingGraphResolver::interconnect_delay_br std::vector components; - //We assume that the delay calculator has already cached all of the relevant delays, - //we just retrieve the cached values. This assumption greatly simplifies the calculation - //process and avoids us duplicating the complex delay calculation logic from the delay - //calcualtor. + // We assume that the delay calculator has already cached all of the relevant delays, + // we just retrieve the cached values. This assumption greatly simplifies the calculation + // process and avoids us duplicating the complex delay calculation logic from the delay + // calculator. // - //However note that this does couple this code tightly with the delay calculator implementation. + // However note that this does couple this code tightly with the delay calculator implementation. //Force delay calculation to ensure results are cached (redundant if already up-to-date) delay_calc_.atom_net_delay(timing_graph_, edge, delay_type); @@ -237,23 +237,23 @@ std::vector VprTimingGraphResolver::interconnect_delay_br interblock_component.delay = net_delay; if ((detail_level() == e_timing_report_detail::DETAILED_ROUTING || detail_level() == e_timing_report_detail::DEBUG) - && !route_ctx.trace.empty()) { - //check if detailed timing report has been selected and that the vector of tracebacks - //is not empty. - if (route_ctx.trace[src_net].head != nullptr) { - //the traceback is not a nullptr, so we find the path of the net from source to sink. - //Note that the previously declared interblock_component will not be added to the - //vector of net components. + && !route_ctx.route_trees.empty()) { + // check if detailed timing report has been selected and that the routing + // is not empty. + if (route_ctx.route_trees[src_net]) { + // the route tree exists, so we find the path of the net from source to sink. + // Note that the previously declared interblock_component will not be added to the + // vector of net components. get_detailed_interconnect_components(components, src_net, sink_pin); } else { - //the traceback is a nullptr which means this is an unrouted net as part of global routing. - //We add the tag global net, and push the interblock into the vector of net components. + // this is an unrouted net as part of global routing. + // We add the tag global net, and push the interblock into the vector of net components. interblock_component.type_name += ":global net"; components.push_back(interblock_component); } } else { - //for aggregated and netlist modes, we simply add the previously declared interblock_component - //to the vector. + // for aggregated and netlist modes, we simply add the previously declared interblock_component + // to the vector. components.push_back(interblock_component); } tatum::DelayComponent sink_component; @@ -284,39 +284,40 @@ void VprTimingGraphResolver::get_detailed_interconnect_components(std::vector&)g_vpr_ctx.atom().nlist, rt_root, net_id, (ParentPinId&)sink_pin); //find the sink matching sink_pin - } else { - rt_sink = find_sink_rt_node((const Netlist<>&)g_vpr_ctx.clustering().clb_nlist, rt_root, net_id, (ParentPinId&)sink_pin); //find the sink matching sink_pin - } - get_detailed_interconnect_components_helper(components, rt_sink); //from sink, walk up to source and add net components - free_route_tree(rt_root); + auto& route_ctx = g_vpr_ctx.mutable_routing(); + + if (!route_ctx.route_trees[net_id]) + return; + + auto& netlist = is_flat_ ? (const Netlist<>&)g_vpr_ctx.atom().nlist : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; + + int ipin = netlist.pin_net_index(sink_pin); + RRNodeId sink_rr_inode = RRNodeId(route_ctx.net_rr_terminals[net_id][ipin]); //obtain the value of the routing resource sink + + auto rt_sink = route_ctx.route_trees[net_id].value().find_by_rr_id(sink_rr_inode); + + get_detailed_interconnect_components_helper(components, rt_sink.value()); //from sink, walk up to source and add net components } -void VprTimingGraphResolver::get_detailed_interconnect_components_helper(std::vector& components, t_rt_node* node) const { - /* This routine comprieses the second part of obtaining the interconnect components. - * We begin at the sink node and travel up the tree towards the source. For each node, we would - * like to retreive information such as the routing resource type, index, and incremental delay. +void VprTimingGraphResolver::get_detailed_interconnect_components_helper(std::vector& components, const RouteTreeNode& node) const { + /* This routine comprises the second part of obtaining the interconnect components. + * We begin at the sink node and travel up the tree towards the source. For each node, we would + * like to retrieve information such as the routing resource type, index, and incremental delay. * If the type is a channel, then we retrieve the name of the segment as well as the coordinates - * of its start and endpoint. All of this information is stored in the object DelayComponent, + * of its start and end point. All of this information is stored in the object DelayComponent, * which belong to the vector "components". */ auto& device_ctx = g_vpr_ctx.device(); const auto& rr_graph = device_ctx.rr_graph; - //Declare a separate vector "interconnect_components" to hold the interconnect components. We need - //this because we walk from the sink to the source, we will need to add elements to the front of - //interconnect_components so that we maintain the correct order of net components. - //Illustration: + // Declare a separate vector "interconnect_components" to hold the interconnect components. We need + // this because we walk from the sink to the source, we will need to add elements to the front of + // interconnect_components so that we maintain the correct order of net components. + // Illustration: // INTRA // | // IPIN <-end @@ -326,34 +327,36 @@ void VprTimingGraphResolver::get_detailed_interconnect_components_helper(std::ve // OPIN <-start // | // INTRA - not seen yet - //Pushing a stack steps: - //1. OPIN, 2. CHANX, OPIN 3. IPIN, CHANX, OPIN (order is preserved) - //At this point of the code, the vector "components" contains intrablock components. - //At the end of the module, we will append "interconnect_components" to the end of vector "components". + // Pushing a stack steps: + // 1. OPIN, 2. CHANX, OPIN 3. IPIN, CHANX, OPIN (order is preserved) + // At this point of the code, the vector "components" contains intrablock components. + // At the end of the module, we will append "interconnect_components" to the end of vector "components". std::vector interconnect_components; - while (node != nullptr) { - //Process the current interconnect component if it is of type OPIN, CHANX, CHANY, IPIN - //Only process SOURCE, SINK in debug report mode - auto rr_type = rr_graph.node_type(RRNodeId(node->inode)); + vtr::optional current_node = node; + while (current_node) { + // Process the current interconnect component if it is of type OPIN, CHANX, CHANY, IPIN + // Only process SOURCE, SINK in debug report mode + auto rr_type = rr_graph.node_type(RRNodeId(current_node->inode)); if (rr_type == OPIN || rr_type == IPIN || rr_type == CHANX || rr_type == CHANY || ((rr_type == SOURCE || rr_type == SINK) && (detail_level() == e_timing_report_detail::DEBUG))) { - tatum::DelayComponent net_component; //declare a new instance of DelayComponent + tatum::DelayComponent net_component; // declare a new instance of DelayComponent - net_component.type_name = rr_graph.node_coordinate_to_string(RRNodeId(node->inode)); + net_component.type_name = rr_graph.node_coordinate_to_string(RRNodeId(current_node->inode)); - if (node->parent_node) { - net_component.delay = tatum::Time(node->Tdel - node->parent_node->Tdel); // add the incremental delay + if (current_node->parent()) { + net_component.delay = tatum::Time(current_node->Tdel - current_node->parent()->Tdel); // add the incremental delay } else { - net_component.delay = tatum::Time(0.); //No delay on SOURCE + net_component.delay = tatum::Time(0.); // No delay on SOURCE } - interconnect_components.push_back(net_component); //insert net_component into the front of vector interconnect_component + interconnect_components.push_back(net_component); // insert net_component into the front of vector interconnect_component } - node = node->parent_node; //travel up the tree through the parent + current_node = current_node->parent(); } - components.insert(components.end(), interconnect_components.rbegin(), interconnect_components.rend()); //append the completed "interconnect_component" to "component_vector" + + components.insert(components.end(), interconnect_components.rbegin(), interconnect_components.rend()); // append the completed "interconnect_component" to "component_vector" } diff --git a/vpr/src/timing/VprTimingGraphResolver.h b/vpr/src/timing/VprTimingGraphResolver.h index 5bfee93d8fc..8faea482d10 100644 --- a/vpr/src/timing/VprTimingGraphResolver.h +++ b/vpr/src/timing/VprTimingGraphResolver.h @@ -5,7 +5,6 @@ #include "atom_netlist_fwd.h" #include "atom_lookup.h" #include "AnalysisDelayCalculator.h" -#include "route_tree_timing.h" class VprTimingGraphResolver : public tatum::TimingGraphNameResolver { public: @@ -27,7 +26,7 @@ class VprTimingGraphResolver : public tatum::TimingGraphNameResolver { std::vector interconnect_delay_breakdown(tatum::EdgeId edge, DelayType) const; void get_detailed_interconnect_components(std::vector& components, ParentNetId net_id, ParentPinId sink_pin) const; - void get_detailed_interconnect_components_helper(std::vector& components, t_rt_node* node) const; + void get_detailed_interconnect_components_helper(std::vector& components, const RouteTreeNode& node) const; const AtomNetlist& netlist_; const AtomLookup& netlist_lookup_; diff --git a/vpr/src/timing/net_delay.cpp b/vpr/src/timing/net_delay.cpp index a11298e1185..5420c197769 100644 --- a/vpr/src/timing/net_delay.cpp +++ b/vpr/src/timing/net_delay.cpp @@ -8,18 +8,17 @@ #include "globals.h" #include "net_delay.h" -#include "route_tree_timing.h" /* This module keeps track of the time delays for signals to arrive at * * each pin in every net after timing-driven routing is complete. It * - * achieves this by first constructing the skeleton route tree * + * achieves this by first constructing the skeleton route tree * * from the traceback. Next, to obtain the completed route tree, it * * calls functions to calculate the resistance, capacitance, and time * * delays associated with each node. Then, it recursively traverses * * the tree, copying the time delay from each node into the * * net_delay data structure. Essentially, the delays are calculated * * by building the completed route tree from scratch, and is used * - * to check against the time delays computed incrementally during * + * to check against the time delays computed incrementally during * * timing-driven routing. */ /********************** Variables local to this module ***********************/ @@ -36,10 +35,9 @@ static std::unordered_map ipin_to_Tdel_map; static void load_one_net_delay(const Netlist<>& net_list, NetPinsMatrix& net_delay, - ParentNetId net_id, - bool is_flat); + ParentNetId net_id); -static void load_one_net_delay_recurr(t_rt_node* node, ParentNetId net_id); +static void load_one_net_delay_recurr(const RouteTreeNode& node, ParentNetId net_id); static void load_one_constant_net_delay(const Netlist<>& net_list, NetPinsMatrix& net_delay, @@ -47,7 +45,7 @@ static void load_one_constant_net_delay(const Netlist<>& net_list, float delay_value); /*************************** Subroutine definitions **************************/ -void load_net_delay_from_routing(const Netlist<>& net_list, NetPinsMatrix& net_delay, bool is_flat) { +void load_net_delay_from_routing(const Netlist<>& net_list, NetPinsMatrix& net_delay) { /* This routine loads net_delay[0..nets.size()-1][1..num_pins-1]. Each entry * * is the Elmore delay from the net source to the appropriate sink. Both * * the rr_graph and the routing traceback must be completely constructed * @@ -58,36 +56,32 @@ void load_net_delay_from_routing(const Netlist<>& net_list, NetPinsMatrix if (net_list.net_is_ignored(net_id)) { load_one_constant_net_delay(net_list, net_delay, net_id, 0.); } else { - load_one_net_delay(net_list, net_delay, net_id, is_flat); + load_one_net_delay(net_list, net_delay, net_id); } } } static void load_one_net_delay(const Netlist<>& net_list, NetPinsMatrix& net_delay, - ParentNetId net_id, - bool is_flat) { + ParentNetId net_id) { /* This routine loads delay values for one net in * * net_delay[net_id][1..num_pins-1]. First, from the traceback, it * * constructs the route tree and computes its values for R, C, and Tdel. * - * Next, it walks the route tree recursively, storing the time delays for * + * Next, it walks the route tree recursively, storing the time delays for * * each sink into the map ipin_to_Tdel. Then, while looping through the * * net_delay array we search for the pin index in the map, and * * correspondingly update the entry in net_delay. Finally, it frees the * * route tree and clears the ipin_to_Tdel_map associated with that net. */ - auto& route_ctx = g_vpr_ctx.routing(); + auto& route_ctx = g_vpr_ctx.mutable_routing(); - if (route_ctx.trace[net_id].head == nullptr) { + if (!route_ctx.route_trees[net_id]) { VPR_FATAL_ERROR(VPR_ERROR_TIMING, - "in load_one_net_delay: Traceback for net %lu does not exist.\n", size_t(net_id)); + "in load_one_net_delay: Route tree for net %lu does not exist.\n", size_t(net_id)); } - t_rt_node* rt_root = traceback_to_route_tree(net_id, is_flat); // obtain the root of the tree constructed from the traceback - load_new_subtree_R_upstream(rt_root); // load in the resistance values for the route tree - load_new_subtree_C_downstream(rt_root); // load in the capacitance values for the route tree - load_route_tree_Tdel(rt_root, 0.); // load the time delay values for the route tree - load_one_net_delay_recurr(rt_root, net_id); // recursively traverse the tree and load entries into the ipin_to_Tdel map + RouteTree& tree = route_ctx.route_trees[net_id].value(); + load_one_net_delay_recurr(tree.root(), net_id); // recursively traverse the tree and load entries into the ipin_to_Tdel map for (unsigned int ipin = 1; ipin < net_list.net_pins(net_id).size(); ipin++) { auto itr = ipin_to_Tdel_map.find(ipin); @@ -95,19 +89,18 @@ static void load_one_net_delay(const Netlist<>& net_list, net_delay[net_id][ipin] = itr->second; // search for the value of Tdel in the ipin map and load into net_delay } - free_route_tree(rt_root); // free the route tree ipin_to_Tdel_map.clear(); // clear the map } -static void load_one_net_delay_recurr(t_rt_node* node, ParentNetId net_id) { +static void load_one_net_delay_recurr(const RouteTreeNode& rt_node, ParentNetId net_id) { /* This routine recursively traverses the route tree, and copies the Tdel of the sink_type nodes * * into the map. */ - if (node->net_pin_index != OPEN) { // value of OPEN indicates a non-SINK - ipin_to_Tdel_map[node->net_pin_index] = node->Tdel; // add to the map, process current sink-type node + if (rt_node.net_pin_index != OPEN) { // value of OPEN indicates a non-SINK + ipin_to_Tdel_map[rt_node.net_pin_index] = rt_node.Tdel; // add to the map, process current sink-type node } - for (t_linked_rt_edge* edge = node->u.child_list; edge != nullptr; edge = edge->next) { // process children - load_one_net_delay_recurr(edge->child, net_id); + for (auto& child : rt_node.child_nodes()) { // process children + load_one_net_delay_recurr(child, net_id); } } diff --git a/vpr/src/timing/net_delay.h b/vpr/src/timing/net_delay.h index 2f17732be07..f5a1727cd5c 100644 --- a/vpr/src/timing/net_delay.h +++ b/vpr/src/timing/net_delay.h @@ -5,6 +5,6 @@ #include "vtr_vector.h" #include "vpr_net_pins_matrix.h" -void load_net_delay_from_routing(const Netlist<>& net_list, NetPinsMatrix& net_delay, bool is_flat); +void load_net_delay_from_routing(const Netlist<>& net_list, NetPinsMatrix& net_delay); #endif diff --git a/vpr/src/util/vpr_net_pins_matrix.h b/vpr/src/util/vpr_net_pins_matrix.h index abee2115119..585e7f3873a 100644 --- a/vpr/src/util/vpr_net_pins_matrix.h +++ b/vpr/src/util/vpr_net_pins_matrix.h @@ -1,6 +1,10 @@ #ifndef VPR_NET_PINS_MATRIX_H #define VPR_NET_PINS_MATRIX_H +#include "atom_netlist_fwd.h" +#include "clustered_netlist.h" +#include "clustered_netlist_fwd.h" +#include "netlist_fwd.h" #include "vtr_ragged_matrix.h" template diff --git a/vpr/test/test_connection_router.cpp b/vpr/test/test_connection_router.cpp index 6b87338e257..06dda0497f5 100644 --- a/vpr/test/test_connection_router.cpp +++ b/vpr/test/test_connection_router.cpp @@ -1,3 +1,4 @@ +#include #include "catch2/catch_test_macros.hpp" #include "vpr_api.h" @@ -5,7 +6,6 @@ #include "globals.h" #include "net_delay.h" #include "place_and_route.h" -#include "route_tree_timing.h" #include "timing_place_lookup.h" static constexpr const char kArchFile[] = "../../vtr_flow/arch/timing/k6_frac_N10_mem32K_40nm.xml"; @@ -22,7 +22,7 @@ static float do_one_route(int source_node, bool is_flat = router_opts.flat_routing; auto& device_ctx = g_vpr_ctx.device(); - t_rt_node* rt_root = init_route_tree_to_source_no_net(source_node); + RouteTree tree((RRNodeId(source_node))); // Update base costs according to fanout and criticality rules. update_rr_base_costs(1); @@ -67,7 +67,7 @@ static float do_one_route(int source_node, -1, false, std::unordered_map()); - std::tie(found_path, cheapest) = router.timing_driven_route_connection_from_route_tree(rt_root, + std::tie(found_path, cheapest) = router.timing_driven_route_connection_from_route_tree(tree.root(), sink_node, cost_params, bounding_box, @@ -81,11 +81,9 @@ static float do_one_route(int source_node, REQUIRE(cheapest.index == sink_node); // Get the delay - t_rt_node* rt_node_of_sink = update_route_tree(&cheapest, OPEN, nullptr, router_opts.flat_routing); - delay = rt_node_of_sink->Tdel; - - // Clean up - free_route_tree(rt_root); + vtr::optional rt_node_of_sink; + std::tie(std::ignore, rt_node_of_sink) = tree.update_from_heap(&cheapest, OPEN, nullptr, router_opts.flat_routing); + delay = rt_node_of_sink.value().Tdel; } // Reset for the next router call. @@ -133,9 +131,6 @@ TEST_CASE("connection_router", "[vpr]") { vpr_install_signal_handler(); vpr_initialize_logging(); - bool is_flat = vpr_setup.RouterOpts.flat_routing; - const Netlist<>& net_list = is_flat ? (const Netlist<>&)g_vpr_ctx.atom().nlist : (const Netlist<>&)g_vpr_ctx.clustering().clb_nlist; - // Command line arguments const char* argv[] = { "test_vpr", @@ -187,9 +182,8 @@ TEST_CASE("connection_router", "[vpr]") { REQUIRE(delay < std::numeric_limits::infinity()); // Clean up - free_routing_structs(net_list); - vpr_free_all(net_list, - arch, + free_routing_structs(); + vpr_free_all(arch, vpr_setup); auto& atom_ctx = g_vpr_ctx.mutable_atom(); diff --git a/vpr/test/test_post_verilog.cpp b/vpr/test/test_post_verilog.cpp index c8ef90d6d04..514374b7cbb 100644 --- a/vpr/test/test_post_verilog.cpp +++ b/vpr/test/test_post_verilog.cpp @@ -31,8 +31,8 @@ void do_vpr_flow(const char* input_unc_opt, const char* output_unc_opt) { bool flow_succeeded = vpr_flow(vpr_setup, arch); - free_routing_structs((const Netlist<>&)g_vpr_ctx.clustering().clb_nlist); - vpr_free_all((const Netlist<>&)g_vpr_ctx.clustering().clb_nlist, arch, vpr_setup); + free_routing_structs(); + vpr_free_all(arch, vpr_setup); auto& atom_ctx = g_vpr_ctx.mutable_atom(); diff --git a/vpr/test/test_vpr.cpp b/vpr/test/test_vpr.cpp index 2bee4eced70..b715679f530 100644 --- a/vpr/test/test_vpr.cpp +++ b/vpr/test/test_vpr.cpp @@ -169,7 +169,7 @@ TEST_CASE("read_rr_graph_metadata", "[vpr]") { echo_enabled, echo_file_name, false); - vpr_free_all((const Netlist<>&)g_vpr_ctx.clustering().clb_nlist, arch, vpr_setup); + vpr_free_all(arch, vpr_setup); auto& atom_ctx = g_vpr_ctx.mutable_atom(); free_pack_molecules(atom_ctx.list_of_pack_molecules.release()); @@ -234,7 +234,7 @@ TEST_CASE("read_rr_graph_metadata", "[vpr]") { REQUIRE(value != nullptr); CHECK_THAT(value->as_string().get(&arch.strings), Equals("test edge")); } - vpr_free_all((const Netlist<>&)g_vpr_ctx.clustering().clb_nlist, arch, vpr_setup); + vpr_free_all(arch, vpr_setup); auto& atom_ctx = g_vpr_ctx.mutable_atom(); free_pack_molecules(atom_ctx.list_of_pack_molecules.release()); diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_nightly_test1/vpr_reg_mcnc_equiv/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_nightly_test1/vpr_reg_mcnc_equiv/config/golden_results.txt index f54bd53fea1..5705eff7e4e 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_nightly_test1/vpr_reg_mcnc_equiv/config/golden_results.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_nightly_test1/vpr_reg_mcnc_equiv/config/golden_results.txt @@ -9,7 +9,7 @@ k6_N10_40nm.xml dsip.pre-vpr.blif common 8.98 vpr 63.57 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 95 229 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 65092 229 197 1370 1567 1 535 521 16 16 256 io auto 25.8 MiB 0.40 4561 63.6 MiB 0.86 0.02 2.5005 -628.06 -2.5005 2.5005 0.89 0.00334002 0.00304005 0.274314 0.245709 38 7841 24 1.05632e+07 5.11993e+06 632420. 2470.39 3.55 1.17098 1.0567 7234 21 2954 12412 674078 134874 2.86759 2.86759 -738.271 -2.86759 0 0 795593. 3107.78 0.35 0.41 0.198071 0.181997 k6_N10_40nm.xml elliptic.pre-vpr.blif common 27.44 vpr 75.48 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 230 131 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 77292 131 114 3421 3535 1 1217 475 18 18 324 clb auto 38.4 MiB 1.11 11570 75.5 MiB 1.37 0.02 6.53834 -3919.35 -6.53834 6.53834 1.16 0.00619897 0.00541421 0.469367 0.399191 52 19846 29 1.37969e+07 1.23956e+07 1.06130e+06 3275.60 8.32 2.62385 2.24634 17638 27 8530 40930 2146023 314235 7.29077 7.29077 -4559.02 -7.29077 0 0 1.39738e+06 4312.90 0.73 1.18 0.567664 0.500941 k6_N10_40nm.xml ex1010.pre-vpr.blif common 34.18 vpr 78.71 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 302 10 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 80600 10 10 2659 2669 0 1417 322 20 20 400 clb auto 42.2 MiB 1.81 26744 78.7 MiB 1.58 0.02 6.13037 -60.5222 -6.13037 nan 1.59 0.00849131 0.00704769 0.544255 0.466669 94 47980 40 1.74617e+07 1.6276e+07 2.27873e+06 5696.83 21.77 3.67277 3.11612 42286 31 14568 90613 5743576 639376 6.64251 nan -64.9523 -6.64251 0 0 2.85166e+06 7129.14 0.89 1.61 0.453105 0.398577 - k6_N10_40nm.xml ex5p.pre-vpr.blif common 5.71 vpr 60.71 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 78 8 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 62164 8 63 761 824 0 443 149 11 11 121 clb auto 22.9 MiB 0.38 4411 60.7 MiB 0.21 0.01 3.72172 -164.758 -3.72172 nan 0.32 0.00160985 0.00136124 0.0716221 0.062581 52 7089 41 4.36541e+06 4.20373e+06 358204. 2960.36 2.48 0.684271 0.595377 6610 34 5292 22309 1021152 171105 4.33394 nan -189.86 -4.33394 0 0 471571. 3897.28 0.17 0.50 0.190568 0.170474 + k6_N10_40nm.xml ex5p.pre-vpr.blif common 4.88 vpr 60.52 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 78 8 -1 -1 success v8.0.0-7841-g74dc1fc14-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-4.15.0-197-generic x86_64 2023-06-08T03:59:32 redacted.eecg.utoronto.ca /home/redacted/vtr-verilog-to-routing/vtr_flow/tasks 61976 8 63 761 824 0 443 149 11 11 121 clb auto 22.7 MiB 0.22 4463 60.5 MiB 0.11 0.00 3.76649 -161.881 -3.76649 nan 0.19 0.000590885 0.000466147 0.0315968 0.0270149 68 6642 32 4.36541e+06 4.20373e+06 471571. 3897.28 2.80 0.460993 0.396642 6273 24 4716 21787 907950 138607 3.98923 nan -180.549 -3.98923 0 0 579861. 4792.24 0.12 0.21 0.0712433 0.0650243 k6_N10_40nm.xml frisc.pre-vpr.blif common 33.81 vpr 74.96 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 240 20 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 76764 20 116 3175 3291 1 1254 376 18 18 324 clb auto 38.0 MiB 1.17 15538 75.0 MiB 1.46 0.02 7.84117 -4191.88 -7.84117 7.84117 1.19 0.0074489 0.00630636 0.559765 0.484374 68 26158 40 1.37969e+07 1.29346e+07 1.39738e+06 4312.90 17.07 4.27847 3.66049 21737 27 9074 38700 2043537 292479 8.98443 8.98443 -4756.73 -8.98443 0 0 1.71505e+06 5293.36 0.82 1.17 0.582609 0.512788 k6_N10_40nm.xml misex3.pre-vpr.blif common 6.79 vpr 61.49 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 87 14 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 62968 14 14 828 842 0 488 115 12 12 144 clb auto 23.6 MiB 0.43 4813 61.5 MiB 0.17 0.01 4.15972 -54.4381 -4.15972 nan 0.42 0.00201964 0.00169484 0.0677982 0.0597447 50 7415 30 5.3894e+06 4.68878e+06 421775. 2928.99 2.60 0.700258 0.608016 7122 38 6126 27888 1140071 180016 4.78724 nan -61.1197 -4.78724 0 0 539713. 3748.01 0.20 0.60 0.229754 0.204855 k6_N10_40nm.xml pdc.pre-vpr.blif common 34.32 vpr 79.66 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 307 16 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 81572 16 40 2839 2879 0 1501 363 20 20 400 clb auto 43.2 MiB 1.57 25581 79.7 MiB 1.81 0.03 6.08664 -222.907 -6.08664 nan 1.56 0.00929983 0.00777603 0.622507 0.530418 86 42950 30 1.74617e+07 1.65455e+07 2.10771e+06 5269.28 20.07 4.51046 3.81592 37535 22 13138 75558 3768186 483931 6.62739 nan -240.231 -6.62739 0 0 2.64451e+06 6611.28 0.82 1.18 0.383771 0.344257 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_nightly_test1_odin/vpr_reg_mcnc_equiv/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_nightly_test1_odin/vpr_reg_mcnc_equiv/config/golden_results.txt index f54bd53fea1..43d29c852de 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_nightly_test1_odin/vpr_reg_mcnc_equiv/config/golden_results.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_nightly_test1_odin/vpr_reg_mcnc_equiv/config/golden_results.txt @@ -9,7 +9,7 @@ k6_N10_40nm.xml dsip.pre-vpr.blif common 8.98 vpr 63.57 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 95 229 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 65092 229 197 1370 1567 1 535 521 16 16 256 io auto 25.8 MiB 0.40 4561 63.6 MiB 0.86 0.02 2.5005 -628.06 -2.5005 2.5005 0.89 0.00334002 0.00304005 0.274314 0.245709 38 7841 24 1.05632e+07 5.11993e+06 632420. 2470.39 3.55 1.17098 1.0567 7234 21 2954 12412 674078 134874 2.86759 2.86759 -738.271 -2.86759 0 0 795593. 3107.78 0.35 0.41 0.198071 0.181997 k6_N10_40nm.xml elliptic.pre-vpr.blif common 27.44 vpr 75.48 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 230 131 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 77292 131 114 3421 3535 1 1217 475 18 18 324 clb auto 38.4 MiB 1.11 11570 75.5 MiB 1.37 0.02 6.53834 -3919.35 -6.53834 6.53834 1.16 0.00619897 0.00541421 0.469367 0.399191 52 19846 29 1.37969e+07 1.23956e+07 1.06130e+06 3275.60 8.32 2.62385 2.24634 17638 27 8530 40930 2146023 314235 7.29077 7.29077 -4559.02 -7.29077 0 0 1.39738e+06 4312.90 0.73 1.18 0.567664 0.500941 k6_N10_40nm.xml ex1010.pre-vpr.blif common 34.18 vpr 78.71 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 302 10 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 80600 10 10 2659 2669 0 1417 322 20 20 400 clb auto 42.2 MiB 1.81 26744 78.7 MiB 1.58 0.02 6.13037 -60.5222 -6.13037 nan 1.59 0.00849131 0.00704769 0.544255 0.466669 94 47980 40 1.74617e+07 1.6276e+07 2.27873e+06 5696.83 21.77 3.67277 3.11612 42286 31 14568 90613 5743576 639376 6.64251 nan -64.9523 -6.64251 0 0 2.85166e+06 7129.14 0.89 1.61 0.453105 0.398577 - k6_N10_40nm.xml ex5p.pre-vpr.blif common 5.71 vpr 60.71 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 78 8 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 62164 8 63 761 824 0 443 149 11 11 121 clb auto 22.9 MiB 0.38 4411 60.7 MiB 0.21 0.01 3.72172 -164.758 -3.72172 nan 0.32 0.00160985 0.00136124 0.0716221 0.062581 52 7089 41 4.36541e+06 4.20373e+06 358204. 2960.36 2.48 0.684271 0.595377 6610 34 5292 22309 1021152 171105 4.33394 nan -189.86 -4.33394 0 0 471571. 3897.28 0.17 0.50 0.190568 0.170474 + k6_N10_40nm.xml ex5p.pre-vpr.blif common 4.87 vpr 60.63 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 78 8 -1 -1 success v8.0.0-7841-g74dc1fc14-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-4.15.0-197-generic x86_64 2023-06-08T03:59:32 redacted.eecg.utoronto.ca /home/redacted/vtr-verilog-to-routing/vtr_flow/tasks 62084 8 63 761 824 0 443 149 11 11 121 clb auto 22.8 MiB 0.24 4463 60.6 MiB 0.11 0.00 3.76649 -161.881 -3.76649 nan 0.19 0.000617534 0.000492662 0.031884 0.0273394 68 6642 32 4.36541e+06 4.20373e+06 471571. 3897.28 2.75 0.449263 0.389806 6273 24 4716 21787 907950 138607 3.98923 nan -180.549 -3.98923 0 0 579861. 4792.24 0.12 0.21 0.0713783 0.0651479 k6_N10_40nm.xml frisc.pre-vpr.blif common 33.81 vpr 74.96 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 240 20 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 76764 20 116 3175 3291 1 1254 376 18 18 324 clb auto 38.0 MiB 1.17 15538 75.0 MiB 1.46 0.02 7.84117 -4191.88 -7.84117 7.84117 1.19 0.0074489 0.00630636 0.559765 0.484374 68 26158 40 1.37969e+07 1.29346e+07 1.39738e+06 4312.90 17.07 4.27847 3.66049 21737 27 9074 38700 2043537 292479 8.98443 8.98443 -4756.73 -8.98443 0 0 1.71505e+06 5293.36 0.82 1.17 0.582609 0.512788 k6_N10_40nm.xml misex3.pre-vpr.blif common 6.79 vpr 61.49 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 87 14 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 62968 14 14 828 842 0 488 115 12 12 144 clb auto 23.6 MiB 0.43 4813 61.5 MiB 0.17 0.01 4.15972 -54.4381 -4.15972 nan 0.42 0.00201964 0.00169484 0.0677982 0.0597447 50 7415 30 5.3894e+06 4.68878e+06 421775. 2928.99 2.60 0.700258 0.608016 7122 38 6126 27888 1140071 180016 4.78724 nan -61.1197 -4.78724 0 0 539713. 3748.01 0.20 0.60 0.229754 0.204855 k6_N10_40nm.xml pdc.pre-vpr.blif common 34.32 vpr 79.66 MiB -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 307 16 -1 -1 success 574ed3d-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-5.10.35-v8 x86_64 2023-02-09T03:32:29 gh-actions-runner-vtr-auto-spawned1 /root/vtr-verilog-to-routing/vtr-verilog-to-routing 81572 16 40 2839 2879 0 1501 363 20 20 400 clb auto 43.2 MiB 1.57 25581 79.7 MiB 1.81 0.03 6.08664 -222.907 -6.08664 nan 1.56 0.00929983 0.00777603 0.622507 0.530418 86 42950 30 1.74617e+07 1.65455e+07 2.10771e+06 5269.28 20.07 4.51046 3.81592 37535 22 13138 75558 3768186 483931 6.62739 nan -240.231 -6.62739 0 0 2.64451e+06 6611.28 0.82 1.18 0.383771 0.344257 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_breadth_first/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_breadth_first/config/config.txt deleted file mode 100644 index bc382b5d66c..00000000000 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_breadth_first/config/config.txt +++ /dev/null @@ -1,27 +0,0 @@ -############################################## -# Configuration file for running experiments -############################################## - -# Path to directory of circuits to use -circuits_dir=benchmarks/verilog - -# Path to directory of architectures to use -archs_dir=arch/timing - -# Add circuits to list to sweep -circuit_list_add=stereovision3.v - -# Add architectures to list to sweep -arch_list_add=k6_N10_mem32K_40nm.xml - -# Parse info and how to parse -parse_file=vpr_fixed_chan_width.txt - -# How to parse QoR info -qor_parse_file=qor_fixed_chan_width.txt - -# Pass requirements -pass_requirements_file=pass_requirements_fixed_chan_width.txt - -# Script parameters -script_params = --route_chan_width 100 --router_algorithm breadth_first diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_breadth_first/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_breadth_first/config/golden_results.txt deleted file mode 100644 index 931202bf373..00000000000 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_breadth_first/config/golden_results.txt +++ /dev/null @@ -1,2 +0,0 @@ - arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem parmys_synth_time max_parmys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time placed_wirelength_est place_mem place_time place_quench_time placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time routed_wirelength total_nets_routed total_connections_routed total_heap_pushes total_heap_pops logic_block_area_total logic_block_area_used routing_area_total routing_area_per_tile crit_path_route_success_iteration critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS route_mem crit_path_route_time crit_path_total_timing_analysis_time crit_path_total_sta_time router_lookahead_mem router_lookahead_computation_time - k6_N10_mem32K_40nm.xml stereovision3.v common 1.14 vpr 61.98 MiB -1 -1 0.42 25804 5 0.11 -1 -1 35656 -1 -1 12 10 0 0 success v8.0.0-6989-g4a9293e1e-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 11.3.0 on Linux-5.15.0-58-generic x86_64 2023-02-04T01:37:29 dev /home/dev/Desktop/CAS-Atlantic/vtr-verilog-to-routing 63468 10 2 181 183 1 40 24 6 6 36 clb auto 23.5 MiB 0.04 152 62.0 MiB 0.01 0.00 2.0099 -85.4829 -2.0099 2.0099 0.00 0.000129854 0.000105545 0.00275505 0.00233793 172 -1 -1 -1 -1 646728 646728 138825. 3856.24 10 2.19328 2.19328 -93.2963 -2.19328 0 0 62.0 MiB 0.01 0.00494801 0.00444386 62.0 MiB 0.01 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_custom_grid/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_custom_grid/config/golden_results.txt index 693651214b7..7b03b115bc1 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_custom_grid/config/golden_results.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/strong_custom_grid/config/golden_results.txt @@ -4,6 +4,6 @@ multiwidth_blocks.xml raygentop.v common 19.20 vpr 84.08 MiB -1 -1 2.74 45672 3 0.73 -1 -1 41176 -1 -1 120 236 1 6 success v8.0.0-6989-g4a9293e1e-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 11.3.0 on Linux-5.15.0-58-generic x86_64 2023-02-04T01:37:29 dev /home/dev/Desktop/CAS-Atlantic/vtr-verilog-to-routing 86100 236 305 3195 3007 1 1534 668 19 19 361 io clb auto 47.0 MiB 2.43 13467 84.1 MiB 1.86 0.02 4.32599 -2624.75 -4.32599 4.32599 0.79 0.00426971 0.00382797 0.525267 0.469623 72 27028 27 1.65001e+07 9.39128e+06 1.34933e+06 3737.77 6.51 1.88472 1.7064 22829 16 6300 16262 3617564 891202 4.95899 4.95899 -3105.48 -4.95899 0 0 1.70087e+06 4711.55 0.44 0.86 0.259564 0.24446 non_column.xml raygentop.v common 52.51 vpr 102.34 MiB -1 -1 2.92 45704 4 0.67 -1 -1 40604 -1 -1 121 236 1 6 success v8.0.0-6989-g4a9293e1e-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 11.3.0 on Linux-5.15.0-58-generic x86_64 2023-02-04T01:37:29 dev /home/dev/Desktop/CAS-Atlantic/vtr-verilog-to-routing 104800 236 305 3191 3003 1 1528 669 33 33 1089 io auto 48.3 MiB 2.47 15480 96.8 MiB 1.75 0.03 4.52659 -2781.82 -4.52659 4.52659 3.22 0.00419781 0.00367326 0.463081 0.412393 58 27326 24 5.44432e+07 9.44517e+06 3.56397e+06 3272.70 33.56 2.39271 2.15992 24345 15 6119 15939 2924713 696809 5.17993 5.17993 -3221.9 -5.17993 0 0 4.56271e+06 4189.81 1.71 0.77 0.247711 0.232672 non_column_tall_aspect_ratio.xml raygentop.v common 32.40 vpr 96.02 MiB -1 -1 2.90 45452 4 0.67 -1 -1 40620 -1 -1 121 236 1 6 success v8.0.0-6989-g4a9293e1e-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 11.3.0 on Linux-5.15.0-58-generic x86_64 2023-02-04T01:37:29 dev /home/dev/Desktop/CAS-Atlantic/vtr-verilog-to-routing 98320 236 305 3191 3003 1 1528 669 23 46 1058 io auto 48.6 MiB 2.48 14503 95.9 MiB 1.61 0.02 4.82901 -2870.94 -4.82901 4.82901 3.11 0.00441668 0.00397965 0.436057 0.387679 48 30717 46 5.05849e+07 9.44517e+06 2.97514e+06 2812.04 14.48 1.8282 1.65276 24801 20 7106 18750 3263133 791246 5.37363 5.37363 -3355.22 -5.37363 0 0 3.78429e+06 3576.83 1.13 0.87 0.287444 0.268341 - non_column_wide_aspect_ratio.xml raygentop.v common 46.04 vpr 118.38 MiB -1 -1 2.77 45372 4 0.62 -1 -1 40588 -1 -1 121 236 1 6 success v8.0.0-6989-g4a9293e1e-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 11.3.0 on Linux-5.15.0-58-generic x86_64 2023-02-04T01:37:29 dev /home/dev/Desktop/CAS-Atlantic/vtr-verilog-to-routing 121220 236 305 3191 3003 1 1528 669 53 27 1431 io auto 48.7 MiB 2.42 16502 109.2 MiB 1.66 0.02 4.55927 -2826.25 -4.55927 4.55927 4.28 0.00445482 0.00400601 0.470425 0.420339 48 33090 46 7.18852e+07 9.44517e+06 3.96130e+06 2768.20 25.45 2.45369 2.21254 26699 16 6824 17971 3137391 720955 5.59252 5.59252 -3293.49 -5.59252 0 0 5.06025e+06 3536.17 1.60 0.82 0.267071 0.250762 + non_column_wide_aspect_ratio.xml raygentop.v common 46.35 vpr 121.87 MiB -1 -1 2.26 42128 4 1.80 -1 -1 37796 -1 -1 121 236 1 6 success v8.0.0-7841-g74dc1fc14-dirty release IPO VTR_ASSERT_LEVEL=2 GNU 9.4.0 on Linux-4.15.0-197-generic x86_64 2023-06-08T03:59:32 redacted.eecg.utoronto.ca /home/redacted/vtr-verilog-to-routing/vtr_flow/tasks 124796 236 305 3191 3003 1 1528 669 53 27 1431 io auto 47.4 MiB 1.77 17266 108.5 MiB 1.21 0.01 4.80447 -2891.79 -4.80447 4.80447 3.57 0.00363128 0.00321672 0.388324 0.348591 64 28441 25 7.18852e+07 9.44517e+06 5.23266e+06 3656.65 26.71 2.33031 2.1133 26845 21 6314 17293 2966115 662201 5.72858 5.72858 -3293.82 -5.72858 0 0 6.59521e+06 4608.81 1.73 0.73 0.255886 0.23931 custom_sbloc.xml raygentop.v common 21.27 vpr 83.72 MiB -1 -1 2.79 45776 3 0.70 -1 -1 41444 -1 -1 120 236 1 6 success v8.0.0-6989-g4a9293e1e-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 11.3.0 on Linux-5.15.0-58-generic x86_64 2023-02-04T01:37:29 dev /home/dev/Desktop/CAS-Atlantic/vtr-verilog-to-routing 85732 236 305 3195 3007 1 1534 668 19 19 361 io clb auto 46.9 MiB 2.43 13735 83.7 MiB 1.91 0.02 4.39612 -2675.02 -4.39612 4.39612 0.74 0.0044847 0.00405598 0.53155 0.477899 70 26190 30 1.65001e+07 9.39128e+06 1.29772e+06 3594.79 8.57 2.26361 2.04893 22184 17 6363 16915 3424859 804203 5.11896 5.11896 -3113.51 -5.11896 0 0 1.63975e+06 4542.24 0.42 0.86 0.285233 0.26665 multiple_io_types.xml raygentop.v common 145.09 vpr 502.64 MiB -1 -1 2.61 45672 3 0.68 -1 -1 41308 -1 -1 120 236 1 6 success v8.0.0-6989-g4a9293e1e-dirty release IPO VTR_ASSERT_LEVEL=3 GNU 11.3.0 on Linux-5.15.0-58-generic x86_64 2023-02-04T01:37:29 dev /home/dev/Desktop/CAS-Atlantic/vtr-verilog-to-routing 514708 236 305 3195 3007 1 1534 668 70 70 4900 io_left auto 47.7 MiB 3.30 29048 502.6 MiB 1.09 0.03 4.50877 -3399.68 -4.50877 4.50877 37.93 0.00538678 0.00467793 0.194474 0.172994 58 43008 22 2.76175e+08 9.39128e+06 1.56462e+07 3193.10 74.18 1.66997 1.51029 39061 15 7208 17987 4915378 1188414 5.09776 5.09776 -3855.81 -5.09776 0 0 2.00552e+07 4092.91 5.78 1.00 0.210002 0.197399 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt index 73335b2f919..e0781c9c180 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong/task_list.txt @@ -5,7 +5,6 @@ regression_tests/vtr_reg_strong/strong_bidir regression_tests/vtr_reg_strong/strong_binary regression_tests/vtr_reg_strong/strong_blocks_with_no_inputs regression_tests/vtr_reg_strong/strong_bounding_box -regression_tests/vtr_reg_strong/strong_breadth_first regression_tests/vtr_reg_strong/strong_check_route_options regression_tests/vtr_reg_strong/strong_cin_tie_off regression_tests/vtr_reg_strong/strong_clock_aliases diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/strong_breadth_first/config/config.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/strong_breadth_first/config/config.txt deleted file mode 100644 index e37b6c73600..00000000000 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/strong_breadth_first/config/config.txt +++ /dev/null @@ -1,27 +0,0 @@ -############################################## -# Configuration file for running experiments -############################################## - -# Path to directory of circuits to use -circuits_dir=benchmarks/verilog - -# Path to directory of architectures to use -archs_dir=arch/timing - -# Add circuits to list to sweep -circuit_list_add=stereovision3.v - -# Add architectures to list to sweep -arch_list_add=k6_N10_mem32K_40nm.xml - -# Parse info and how to parse -parse_file=vpr_fixed_chan_width.txt - -# How to parse QoR info -qor_parse_file=qor_fixed_chan_width.txt - -# Pass requirements -pass_requirements_file=pass_requirements_fixed_chan_width.txt - -# Script parameters -script_params = -start odin --route_chan_width 100 --router_algorithm breadth_first diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/strong_breadth_first/config/golden_results.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/strong_breadth_first/config/golden_results.txt deleted file mode 100644 index a0812c7c7d5..00000000000 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/strong_breadth_first/config/golden_results.txt +++ /dev/null @@ -1,2 +0,0 @@ - arch circuit script_params vtr_flow_elapsed_time vtr_max_mem_stage vtr_max_mem error odin_synth_time max_odin_mem parmys_synth_time max_parmys_mem abc_depth abc_synth_time abc_cec_time abc_sec_time max_abc_mem ace_time max_ace_mem num_clb num_io num_memories num_mult vpr_status vpr_revision vpr_build_info vpr_compiler vpr_compiled hostname rundir max_vpr_mem num_primary_inputs num_primary_outputs num_pre_packed_nets num_pre_packed_blocks num_netlist_clocks num_post_packed_nets num_post_packed_blocks device_width device_height device_grid_tiles device_limiting_resources device_name pack_mem pack_time placed_wirelength_est place_mem place_time place_quench_time placed_CPD_est placed_setup_TNS_est placed_setup_WNS_est placed_geomean_nonvirtual_intradomain_critical_path_delay_est place_delay_matrix_lookup_time place_quench_timing_analysis_time place_quench_sta_time place_total_timing_analysis_time place_total_sta_time routed_wirelength total_nets_routed total_connections_routed total_heap_pushes total_heap_pops logic_block_area_total logic_block_area_used routing_area_total routing_area_per_tile crit_path_route_success_iteration critical_path_delay geomean_nonvirtual_intradomain_critical_path_delay setup_TNS setup_WNS hold_TNS hold_WNS route_mem crit_path_route_time crit_path_total_timing_analysis_time crit_path_total_sta_time router_lookahead_mem router_lookahead_computation_time - k6_N10_mem32K_40nm.xml stereovision3.v common 1.82 vpr 61.00 MiB 0.09 9924 -1 -1 4 0.21 -1 -1 33164 -1 -1 19 11 0 0 success v8.0.0-6793-gb52911b9f release IPO VTR_ASSERT_LEVEL=2 GNU 7.5.0 on Linux-4.15.0-167-generic x86_64 2022-11-27T15:52:14 betzgrp-wintermute.eecg.utoronto.ca /home/elgamma8/research/pack_refactor/vtr-verilog-to-routing 62464 11 30 262 292 2 104 60 7 7 49 clb auto 22.4 MiB 0.15 398 61.0 MiB 0.03 0.00 2.2193 -164.973 -2.2193 2.11301 0.00 0.000382612 0.000310344 0.0121909 0.0101582 509 -1 -1 -1 -1 1.07788e+06 1.02399e+06 207176. 4228.08 9 2.79123 2.54943 -179.419 -2.79123 0 0 61.0 MiB 0.05 0.0180466 0.0157502 61.0 MiB 0.04 diff --git a/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/task_list.txt b/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/task_list.txt index 886dd7d1255..f163b140225 100644 --- a/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/task_list.txt +++ b/vtr_flow/tasks/regression_tests/vtr_reg_strong_odin/task_list.txt @@ -5,7 +5,6 @@ regression_tests/vtr_reg_strong_odin/strong_bidir regression_tests/vtr_reg_strong_odin/strong_binary regression_tests/vtr_reg_strong_odin/strong_blocks_with_no_inputs regression_tests/vtr_reg_strong_odin/strong_bounding_box -regression_tests/vtr_reg_strong_odin/strong_breadth_first regression_tests/vtr_reg_strong_odin/strong_check_route_options regression_tests/vtr_reg_strong_odin/strong_cin_tie_off regression_tests/vtr_reg_strong_odin/strong_clock_aliases