summaryrefslogtreecommitdiffstats
path: root/src/import/generic/memory/lib/utils/dimm/mss_timing.H
diff options
context:
space:
mode:
Diffstat (limited to 'src/import/generic/memory/lib/utils/dimm/mss_timing.H')
-rw-r--r--src/import/generic/memory/lib/utils/dimm/mss_timing.H888
1 files changed, 888 insertions, 0 deletions
diff --git a/src/import/generic/memory/lib/utils/dimm/mss_timing.H b/src/import/generic/memory/lib/utils/dimm/mss_timing.H
index 54fac6474..aa1915e3d 100644
--- a/src/import/generic/memory/lib/utils/dimm/mss_timing.H
+++ b/src/import/generic/memory/lib/utils/dimm/mss_timing.H
@@ -22,3 +22,891 @@
/* permissions and limitations under the License. */
/* */
/* IBM_PROLOG_END_TAG */
+
+///
+/// @file mss_timing.H
+/// @brief Determine effective config for mss settings
+///
+// *HWP HWP Owner: Louis Stermole <stermole@us.ibm.com>
+// *HWP FW Owner: Stephen Glancy <sglancy@us.ibm.com>
+// *HWP Team: Memory
+// *HWP Level: 3
+// *HWP Consumed by: HB:FSP
+
+#ifndef _MSS_GEN_TIMING_H_
+#define _MSS_GEN_TIMING_H_
+
+#include <cstdint>
+#include <fapi2.H>
+#include <generic/memory/lib/mss_generic_attribute_getters.H>
+#include <generic/memory/lib/utils/find.H>
+#include <generic/memory/lib/utils/shared/mss_generic_consts.H>
+#include <generic/memory/lib/utils/conversions.H>
+#include <generic/memory/lib/spd/spd_utils.H>
+
+namespace mss
+{
+
+///
+/// @brief Enums for ffdc error callout so we know which function had the error
+///
+enum timing_ffdc_codes
+{
+ TRAS = 0,
+ TFAW_HALF_KB_PAGE_HELPER = 1,
+ TFAW_ONE_KB_PAGE_HELPER = 2,
+ TFAW_TW_KB_PAGE_HELPER = 3,
+ TFAW_SLR_X4_HELPER = 4,
+ TFAW_SLR_X8_HELPER = 5,
+ TRRD_S_SLR = 6,
+ TRRD_L_SLR = 7,
+ TRRD_L_HALF_AND_1KB_PAGE_HELPER = 8,
+ TRRD_S_HALF_AND_1KB_PAGE_HELPER = 9,
+ TRRD_S_2KB_PAGE_HELPER = 10,
+ TRRD_S = 11,
+ TRRD_L = 12,
+ TFAW = 13,
+ TDLLK = 14,
+};
+
+enum refresh_rate : uint8_t
+{
+ REF1X = 1, ///< Refresh rate 1X
+ REF2X = 2, ///< Refresh rate 2X
+ REF4X = 4, ///< Refresh rate 4X
+};
+
+namespace spd
+{
+
+///
+/// @brief Returns clock cycles form picoseconds based on speed bin
+/// Uses SPD rounding algorithm for DDR4
+/// @tparam OT the output type, derrived from the parameters
+/// @param[in] i_freq frequency of the DIMM
+/// @param[in] timing_in_ps timing parameter in ps
+/// @return the clock cycles of timing parameter (provided in ps)
+/// @note Uses DDR4 SPD Contents Rounding Algorithm
+/// @note Item 2220.46
+///
+template<typename OT>
+inline OT ps_to_nck( const uint64_t i_freq, const OT& i_timing_in_ps)
+{
+ OT l_tck_in_ps = 0;
+ OT l_temp_nck = 0;
+
+ // No time if MT/s is 0 (well, infinite really but shut up)
+ if (i_freq == 0)
+ {
+ return 0;
+ }
+
+ FAPI_TRY( freq_to_ps(i_freq, l_tck_in_ps),
+ "Failed freq() accessor" );
+ FAPI_TRY( calc_nck(i_timing_in_ps, l_tck_in_ps, spd::INVERSE_DDR4_CORRECTION_FACTOR, l_temp_nck),
+ "Failed calc_nck()" );
+
+ return l_temp_nck;
+
+fapi_try_exit:
+ // We simply can't work if we get an unsupported value that can't be converted to a valid tCK (clock period)
+ // ...so this should be ok
+ FAPI_ERR("Obtained an invalid MSS_FREQ (%d), or overflow occurred - stopping", i_freq);
+ fapi2::Assert(false);
+
+ // Keeps compiler happy
+ return 0;
+}
+
+///
+/// @brief Returns clock cycles from nanoseconds
+/// Uses SPD rounding algorithm for DDR4
+/// @tparam OT the output type, derrived from the parameters
+/// @param[in] i_freq frequency of the DIMM
+/// @param[out] o_value_nck the end calculation in nck
+/// @return the clock cycles of timing parameter (provided in ps)
+/// @note Uses DDR4 SPD Contents Rounding Algorithm
+/// @note Item 2220.46
+///
+template<typename OT>
+inline OT ns_to_nck( const uint64_t i_freq, const OT& i_timing_in_ns)
+{
+ return ps_to_nck(i_freq, i_timing_in_ns * CONVERT_PS_IN_A_NS);
+}
+
+}// spd
+
+///
+/// @brief Calculates refresh interval time
+/// @param[in] i_mode fine refresh rate mode
+/// @param[in] i_refresh_request_rate refresh rate
+/// @param[out] o_value timing val in ps
+/// @return fapi2::ReturnCode
+///
+inline fapi2::ReturnCode calc_trefi( const refresh_rate i_mode,
+ const uint8_t i_refresh_request_rate,
+ uint64_t& o_timing )
+{
+ // Proposed DDR4 Full spec update(79-4B)
+ // Item No. 1716.78C
+ // pg.46
+ // Table 24 - tREFI and tRFC parameters (in ps)
+ constexpr uint64_t TREFI_BASE = 7800000;
+
+ uint64_t l_refresh_request = 0;
+ constexpr double TEN_PERCENT_FASTER = 0.90;
+
+ switch(i_refresh_request_rate)
+ {
+ case fapi2::ENUM_ATTR_MSS_MRW_REFRESH_RATE_REQUEST_SINGLE:
+ l_refresh_request = TREFI_BASE;
+ break;
+
+ case fapi2::ENUM_ATTR_MSS_MRW_REFRESH_RATE_REQUEST_DOUBLE:
+ // We are truncating but there is no remainder with TREFI_BASE, so we are okay
+ l_refresh_request = TREFI_BASE / 2;
+ break;
+
+ case fapi2::ENUM_ATTR_MSS_MRW_REFRESH_RATE_REQUEST_SINGLE_10_PERCENT_FASTER:
+ // We are truncating but there is no remainder with TREFI_BASE, so we are okay
+ // 10% faster so 100% - 10% = 90%
+ l_refresh_request = TREFI_BASE * TEN_PERCENT_FASTER;
+ break;
+
+ case fapi2::ENUM_ATTR_MSS_MRW_REFRESH_RATE_REQUEST_DOUBLE_10_PERCENT_FASTER:
+ // We are truncating but there is no remainder with TREFI_BASE, so we are okay
+ // 10% faster so 100% - 10% = 90%
+ l_refresh_request = (TREFI_BASE / 2) * TEN_PERCENT_FASTER;
+ break;
+
+ default:
+ // Will catch incorrect MRW value set
+ FAPI_ASSERT(false,
+ fapi2::MSS_INVALID_REFRESH_RATE_REQUEST().set_REFRESH_RATE_REQUEST(i_refresh_request_rate),
+ "Incorrect refresh request rate received: %d ", i_refresh_request_rate);
+ break;
+ }
+
+ o_timing = (l_refresh_request / i_mode);
+
+ FAPI_INF( "tREFI (ps): %d, refresh request (ps): %d, tREFI_base (ps): %d, REF%dX",
+ o_timing, l_refresh_request, TREFI_BASE, i_mode );
+
+ return fapi2::FAPI2_RC_SUCCESS;
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+/// @brief Calculates Minimum Refresh Recovery Delay Time (different logical rank)
+/// @param[in] i_mode fine refresh rate mode
+/// @param[in] i_density SDRAM density
+/// @param[out] o_trfc_in_ps timing val in ps
+/// @return fapi2::FAPI2_RC_SUCCESS iff okay
+///
+inline fapi2::ReturnCode calc_trfc_dlr(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
+ const uint8_t i_refresh_mode,
+ const uint8_t i_density,
+ uint64_t& o_trfc_in_ps)
+{
+ // Proposed DDR4 3DS Addendum
+ // Item No. 1727.58A
+ // pg. 69 - 71
+ // Table 42 - Refresh parameters by logical rank density
+ static const std::vector<std::pair<uint8_t, uint64_t> > TRFC_DLR1 =
+ {
+ // { density in GBs, tRFC4(min) in picoseconds }
+ {4, 90000},
+ {8, 120000},
+ {16, 185000},
+ };
+
+ // Proposed DDR4 3DS Addendum
+ // Item No. 1727.58A
+ // pg. 69 - 71
+ // Table 42 - Refresh parameters by logical rank density
+ static const std::vector<std::pair<uint8_t, uint64_t> > TRFC_DLR2 =
+ {
+ // { density in GBs, tRFC4(min) in picoseconds }
+ {4, 55000},
+ {8, 90000},
+ {16, 120000},
+ };
+
+ // Proposed DDR4 3DS Addendum
+ // Item No. 1727.58A
+ // pg. 69 - 71
+ // Table 42 - Refresh parameters by logical rank density
+ static const std::vector<std::pair<uint8_t, uint64_t> > TRFC_DLR4 =
+ {
+ // { density in GBs, tRFC4(min) in picoseconds }
+ {4, 40000},
+ {8, 55000},
+ {16, 90000},
+ };
+
+ bool l_is_val_found = 0;
+
+ // Selects appropriate tRFC based on fine refresh mode
+ switch(i_refresh_mode)
+ {
+ case fapi2::ENUM_ATTR_MSS_MRW_FINE_REFRESH_MODE_NORMAL:
+ l_is_val_found = find_value_from_key(TRFC_DLR1, i_density, o_trfc_in_ps);
+ break;
+
+ case fapi2::ENUM_ATTR_MSS_MRW_FINE_REFRESH_MODE_FIXED_2X:
+ case fapi2::ENUM_ATTR_MSS_MRW_FINE_REFRESH_MODE_FLY_2X:
+ l_is_val_found = find_value_from_key(TRFC_DLR2, i_density, o_trfc_in_ps);
+ break;
+
+ case fapi2::ENUM_ATTR_MSS_MRW_FINE_REFRESH_MODE_FIXED_4X:
+ case fapi2::ENUM_ATTR_MSS_MRW_FINE_REFRESH_MODE_FLY_4X:
+ l_is_val_found = find_value_from_key(TRFC_DLR4, i_density, o_trfc_in_ps);
+ break;
+
+ default:
+ // Fine Refresh Mode will be a platform attribute set by the MRW,
+ // which they "shouldn't" mess up as long as use "attribute" enums.
+ // if openpower messes this up we can at least catch it
+ FAPI_ASSERT( false,
+ fapi2::MSS_INVALID_FINE_REFRESH()
+ .set_REFRESH_MODE(i_refresh_mode),
+ "Incorrect Fine Refresh Mode received: %d ",
+ i_refresh_mode);
+ break;
+ }// switch
+
+ FAPI_ASSERT( l_is_val_found,
+ fapi2::MSS_FAILED_TO_FIND_TRFC()
+ .set_SDRAM_DENSITY(i_density)
+ .set_REFRESH_MODE(i_refresh_mode)
+ .set_DIMM_TARGET(i_target),
+ "%s: Unable to find tRFC (ps) from map with SDRAM density key %d with %d refresh mode",
+ mss::c_str(i_target),
+ i_density,
+ i_refresh_mode);
+
+ // Again, FAPI_ASSERT doesn't set current_err to good, only to bad
+ return fapi2::FAPI2_RC_SUCCESS;
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief tRTP *in ps*
+/// @return constexpr value of RTP = 7500 ps
+///
+constexpr uint64_t trtp()
+{
+ // Per JEDEC spec, defaults to 7500 ps for all frequencies.
+ // (technically max of 7.5 ns or 4 nclk, which is always 7.5ns for DDR4)
+ return 7500;
+}
+
+///
+/// @brief Return the minimum allowable tRAS in picoseconds
+/// @param[in] i_target the fapi2 target
+/// @param[in] i_freq freq for the DIMM
+/// @return value in picoseconds
+///
+inline uint64_t tras(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
+ const uint64_t i_freq)
+{
+ uint64_t l_tras = 0;
+
+ switch(i_freq)
+ {
+ case 1866:
+ l_tras = 34000;
+ break;
+
+ case 2133:
+ l_tras = 33000;
+ break;
+
+ case 2400:
+ case 2666:
+ case 2933:
+ case 3200:
+ l_tras = 32000;
+ break;
+
+ default:
+ FAPI_ASSERT( false,
+ fapi2::MSS_INVALID_FREQ_PASSED_IN()
+ .set_FREQ(i_freq)
+ .set_FUNCTION(TRAS)
+ .set_DIMM_TARGET(i_target),
+ "%s Invalid frequency %lu",
+ mss::c_str(i_target),
+ i_freq);
+ }
+
+ return l_tras;
+
+fapi_try_exit:
+
+ // We simply can't work if we can't get the frequency or
+ // if we get an unsupported value that can't be converted to a valid tCK (clock period)
+ // ...so this should be ok
+ FAPI_ERR("Obtained an invalid MSS_FREQ (%d) - stopping", i_freq);
+ fapi2::Assert(false);
+
+ // Keeps compiler happy
+ return 0;
+}
+
+///
+/// @brief Helper function to find tFAW based speed (MT/s) for 1/2 KB page
+/// @tparam T the fapi2::TargetType of a type from which we can get MT/s
+/// @param[in] i_target the fapi2 target
+/// @param[in] i_freq the DRAM frequency
+/// @param[out] o_output timing in clocks (nck)
+/// @return FAPI2_RC_SUCCESS iff okay
+/// @note this is only for non-3DS DIMM
+///
+template< fapi2::TargetType T >
+static fapi2::ReturnCode tfaw_half_kb_page_helper(const fapi2::Target<T>& i_target,
+ const uint64_t i_freq,
+ uint64_t& o_output)
+{
+ // Values derived from DDR4 Spec (79-4A)
+ // 13.3 Timing Parameters by Speed Grade
+ // Table 132. Pg 240
+ fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;
+
+ // It could have been more "efficient" to hand-calculate the answer and
+ // use compile time constants to return the answer. To avoid magic
+ // numbers and to align (more closely) with the DDR4 JEDEC spec,
+ // we let the std library do the work for us for maintainability.
+ // Could have used compile-time constants to denote the numbers below
+ // but they are "random" and vary.
+ switch(i_freq)
+ {
+ // static_cast is needed for template deduction of std::max API
+ case 1866:
+ o_output = std::max( 16, spd::ps_to_nck(i_freq, 17000) );
+ break;
+
+ case 2133:
+ o_output = std::max( 16, spd::ps_to_nck(i_freq, 15000) );
+ break;
+
+ case 2400:
+ o_output = std::max( 16, spd::ps_to_nck(i_freq, 13000) );
+ break;
+
+ case 2666:
+ o_output = std::max( 16, spd::ps_to_nck(i_freq, 12000) );
+ break;
+
+ case 2933:
+ o_output = std::max( 16, spd::ps_to_nck(i_freq, 10875) );
+ break;
+
+ case 3200:
+ o_output = std::max( 16, spd::ps_to_nck(i_freq, 10000) );
+ break;
+
+ default:
+ FAPI_ASSERT( false,
+ fapi2::MSS_INVALID_FREQ_PASSED_IN()
+ .set_FREQ(i_freq)
+ .set_FUNCTION(TFAW_HALF_KB_PAGE_HELPER)
+ .set_DIMM_TARGET(i_target),
+ "%s Invalid frequency %lu",
+ mss::c_str(i_target),
+ i_freq);
+ }
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Helper function to find tFAW based speed (MT/s) for 1KB page
+/// @tparam T the fapi2::TargetType of a type from which we can get MT/s
+/// @param[in] i_target the fapi2 target
+/// @param[in] i_freq the DRAM frequency
+/// @param[out] o_output timing in clocks (nck)
+/// @return FAPI2_RC_SUCCESS iff okay
+/// @note this is only for non-3DS DIMM
+///
+template< fapi2::TargetType T >
+static fapi2::ReturnCode tfaw_1kb_page_helper(const fapi2::Target<T>& i_target,
+ const uint64_t i_freq,
+ uint64_t& o_output)
+{
+ // Values derived from DDR4 Spec (79-4A)
+ // 13.3 Timing Parameters by Speed Grade
+ // Table 132. Pg 240
+ fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;
+
+ // It could have been more "efficient" to hand-calculate the answer and
+ // use compile time constants to return the answer. To avoid magic
+ // numbers and to align (more closely) with the DDR4 JEDEC spec,
+ // we let the std library do the work for us for maintainability (and ease of debug?).
+ // Could have used compile-time constants to denote the numbers below
+ // but they are "random" and vary.
+ switch(i_freq)
+ {
+ case 1866:
+ o_output = std::max( 20, spd::ns_to_nck(i_freq, 23) );
+ break;
+
+ case 2133:
+ case 2400:
+ case 2666:
+ case 2933:
+ case 3200:
+ o_output = std::max( 20, spd::ns_to_nck(i_freq, 21) );
+ break;
+
+ default:
+ FAPI_ASSERT( false,
+ fapi2::MSS_INVALID_FREQ_PASSED_IN()
+ .set_FREQ(i_freq)
+ .set_FUNCTION(TFAW_ONE_KB_PAGE_HELPER)
+ .set_DIMM_TARGET(i_target),
+ "%s Invalid frequency %lu",
+ mss::c_str(i_target),
+ i_freq);
+ break;
+ }
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Helper function to find tFAW based speed (MT/s) for 2KB page
+/// @tparam T the fapi2::TargetType of a type from which we can get MT/s
+/// @param[in] i_target the fapi2 target
+/// @param[in] i_freq the DRAM frequency
+/// @param[out] o_output timing in clocks (nck)
+/// @return FAPI2_RC_SUCCESS iff okay
+/// @note this is only for non-3DS DIMM
+///
+template< fapi2::TargetType T >
+static fapi2::ReturnCode tfaw_2kb_page_helper(const fapi2::Target<T>& i_target,
+ const uint64_t i_freq,
+ uint64_t& o_output)
+{
+
+ // Values derived from DDR4 Spec (79-4A)
+ // 13.3 Timing Parameters by Speed Grade
+ // Table 132. Pg 240
+ fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;
+
+ // It could have been more "efficient" to hand-calculate the answer and
+ // use compile time constants to return the answer. To avoid magic
+ // numbers and to align (more closely) with the DDR4 JEDEC spec,
+ // we let the std library do the work for us for maintainability.
+ // Could have used compile-time constants to denote the numbers below
+ // but they are "random" and vary.
+ switch(i_freq)
+ {
+ case 1866:
+ case 2133:
+ case 2400:
+ case 2666:
+ case 2933:
+ case 3200:
+ o_output = std::max( 28, spd::ns_to_nck(i_freq, 30) );
+ break;
+
+ default:
+ FAPI_ASSERT( false,
+ fapi2::MSS_INVALID_FREQ_PASSED_IN()
+ .set_FREQ(i_freq)
+ .set_FUNCTION(TFAW_TW_KB_PAGE_HELPER)
+ .set_DIMM_TARGET(i_target),
+ "%s Invalid frequency %lu",
+ mss::c_str(i_target),
+ i_freq);
+ break;
+ }
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Return the minimum allowable tFAW in nck
+/// @tparam T the fapi2::TargetType of a type from which we can get MT/s
+/// @param[in] i_target the fapi2 target
+/// @param[in] i_dram_width the page size
+/// @param[in] i_freq the DRAM frequency
+/// @param[out] o_tFAW timing in clocks (nck)
+/// @return FAPI2_RC_SUCCESS iff okay
+//
+template< fapi2::TargetType T >
+fapi2::ReturnCode tfaw( const fapi2::Target<T>& i_target,
+ const uint8_t i_dram_width,
+ const uint64_t i_freq,
+ uint64_t& o_tFAW )
+{
+ switch(i_dram_width)
+ {
+ case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X4:
+ FAPI_TRY( tfaw_half_kb_page_helper(i_target, i_freq, o_tFAW) );
+ break;
+
+ case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8:
+ FAPI_TRY( tfaw_1kb_page_helper(i_target, i_freq, o_tFAW) );
+ break;
+
+ case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X16:
+ FAPI_TRY( tfaw_2kb_page_helper(i_target, i_freq, o_tFAW) );
+ break;
+
+ default:
+ FAPI_ASSERT( false,
+ fapi2::MSS_INVALID_DRAM_WIDTH()
+ .set_DRAM_WIDTH(i_dram_width)
+ .set_DIMM_TARGET(i_target),
+ "Invalid DRAM width with %d for target %s",
+ i_dram_width,
+ mss::c_str(i_target));
+ break;
+ }
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief tFAW_dlr *in nck*
+/// @return 16nck
+/// @note From DDR4 3DS Spec
+/// 12.2 Timing Parameters by Speed Grade
+///
+constexpr uint64_t tfaw_dlr()
+{
+ return 16;
+}
+
+///
+/// @brief tRRD_dlr *in nck*
+/// @return 4nck
+/// @note From DDR4 3DS Spec
+/// 12.2 Timing Parameters by Speed Grade
+///
+constexpr uint64_t trrd_dlr()
+{
+ return 4;
+}
+
+///
+/// @brief Helper function to find tRRD_L based speed (MT/s) for 1KB page
+/// @tparam T the fapi2::TargetType of a type from which we can get MT/s
+/// @param[in] i_target the fapi2 target
+/// @param[in] i_freq the DRAM frequency
+/// @param[out] o_output timing in clocks (nck)
+/// @return FAPI2_RC_SUCCESS iff okay
+/// @note this is only for non-3DS DIMM
+///
+template< fapi2::TargetType T >
+static fapi2::ReturnCode trrd_l_half_and_1kb_page_helper(const fapi2::Target<T>& i_target,
+ const uint64_t i_freq,
+ uint64_t& o_output)
+{
+ // Values derived from DDR4 Spec (79-4A)
+ // 13.3 Timing Parameters by Speed Grade
+ // Table 132. Pg 240
+ fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;
+
+ // It could have been more "efficient" to hand-calculate the answer and
+ // use compile time constants to return the answer. To avoid magic
+ // numbers and to align (more closely) with the DDR4 JEDEC spec,
+ // we let the std library do the work for us for maintainability (and ease of debug?).
+ // Could have used compile-time constants to denote the numbers below
+ // but they are "random" and vary.
+ switch(i_freq)
+ {
+ case 1866:
+ case 2133:
+ // From the spec: Max(4nCK,5.3ns)
+ o_output = std::max( 4, spd::ps_to_nck(i_freq, 5300) );
+ break;
+
+ case 2400:
+ case 2666:
+ case 2933:
+ case 3200:
+ // Max(4nCK,4.9ns)
+ o_output = std::max( 4, spd::ps_to_nck(i_freq, 4900) );
+ break;
+
+ default:
+ FAPI_ASSERT( false,
+ fapi2::MSS_INVALID_FREQ_PASSED_IN()
+ .set_FREQ(i_freq)
+ .set_FUNCTION(TRRD_L_HALF_AND_1KB_PAGE_HELPER)
+ .set_DIMM_TARGET(i_target),
+ "%s Invalid frequency %lu",
+ mss::c_str(i_target),
+ i_freq);
+ }
+
+ return fapi2::FAPI2_RC_SUCCESS;
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Helper function to find tRRD_L based speed (MT/s) for 2KB page
+/// @tparam T the fapi2::TargetType of a type from which we can get MT/s
+/// @param[in] i_target the fapi2 target
+/// @param[in] i_freq the DRAM frequency
+/// @param[out] o_output timing in clocks (nck)
+/// @return FAPI2_RC_SUCCESS iff okay
+/// @note this is only for non-3DS DIMM
+///
+template< fapi2::TargetType T >
+static fapi2::ReturnCode trrd_l_2kb_page_helper(const fapi2::Target<T>& i_target,
+ const uint64_t i_freq,
+ uint64_t& o_output)
+{
+
+ // Values derived from DDR4 Spec (79-4A)
+ // 13.3 Timing Parameters by Speed Grade
+ // Table 132. Pg 240
+ fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;
+
+ // It could have been more "efficient" to hand-calculate the answer and
+ // use compile time constants to return the answer. To avoid magic
+ // numbers and to align (more closely) with the DDR4 JEDEC spec,
+ // we let the std library do the work for us for maintainability (and ease of debug?).
+ // Could have used compile-time constants to denote the numbers below
+ // but they are "random" and vary.
+ switch(i_freq)
+ {
+ case 1866:
+ case 2133:
+ case 2400:
+ case 2666:
+ case 2933:
+ case 3200:
+ o_output = std::max( 4, spd::ps_to_nck(i_freq, 6400) );
+ break;
+
+ default:
+ FAPI_TRY(fapi2::FAPI2_RC_INVALID_PARAMETER, "%s Invalid frequency %lu", mss::c_str(i_target), i_freq);
+ break;
+ }
+
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Return the minimum allowable tRRD_L in nck
+/// @tparam T the fapi2::TargetType of a type from which we can get MT/s
+/// @param[in] i_target the fapi2 target
+/// @param[in] i_dram_width the page size
+/// @param[in] i_freq the DRAM frequency
+/// @param[out] o_output timing in clocks (nck)
+/// @return FAPI2_RC_SUCCESS iff okay
+/// @note this is only for non-3DS DIMM
+///
+template< fapi2::TargetType T >
+fapi2::ReturnCode trrd_l( const fapi2::Target<T>& i_target,
+ const uint8_t i_dram_width,
+ const uint64_t i_freq,
+ uint64_t& o_tRRD_L )
+{
+ switch(i_dram_width)
+ {
+ case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X4:
+ case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8:
+ FAPI_TRY( trrd_l_half_and_1kb_page_helper(i_target, i_freq, o_tRRD_L),
+ "Error calculating trrd l for half and 1kb page" );
+ break;
+
+ case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X16:
+ FAPI_TRY( trrd_l_2kb_page_helper(i_target, i_freq, o_tRRD_L) );
+ break;
+
+ default:
+ FAPI_ASSERT( false,
+ fapi2::MSS_INVALID_PAGE_SIZE()
+ .set_DRAM_WIDTH(i_dram_width)
+ .set_DIMM_TARGET(i_target),
+ "%s Recieved an invalid page size: %lu",
+ mss::c_str(i_target),
+ i_dram_width);
+ break;
+ }
+
+ return fapi2::FAPI2_RC_SUCCESS;
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Helper function to find tRRD_S based speed (MT/s) for 1KB page
+/// @tparam T the fapi2::TargetType of a type from which we can get MT/s
+/// @param[in] i_target the fapi2 target
+/// @param[in] i_freq the DRAM frequency
+/// @param[out] o_output timing in clocks (nck)
+/// @return FAPI2_RC_SUCCESS iff okay
+/// @note this is only for non-3DS DIMM
+///
+template< fapi2::TargetType T >
+static fapi2::ReturnCode trrd_s_half_and_1kb_page_helper(const fapi2::Target<T>& i_target,
+ const uint64_t i_freq,
+ uint64_t& o_output)
+{
+ // Values derived from DDR4 Spec (79-4A)
+ // 13.3 Timing Parameters by Speed Grade
+ // Table 132. Pg 240
+ fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;
+
+ // It could have been more "efficient" to hand-calculate the answer and
+ // use compile time constants to return the answer. To avoid magic
+ // numbers and to align (more closely) with the DDR4 JEDEC spec,
+ // we let the std library do the work for us for maintainability (and ease of debug?).
+ // Could have used compile-time constants to denote the numbers below
+ // but they are "random" and vary.
+ switch(i_freq)
+ {
+ case 1866:
+ o_output = std::max( 4, spd::ps_to_nck(i_freq, 4200) );
+ break;
+
+ case 2133:
+ o_output = std::max( 4, spd::ps_to_nck(i_freq, 3700) );
+ break;
+
+ case 2400:
+ o_output = std::max( 4, spd::ps_to_nck(i_freq, 3300) );
+ break;
+
+ case 2666:
+ o_output = std::max( 4, spd::ps_to_nck(i_freq, 3000) );
+ break;
+
+ case 2933:
+ o_output = std::max( 4, spd::ps_to_nck(i_freq, 2700) );
+ break;
+
+ case 3200:
+ o_output = std::max( 4, spd::ps_to_nck(i_freq, 2500) );
+ break;
+
+ default:
+ FAPI_ASSERT( false,
+ fapi2::MSS_INVALID_FREQ_PASSED_IN()
+ .set_FREQ(i_freq)
+ .set_FUNCTION(TRRD_S_HALF_AND_1KB_PAGE_HELPER)
+ .set_DIMM_TARGET(i_target),
+ "%s Invalid frequency %lu",
+ mss::c_str(i_target),
+ i_freq);
+ }
+
+ return fapi2::FAPI2_RC_SUCCESS;
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Helper function to find tRRD_S based speed (MT/s) for 2KB page
+/// @tparam T the fapi2::TargetType of a type from which we can get MT/s
+/// @param[in] i_target the fapi2 target
+/// @param[in] i_freq the DRAM frequency
+/// @param[out] o_output timing in clocks (nck)
+/// @return FAPI2_RC_SUCCESS iff okay
+/// @note this is only for non-3DS DIMM
+///
+template< fapi2::TargetType T >
+static fapi2::ReturnCode trrd_s_2kb_page_helper(const fapi2::Target<T>& i_target,
+ const uint64_t i_freq,
+ uint64_t& o_output)
+{
+ // Values derived from DDR4 Spec (79-4A)
+ // 13.3 Timing Parameters by Speed Grade
+ // Table 132. Pg 240
+ fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;
+
+ // It could have been more "efficient" to hand-calculate the answer and
+ // use compile time constants to return the answer. To avoid magic
+ // numbers and to align (more closely) with the DDR4 JEDEC spec,
+ // we let the std library do the work for us for maintainability (and ease of debug?).
+ // Could have used compile-time constants to denote the numbers below
+ // but they are "random" and vary.
+ switch(i_freq)
+ {
+ case 1866:
+ case 2133:
+ case 2400:
+ case 2666:
+ case 2933:
+ case 3200:
+ o_output = std::max( 4, spd::ps_to_nck(i_freq, 5300) );
+ break;
+
+ default:
+ FAPI_ASSERT( false,
+ fapi2::MSS_INVALID_FREQ_PASSED_IN()
+ .set_FREQ(i_freq)
+ .set_FUNCTION(TRRD_S_2KB_PAGE_HELPER)
+ .set_DIMM_TARGET(i_target),
+ "%s Invalid frequency %lu",
+ mss::c_str(i_target),
+ i_freq);
+ break;
+ }
+
+ return fapi2::FAPI2_RC_SUCCESS;
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+///
+/// @brief Return the minimum allowable tRRD_S in nck
+/// @tparam T the fapi2::TargetType of a type from which we can get MT/s
+/// @param[in] i_target the fapi2 target
+/// @param[in] i_dram_width the page size
+/// @param[in] i_freq the DRAM frequency
+/// @param[out] o_tRRD_S timing in clocks (nck)
+/// @return FAPI2_RC_SUCCESS iff okay
+/// @note this is only for non-3DS DIMM
+///
+template< fapi2::TargetType T >
+fapi2::ReturnCode trrd_s( const fapi2::Target<T>& i_target,
+ const uint8_t i_dram_width,
+ const uint64_t i_freq,
+ uint64_t& o_tRRD_S )
+{
+ switch(i_dram_width)
+ {
+ case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X4:
+ case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8:
+ FAPI_TRY( trrd_s_half_and_1kb_page_helper(i_target, i_freq, o_tRRD_S),
+ "Error calculating trrd_s for half and 1kb page" );
+ break;
+
+ case fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X16:
+ FAPI_TRY( trrd_s_2kb_page_helper(i_target, i_freq, o_tRRD_S) );
+ break;
+
+ default:
+ FAPI_ASSERT( false,
+ fapi2::MSS_INVALID_PAGE_SIZE()
+ .set_DRAM_WIDTH(i_dram_width)
+ .set_DIMM_TARGET(i_target),
+ "%s Recieved an invalid page size: %lu",
+ mss::c_str(i_target),
+ i_dram_width);
+ break;
+ }
+
+ return fapi2::FAPI2_RC_SUCCESS;
+fapi_try_exit:
+ return fapi2::current_err;
+}
+
+} // mss
+#endif
OpenPOWER on IntegriCloud