summaryrefslogtreecommitdiffstats
path: root/src/import/chips/centaur/procedures/hwp/memory/p9c_mss_bulk_pwr_throttles.C
blob: 02f8a2a1d91a2d97c42857cdbc684cb41b88dc61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/centaur/procedures/hwp/memory/p9c_mss_bulk_pwr_throttles.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */


/// @file p9c_mss_bulk_pwr_throttles.C
/// @brief  Sets the throttle attributes based on a power limit for the dimms on the channel pair
///
/// *HWP HWP Owner: Andre Marin <aamaring@us.ibm.com>
/// *HWP HWP Backup: Mike Pardeik <pardeik@us.ibm.com>
/// *HWP Team: Memory
/// *HWP Level: 2
/// *HWP Consumed by: HB
///

// DESCRIPTION:
// The purpose of this procedure is to set the throttle attributes based on a
// power limit for the dimms on the channel pair
// At the end, output attributes will be updated with throttle values that will
// have dimms at or below the limit
// NOTE:  ISDIMMs and CDIMMs are handled differently
//   ISDIMMs use a power per DIMM for the thermal power limit from the MRW
//   CDIMM will use power per CDIMM (power for all virtual dimms) for the
//    thermal power limit from the MRW
// Plan is to have ISDIMM use the per-slot throttles (thermal throttles) or
//  per-mba throttles (power throttles), and CDIMM to use the per-chip throttles
// Note that throttle_n_per_mba takes on different meanings depending on how
// cfg_nm_per_slot_enabled is set
//   Can be slot0/slot1 OR slot0/MBA throttling
// Note that throttle_n_per_chip takes on different meaning depending on how
// cfg_count_other_mba_dis is set
//  Can be per-chip OR per-mba throttling
// These inits here are done in mss_scominit
// ISDIMM:  These registers need to be setup to these values, will be able to
//  do per slot or per MBA throttling
//   cfg_nm_per_slot_enabled = 1
//   cfg_count_other_mba_dis = 1
// CDIMM:  These registers need to be setup to these values, will be able to
//  do per mba or per chip throttling
//   cfg_nm_per_slot_enabled = 0
//   cfg_count_other_mba_dis = 0

//------------------------------------------------------------------------------
//  My Includes
//------------------------------------------------------------------------------
#include <p9c_mss_bulk_pwr_throttles.H>
#include <generic/memory/lib/utils/c_str.H>
#include <dimmConsts.H>
#include <lib/utils/cumulus_find.H>
#include <generic/memory/lib/utils/count_dimm.H>
//------------------------------------------------------------------------------
//  Includes
//------------------------------------------------------------------------------
#include <fapi2.H>

using fapi2::TARGET_TYPE_MEMBUF_CHIP;
using fapi2::TARGET_TYPE_MBA;
using fapi2::FAPI2_RC_SUCCESS;

constexpr uint32_t DRAM_UTIL_LIMIT_BOTH_MBA_WITH_DIMMS = 5625; // units c%
constexpr uint8_t IDLE_UTIL = 0; // in percent
constexpr double UTIL_FLOOR = 1; // in percent, utilization floor

extern "C" {
    /// @brief This function determines the throttle values power limit for the dimms on the channel pair
    /// @param[in] i_target_mba:  MBA Target
    /// @return fapi2::ReturnCode
    fapi2::ReturnCode p9c_mss_bulk_pwr_throttles(const fapi2::Target<fapi2::TARGET_TYPE_MBA>& i_target_mba)
    {
        FAPI_INF("*** Running mss_bulk_pwr_throttles on %s ***", mss::c_str(i_target_mba));
        uint32_t l_channel_pair_watt_target = 0;
        double l_utilization = 0;
        double l_channel_power_slope = 0;
        double l_channel_power_intercept = 0;
        uint32_t l_channel_pair_power = 0;
        constexpr bool l_utilization_is_a_min_value = false; //.set to false

        // If MBA has no DIMMs, return as there is nothing to do
        if (mss::count_dimm(i_target_mba) == 0)
        {
            FAPI_INF("++++ NO DIMM on %s ++++", mss::c_str(i_target_mba));
            return fapi2::FAPI2_RC_SUCCESS;
        }

        // get input attributes
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_MSS_MEM_WATT_TARGET,
                               i_target_mba, l_channel_pair_watt_target));

        FAPI_INF("[%s Available Channel Pair Power (Watt Target) %d cW]", mss::c_str(i_target_mba), l_channel_pair_watt_target);

        // call p9c_mss_bulk_pwr_channel_power to get the channel pair power slope and intercept values to use
        FAPI_TRY(p9c_mss_bulk_pwr_channel_pair_power_curve(i_target_mba, l_channel_power_slope, l_channel_power_intercept));

        // calculate the utilization needed to be under power limit
        if ((l_channel_pair_watt_target > l_channel_power_intercept) && (l_channel_power_slope > 0))
        {
            l_utilization = (l_channel_pair_watt_target - l_channel_power_intercept) / l_channel_power_slope * PERCENT_CONVERSION;
        }

        // call p9c_mss_bulk_pwr_util_to_throttle_power to get the memory throttle and channel pair power attributes defined
        FAPI_TRY(p9c_mss_bulk_pwr_util_to_throttle_power(i_target_mba, l_utilization, l_channel_power_slope,
                 l_channel_power_intercept, l_utilization_is_a_min_value));

        // Get channel pair power attribute value just defined from above call to p9c_mss_bulk_pwr_util_to_throttle_power
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_MSS_CHANNEL_PAIR_MAXPOWER,
                               i_target_mba, l_channel_pair_power));

        // Check to see if there is not enough available power (units are in cW)
        // Return a bad RC if power is over the limit
        FAPI_ASSERT((l_channel_pair_power <= l_channel_pair_watt_target),
                    fapi2::CEN_MSS_NOT_ENOUGH_AVAILABLE_DIMM_POWER().
                    set_PAIR_POWER(l_channel_pair_power).
                    set_PAIR_WATT_TARGET(l_channel_pair_watt_target).
                    set_MEM_MBA(i_target_mba),
                    "%s Not enough available memory power [Channel Pair Power %d/%d cW]", mss::c_str(i_target_mba), l_channel_pair_power,
                    l_channel_pair_watt_target);

        FAPI_INF("*** mss_bulk_pwr_throttles COMPLETE on %s ***", mss::c_str(i_target_mba));

    fapi_try_exit:
        return fapi2::current_err;
    }

    ///
    /// @brief This function determines the channel pair power slope and intercept values to use
    ///   to calculate the channel pair power (ie.  power of dimms attached to channel pair)
    /// @param[in] i_target_mba:  MBA Target
    /// @param[out]  o_channel_power_slope channel pair power slope
    /// @param[out]  o_channel_power_intercept channel pair power intercept
    /// @return fapi2::ReturnCode
    ///
    fapi2::ReturnCode p9c_mss_bulk_pwr_channel_pair_power_curve(const fapi2::Target<fapi2::TARGET_TYPE_MBA>& i_target_mba,
            double& o_channel_power_slope,
            double& o_channel_power_intercept
                                                               )
    {
        FAPI_INF("*** Running p9c_mss_bulk_pwr_channel_pair_power_curve on %s ***", mss::c_str(i_target_mba));

        // add up the power from all dimms for this MBA (across both channels)
        // for IDLE (utilization=0) and maximum utilization
        double l_max_util = 0;
        double l_channel_pair_power_idle = 0;
        double l_channel_pair_power_max = 0;
        uint8_t l_port = 0;
        uint8_t l_dimm = 0;
        double l_channel_power_array_idle[MAX_PORTS_PER_MBA] = {0};
        double l_channel_power_array_max[MAX_PORTS_PER_MBA] = {0};
        double l_dimm_power_array_idle[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT] = {0};
        double l_dimm_power_array_max[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT] = {0};
        uint8_t l_dimm_ranks_array[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT] = {0};
        uint32_t l_total_power_slope_array[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT] = {0};
        uint32_t l_total_power_int_array[MAX_PORTS_PER_MBA][MAX_DIMM_PER_PORT] = {0};
        uint8_t l_power_curve_percent_uplift = 0;
        uint8_t l_power_curve_percent_uplift_idle = 0;
        uint32_t l_max_dram_databus_util = 0;
        uint32_t l_throttle_d = 0;

        // If MBA has no DIMMs, return as there is nothing to do
        if (mss::count_dimm(i_target_mba) == 0)
        {
            FAPI_INF("++++ NO DIMM on %s ++++", mss::c_str(i_target_mba));
            return fapi2::FAPI2_RC_SUCCESS;
        }

        // get input attributes
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_MSS_MRW_MAX_DRAM_DATABUS_UTIL,
                               fapi2::Target<fapi2::TARGET_TYPE_SYSTEM>(), l_max_dram_databus_util));
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_NUM_RANKS_PER_DIMM,
                               i_target_mba, l_dimm_ranks_array));
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_MSS_TOTAL_POWER_SLOPE,
                               i_target_mba, l_total_power_slope_array));
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_MSS_TOTAL_POWER_INT,
                               i_target_mba, l_total_power_int_array));
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_MSS_MRW_DIMM_POWER_CURVE_PERCENT_UPLIFT,
                               fapi2::Target<fapi2::TARGET_TYPE_SYSTEM>(), l_power_curve_percent_uplift));
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_MSS_MRW_DIMM_POWER_CURVE_PERCENT_UPLIFT_IDLE,
                               fapi2::Target<fapi2::TARGET_TYPE_SYSTEM>(), l_power_curve_percent_uplift_idle));
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_MSS_MRW_MEM_M_DRAM_CLOCKS, fapi2::Target<fapi2::TARGET_TYPE_SYSTEM>(),
                               l_throttle_d));

        // If throttling is disabled, set max util to MAX_UTIL
        if (l_throttle_d == 0)
        {
            FAPI_INF("%s Memory Throttling is Disabled with M=0", mss::c_str(i_target_mba));
            l_max_dram_databus_util = MAX_UTIL;
        }

        // Maximum theoretical data bus utilization (percent of max) (for ceiling)
        // Comes from MRW value in c% - convert to %
        // We don't need to limit this because this function is only determining the channel pair power slope and interecpt
        l_max_util = convert_to_percent(static_cast<double>(l_max_dram_databus_util));

        for (l_port = 0; l_port < MAX_PORTS_PER_MBA; l_port++)
        {
            l_channel_power_array_idle[l_port] = 0;
            l_channel_power_array_max[l_port] = 0;

            for (l_dimm = 0; l_dimm < MAX_DIMM_PER_PORT; l_dimm++)
            {
                // default dimm power is zero (used for dimms that are not physically present)
                l_dimm_power_array_idle[l_port][l_dimm] = 0;
                l_dimm_power_array_max[l_port][l_dimm] = 0;

                // See if there are any ranks present on the dimm (configured or deconfigured)
                if ((l_dimm_ranks_array[l_port][l_dimm] > 0) && (l_total_power_slope_array[l_port][l_dimm] > 0)
                    && (l_total_power_int_array[l_port][l_dimm] > 0))
                {
                    l_dimm_power_array_idle[l_port][l_dimm] = l_dimm_power_array_idle[l_port][l_dimm] +
                            (convert_to_percent(IDLE_UTIL) * l_total_power_slope_array[l_port][l_dimm]) + l_total_power_int_array[l_port][l_dimm];
                    l_dimm_power_array_max[l_port][l_dimm] = l_dimm_power_array_max[l_port][l_dimm] + (convert_to_percent(l_max_util) *
                            l_total_power_slope_array[l_port][l_dimm]) + l_total_power_int_array[l_port][l_dimm];
                    // Include any system uplift here too
                    l_dimm_power_array_idle[l_port][l_dimm] =
                        l_dimm_power_array_idle[l_port][l_dimm] *
                        (1 + (static_cast<double>(l_power_curve_percent_uplift_idle)) / 100);
                    l_dimm_power_array_max[l_port][l_dimm] =
                        l_dimm_power_array_max[l_port][l_dimm] *
                        (1 + (static_cast<double>(l_power_curve_percent_uplift)) / 100);
                    FAPI_DBG("%s [P%d:D%d][IDLE/MAX CH Util %d/%4.2lf][Slope:Int %d:%d][UpliftPercent IDLE/MAX %d/%d)][IDLE/MAX Power %4.2lf/%4.2lf cW]",
                             mss::c_str(i_target_mba), l_port, l_dimm, IDLE_UTIL, l_max_util, l_total_power_slope_array[l_port][l_dimm],
                             l_total_power_int_array[l_port][l_dimm],
                             l_power_curve_percent_uplift_idle, l_power_curve_percent_uplift, l_dimm_power_array_idle[l_port][l_dimm],
                             l_dimm_power_array_max[l_port][l_dimm]);
                }

                // calculate channel power by adding up the power of each dimm
                l_channel_power_array_idle[l_port] += l_dimm_power_array_idle[l_port][l_dimm];
                l_channel_power_array_max[l_port] += l_dimm_power_array_max[l_port][l_dimm];
            }

            FAPI_DBG("%s [P%d][IDLE/MAX CH Util %d/%4.2lf][IDLE/MAX Power %4.2lf/%4.2lf cW]",
                     mss::c_str(i_target_mba), l_port, IDLE_UTIL, l_max_util,
                     l_channel_power_array_idle[l_port], l_channel_power_array_max[l_port]);
            l_channel_pair_power_idle += l_channel_power_array_idle[l_port];
            l_channel_pair_power_max += l_channel_power_array_max[l_port];
        }

        // calculate the slope/intercept values from power values just calculated above
        o_channel_power_slope = (l_channel_pair_power_max - l_channel_pair_power_idle) / ( convert_to_percent(l_max_util) -
                                convert_to_percent(IDLE_UTIL) );
        o_channel_power_intercept = l_channel_pair_power_idle;

        FAPI_INF("%s [Channel Pair Power IDLE/MAX %4.2lf/%4.2lf cW][Slope/Intercept %4.2lf/%4.2lf cW]",
                 mss::c_str(i_target_mba), l_channel_pair_power_idle, l_channel_pair_power_max, o_channel_power_slope,
                 o_channel_power_intercept);


        FAPI_INF("*** p9c_mss_bulk_pwr_channel_pair_power_curve COMPLETE on %s ***", mss::c_str(i_target_mba));

    fapi_try_exit:
        return fapi2::current_err;
    }


    ///
    /// @brief This function determines the memory throttle and channel pair power
    ///   attribute values for a given dram data bus utilization value
    /// @param[in] i_target_mba:  MBA Target
    /// @param[in]  i_utilization Dram data bus utilization value (units %)
    /// @param[in]  i_channel_power_slope channel pair power slope
    /// @param[in]  i_channel_power_intercept channel pair power
    /// @param[in]  i_utilization_is_a_min_value tells us if i_utilization is a min or max target to meet (0=max, 1=min)
    /// @return fapi2::ReturnCode
    ///
    fapi2::ReturnCode p9c_mss_bulk_pwr_util_to_throttle_power(const fapi2::Target<fapi2::TARGET_TYPE_MBA>& i_target_mba,
            double i_utilization,
            const double i_channel_power_slope,
            const double i_channel_power_intercept,
            const bool i_utilization_is_a_min_value
                                                             )
    {
        FAPI_INF("*** Running p9c_mss_bulk_pwr_util_to_throttle_power on %s ***", mss::c_str(i_target_mba));
        FAPI_INF("%s [Input Util %4.2lf percent]", mss::c_str(i_target_mba), i_utilization);

        // Limit max utilization to 100% DMI utilization so we don't end up with memory powers that can never be achieved
        // For CDIMMs, limit to 56.25% dram data bus utilization (Note that CDIMMs have both MBAs with DIMMs)
        // For ISDIMMs, limit to 56.25% dram data bus utilization if both MBAs have DIMMs
        // For ISDIMMs, no limitation if only one MBA has DIMMs (power will be higher than it should be, but this is not a full
        //     configuration so we should not be stressing any power limitations so ok to over estimate the power)
        double l_max_util = 0;
        uint8_t l_custom_dimm = 0;
        uint32_t l_throttle_d = 0;
        uint32_t l_throttle_n_per_mba = 0;
        uint32_t l_throttle_n_per_chip = 0;
        uint8_t l_num_mba_with_dimms = 0;
        uint32_t l_max_dram_databus_util = 0;
        uint32_t l_channel_pair_power = 0;
        uint8_t l_throttle_multiplier = 0;
        double l_max_util_power_calc = 0;
        double l_util_power_calc = 0;
        double l_utilization_calc_without_throttle_adder = 0;
        double l_utilization_calc = 0;
        uint8_t l_throttle_adder = 0;

        // If MBA has no DIMMs, return as there is nothing to do
        if (mss::count_dimm(i_target_mba) == 0)
        {
            FAPI_INF("++++ NO DIMM on %s ++++", mss::c_str(i_target_mba));
            return fapi2::FAPI2_RC_SUCCESS;
        }

        // get input attributes
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_MSS_MRW_MAX_DRAM_DATABUS_UTIL,
                               fapi2::Target<fapi2::TARGET_TYPE_SYSTEM>(), l_max_dram_databus_util));
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_CUSTOM_DIMM, i_target_mba,  l_custom_dimm));
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_MSS_MRW_MEM_M_DRAM_CLOCKS, fapi2::Target<fapi2::TARGET_TYPE_SYSTEM>(),
                               l_throttle_d));

        // If throttling is disabled, set max util to MAX_UTIL
        if (l_throttle_d == 0)
        {
            FAPI_INF("%s Memory Throttling is Disabled with M=0", mss::c_str(i_target_mba));
            l_max_dram_databus_util = MAX_UTIL;
        }

        // Maximum theoretical data bus utilization (percent of max) (for ceiling)
        // Comes from MRW value in c% - convert to %
        if (l_max_dram_databus_util > MAX_UTIL)
        {
            l_max_dram_databus_util = MAX_UTIL;
            FAPI_INF("%s [Max Util Limited to %4.2lf centi percent]", mss::c_str(i_target_mba), l_max_dram_databus_util);
        }

        l_max_util = convert_to_percent(static_cast<double>(l_max_dram_databus_util));

        // Limit input utilization if needed
        if (i_utilization > l_max_util)
        {
            i_utilization = l_max_util;
            FAPI_INF("%s [Input Util Limited to %4.2lf percent]", mss::c_str(i_target_mba), i_utilization);
        }

        // get number of mba's with dimms, used below to help determine utilization values for power calculations
        // Have to have this section in braces otherwise compile fails
        {
            const auto& l_target_chip = mss::find_target<TARGET_TYPE_MEMBUF_CHIP>(i_target_mba);

            for (const auto& l_mba : mss::find_targets<TARGET_TYPE_MBA>(l_target_chip))
            {
                if (mss::count_dimm(l_mba) > 0)
                {
                    l_num_mba_with_dimms++;
                }
            }
        }

        // Set the throttle multiplier based on how throttles are used
        // CDIMMs use per mba and per chip throttles (for l_throttle_n_per_mba and l_throttle_n_per_chip), set to 2
        // ISDIMMs use per slot and per mba throttles (for l_throttle_n_per_mba and l_throttle_n_per_chip), set to 1
        l_throttle_multiplier = (l_custom_dimm == fapi2::ENUM_ATTR_CEN_EFF_CUSTOM_DIMM_YES) ? 2 : 1;

        FAPI_INF("%s [Number MBAs with DIMMs %d][Throttle Multiplier %d]", mss::c_str(i_target_mba), l_num_mba_with_dimms,
                 l_throttle_multiplier);

        // Power calculation for channel pair power
        l_max_util_power_calc = l_max_util;

        if (l_num_mba_with_dimms > 1)
        {
            l_max_util_power_calc = (l_max_util < (convert_to_percent(static_cast<double>(DRAM_UTIL_LIMIT_BOTH_MBA_WITH_DIMMS)))) ?
                                    l_max_util : (convert_to_percent(static_cast<double>(DRAM_UTIL_LIMIT_BOTH_MBA_WITH_DIMMS)));
        }

        FAPI_INF("%s [Max Util for power calculations %4.2lf percent]", mss::c_str(i_target_mba), l_max_util_power_calc);

        // Limit utilization to max utilization
        l_util_power_calc = (i_utilization > l_max_util_power_calc) ? l_max_util_power_calc : i_utilization;
        FAPI_INF("%s [Util for power calculations %4.2lf percent]", mss::c_str(i_target_mba), l_util_power_calc);

        // call p9c_mss_bulk_pwr_util_to_throttle to get the memory throttle Nmba and Nchip settings for the realistic load for power calculations
        FAPI_TRY(p9c_mss_bulk_pwr_util_to_throttle(i_target_mba, l_util_power_calc, l_max_util_power_calc, l_throttle_n_per_mba,
                 l_throttle_n_per_chip, l_throttle_adder, i_utilization_is_a_min_value));

        // Calculate out the utilization at the throttle settings determined for power calculations (units %)
        // throttling disabled with M=0, use MAX_UTIL
        if (l_throttle_d == 0)
        {
            l_util_power_calc = convert_to_percent(static_cast<double>(MAX_UTIL));
        }
        // throttling enabled, use calculated throttle settings to determine utilization
        else
        {
            l_util_power_calc = (static_cast<double>(l_throttle_n_per_chip)) * ADDR_TO_DATA_UTIL_CONVERSION / l_throttle_d /
                                l_throttle_multiplier * PERCENT_CONVERSION;
        }

        // Calculate out the channel pair power at this new utilization setting
        l_channel_pair_power = static_cast<uint32_t>((convert_to_percent(l_util_power_calc)) * i_channel_power_slope +
                               i_channel_power_intercept);

        FAPI_INF("%s [Throttles for power calculations %d/%d/%d]",
                 mss::c_str(i_target_mba), l_throttle_n_per_mba, l_throttle_n_per_chip, l_throttle_d);
        FAPI_INF("%s [UTIL for power calculations %4.2lf percent][Channel Pair Power %d cW]",
                 mss::c_str(i_target_mba), l_util_power_calc, l_channel_pair_power);

        // N Throttle determination (throttles that will actually be used in hardware)
        // Here we calculate the throttle settings based on the input utilization
        // Note that the channel pair power is not based on these since those throttle values could be limited
        // call p9c_mss_bulk_pwr_util_to_throttle to get the memory throttle Nmba and Nchip settings for the input utilization
        FAPI_TRY(p9c_mss_bulk_pwr_util_to_throttle(i_target_mba, i_utilization, l_max_util, l_throttle_n_per_mba,
                 l_throttle_n_per_chip, l_throttle_adder, i_utilization_is_a_min_value));

        // Calculate out the utilization at the throttle settings determined based on input utilization
        // throttling disabled with M=0, use MAX_UTIL
        if (l_throttle_d == 0)
        {
            l_utilization_calc = convert_to_percent(static_cast<double>(MAX_UTIL));
            l_utilization_calc_without_throttle_adder = convert_to_percent(static_cast<double>(MAX_UTIL));
        }
        // throttling enabled, use calculated throttle settings to determine utilization
        else
        {
            l_utilization_calc = (static_cast<double>(l_throttle_n_per_chip)) * ADDR_TO_DATA_UTIL_CONVERSION / l_throttle_d /
                                 l_throttle_multiplier * PERCENT_CONVERSION;
            l_utilization_calc_without_throttle_adder = (static_cast<double>(l_throttle_n_per_chip - l_throttle_adder)) *
                    ADDR_TO_DATA_UTIL_CONVERSION /
                    l_throttle_d / l_throttle_multiplier * PERCENT_CONVERSION;
        }

        FAPI_INF("%s [Final Utilization %4.2lf percent][Final Throttles %d/%d/%d]",
                 mss::c_str(i_target_mba), l_utilization_calc, l_throttle_n_per_mba, l_throttle_n_per_chip, l_throttle_d);

        // Update output attributes
        FAPI_TRY(FAPI_ATTR_SET(fapi2::ATTR_CEN_MSS_MEM_THROTTLE_NUMERATOR_PER_MBA,
                               i_target_mba, l_throttle_n_per_mba));
        FAPI_TRY(FAPI_ATTR_SET(fapi2::ATTR_CEN_MSS_MEM_THROTTLE_NUMERATOR_PER_CHIP,
                               i_target_mba, l_throttle_n_per_chip));
        FAPI_TRY(FAPI_ATTR_SET(fapi2::ATTR_CEN_MSS_MEM_THROTTLE_DENOMINATOR,
                               i_target_mba, l_throttle_d));
        FAPI_TRY(FAPI_ATTR_SET(fapi2::ATTR_CEN_MSS_CHANNEL_PAIR_MAXPOWER,
                               i_target_mba, l_channel_pair_power));

        // Check for error condition if input utilization was a minimum target value to ensure we have
        //     throttle values that meet it (ie.  throttles have util equal to or greater than min util target)
        // Note:  this applies when p9c_mss_util_to_throttle calls this function
        // The calculated utilization could be much higher than the min target, so this will check for that
        // Check should make sure input utilization is equal to or less than calculated util without throttle adder
        //   and input utilization is equal to or greater than the calculated utilization without throttle adder

        FAPI_ASSERT( !((i_utilization_is_a_min_value == true) &&
                       ((l_utilization_calc < i_utilization) ||
                        (l_utilization_calc_without_throttle_adder > i_utilization))),
                     fapi2::CEN_MSS_MIN_UTILIZATION_ERROR().
                     set_UTIL_CALC(static_cast<uint32_t>(l_utilization_calc * PERCENT_CONVERSION)).
                     set_UTIL_TARGET(static_cast<uint32_t>(i_utilization * PERCENT_CONVERSION)).
                     set_MEM_MBA(i_target_mba),
                     "%s Calculated util [%d:%d centi percent] does not meet input min util target [%d centi percent]",
                     mss::c_str(i_target_mba),
                     static_cast<uint32_t>(l_utilization_calc_without_throttle_adder * PERCENT_CONVERSION),
                     static_cast<uint32_t>(l_utilization_calc * PERCENT_CONVERSION),
                     static_cast<uint32_t>(i_utilization * PERCENT_CONVERSION)
                   );

        FAPI_INF("*** p9c_mss_bulk_pwr_util_to_throttle_power COMPLETE on %s ***", mss::c_str(i_target_mba));

    fapi_try_exit:
        return fapi2::current_err;
    }


    ///
    /// @brief This function determines the memory throttle values
    ///   for a given dram data bus utilization value
    /// @param[in] i_target_mba:  MBA Target
    /// @param[in]  i_utilization Input Dram data bus utilization value (units %)
    /// @param[in]  i_max_util Max Dram data bus utilization value (units %)
    /// @param[out]  o_throttle_n_per_mba N memory throttle for per mba throttles
    /// @param[out]  o_throttle_n_per_chip N memory throttle for per mba throttles
    /// @param[out]  o_throttle_adder N throttle adder to use to meet utilization min or max target
    /// @param[in]  i_utilization_is_a_min_value tells us if i_utilization is a min or max target to meet (0=max, 1=min)
    /// @return fapi2::ReturnCode
    ///
    fapi2::ReturnCode p9c_mss_bulk_pwr_util_to_throttle(const fapi2::Target<fapi2::TARGET_TYPE_MBA>& i_target_mba,
            const double i_utilization,
            const double i_max_util,
            uint32_t& o_throttle_n_per_mba,
            uint32_t& o_throttle_n_per_chip,
            uint8_t& o_throttle_adder,
            const bool i_utilization_is_a_min_value
                                                       )
    {
        FAPI_INF("*** Running p9c_mss_bulk_pwr_util_to_throttle on %s ***", mss::c_str(i_target_mba));

        constexpr uint32_t l_min_n_throttle = 1;
        uint32_t l_runtime_throttle_n_per_mba = 0;
        uint32_t l_runtime_throttle_n_per_chip = 0;
        uint8_t l_custom_dimm = 0;
        uint32_t l_throttle_d = 0;
        uint8_t l_throttle_multiplier = 0;
        double l_utilization_calc = 0;

        // get input attributes
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_EFF_CUSTOM_DIMM, i_target_mba,  l_custom_dimm));
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_MSS_MRW_MEM_M_DRAM_CLOCKS, fapi2::Target<fapi2::TARGET_TYPE_SYSTEM>(),
                               l_throttle_d));
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_MSS_RUNTIME_MEM_THROTTLE_NUMERATOR_PER_MBA,
                               i_target_mba, l_runtime_throttle_n_per_mba));
        FAPI_TRY(FAPI_ATTR_GET(fapi2::ATTR_CEN_MSS_RUNTIME_MEM_THROTTLE_NUMERATOR_PER_CHIP,
                               i_target_mba, l_runtime_throttle_n_per_chip));

        // Set the throttle multiplier based on how throttles are used
        // CDIMMs use per mba and per chip throttles (for l_throttle_n_per_mba and l_throttle_n_per_chip), set to 2
        // ISDIMMs use per slot and per mba throttles (for l_throttle_n_per_mba and l_throttle_n_per_chip), set to 1
        l_throttle_multiplier = (l_custom_dimm == fapi2::ENUM_ATTR_CEN_EFF_CUSTOM_DIMM_YES) ? 2 : 1;

        // If input util is a min target, then we need to end up with a utilization equal to or higher than it
        // Set the throttle adder based on whether input utilization is a min or a max target
        // Account for the case when the calculated utilization will equal the input utilization
        if ( ( static_cast<uint32_t>((convert_to_percent(i_utilization)) * l_throttle_d / ADDR_TO_DATA_UTIL_CONVERSION *
                                     PERCENT_CONVERSION) !=
               ( static_cast<uint32_t>((convert_to_percent(i_utilization)) * l_throttle_d / ADDR_TO_DATA_UTIL_CONVERSION) *
                 PERCENT_CONVERSION) )
             && (i_utilization_is_a_min_value == true) )
        {
            o_throttle_adder = 1;
        }

        // Calculate the NperChip and NperMBA Throttles
        //   Uses N/M Throttling.   Equation:  (DRAM data bus utilization Percent / 100 ) = ((N * 4) / M)
        //   The 4 is a constant since dram data bus utilization is 4X the address bus utilization
        // adjust the throttles to minimum utilization if needed
        l_utilization_calc = (i_utilization < UTIL_FLOOR) ? UTIL_FLOOR : i_utilization;
        o_throttle_n_per_chip = static_cast<uint32_t>(((convert_to_percent(l_utilization_calc)) * l_throttle_d /
                                ADDR_TO_DATA_UTIL_CONVERSION) * l_throttle_multiplier +
                                o_throttle_adder);

        // For throttling efficiency, have the per mba throttle be at max value as long as it is less than or equal to the per chip throttle
        if (o_throttle_n_per_chip > ((convert_to_percent(i_max_util)) * l_throttle_d / ADDR_TO_DATA_UTIL_CONVERSION) )
        {
            o_throttle_n_per_mba = static_cast<uint32_t>(((convert_to_percent(i_max_util)) * l_throttle_d /
                                   ADDR_TO_DATA_UTIL_CONVERSION));
        }
        else
        {
            o_throttle_n_per_mba = o_throttle_n_per_chip;
        }

        // ensure that N throttle values are not zero, if so set to l_min_n_throttle to prevent hangs
        if ( (o_throttle_n_per_mba == 0) || (o_throttle_n_per_chip == 0))
        {
            o_throttle_n_per_mba = l_min_n_throttle;
            o_throttle_n_per_chip = o_throttle_n_per_mba * l_throttle_multiplier;
        }

        // for better custom dimm performance, set the per mba throttle to the per chip throttle
        o_throttle_n_per_mba = (l_custom_dimm == fapi2::ENUM_ATTR_CEN_EFF_CUSTOM_DIMM_YES) ? o_throttle_n_per_chip :
                               o_throttle_n_per_mba;

        // adjust the throttles to the runtime throttles that are based on thermal/power limits
        if ( (o_throttle_n_per_mba > l_runtime_throttle_n_per_mba) ||
             (o_throttle_n_per_chip > l_runtime_throttle_n_per_chip) )
        {
            FAPI_INF("%s Throttles for power calculations [%d/%d/%d] will be limited to runtime throttle values [%d/%d/%d].",
                     mss::c_str(i_target_mba), o_throttle_n_per_mba, o_throttle_n_per_chip, l_throttle_d,
                     l_runtime_throttle_n_per_mba, l_runtime_throttle_n_per_chip, l_throttle_d);
            o_throttle_n_per_mba = (o_throttle_n_per_mba > l_runtime_throttle_n_per_mba) ? l_runtime_throttle_n_per_mba :
                                   o_throttle_n_per_mba;
            o_throttle_n_per_chip = (o_throttle_n_per_chip > l_runtime_throttle_n_per_chip) ? l_runtime_throttle_n_per_chip :
                                    o_throttle_n_per_chip;
        }

        FAPI_INF("*** p9c_mss_bulk_pwr_util_to_throttle COMPLETE on %s ***", mss::c_str(i_target_mba));

    fapi_try_exit:
        return fapi2::current_err;
    }


} //end extern C


OpenPOWER on IntegriCloud