summaryrefslogtreecommitdiffstats
path: root/src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/pda.H
blob: fcad0bf4fb74db23c9c47727b242f73f056c1862 (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
/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/pda.H $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2017,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 pda.H
/// @brief Code to support per-DRAM addressability (PDA)
///
// *HWP HWP Owner: Stephen Glancy <sglancy@us.ibm.com>
// *HWP HWP Backup: Louis Stermole <stermole@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 3
// *HWP Consumed by: FSP:HB Memory Lab

#ifndef _MSS_PDA_H_
#define _MSS_PDA_H_

#include <fapi2.H>
#include <p9_mc_scom_addresses.H>
#include <lib/mc/port.H>
#include <lib/shared/mss_const.H>

#include <generic/memory/lib/utils/c_str.H>
#include <generic/memory/lib/utils/find.H>
#include <lib/ccs/ccs_traits_nimbus.H>
#include <generic/memory/lib/ccs/ccs.H>
#include <lib/phy/write_cntrl.H>
#include <lib/dimm/mrs_load.H>
#include <lib/dimm/ddr4/mrs_load_ddr4.H>


#include <map>

namespace mss
{

namespace ddr4
{

namespace pda
{

///
/// @brief Holds all PDA specific constants
///
enum consts : uint64_t
{
    // Note: the delay is taken from the PDA timing register's maximum value 2^6
    MAX_PDA_REG = 64,
    // 10 cycles for safety
    SAFETY = 10,
    // Total delay is your register max + safety
    SAFE_COMMAND_DELAY = MAX_PDA_REG + SAFETY,

    // PDA on/off settings
    START_DELAY_VALUE = 0,
    HOLD_DELAY_VALUE = 0xff,

    // Each enable is just 1 bit and there are 4 per reg.  Adding a length here to make the code look better
    NUM_DRAM_PER_REG = 4,

    // DRAM bit constants
    DRAM0 = MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_0_01_MRS_CMD_N0,
    DRAM1 = MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_0_01_MRS_CMD_N1,
    DRAM2 = MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_0_01_MRS_CMD_N2,
    DRAM3 = MCA_DDRPHY_DP16_DATA_BIT_ENABLE1_P0_0_01_MRS_CMD_N3,
    DRAM_START = DRAM0,
};

///
/// @brief Write bit traits class
/// @tparam W the DRAM's width
///
template < uint8_t W >
class pdaBitTraits;

///
/// @brief Write bit for PDA specialization for x4 devices
///
template < >
class pdaBitTraits<fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X4>
{
    public:
        // One nibble is needed - the register has one nibble per bit, so one bit
        static constexpr uint64_t NUM_BITS = 1;
        static constexpr uint64_t NUM_DRAMS = MAX_DRAMS_X4;
        static const std::vector<std::pair<uint64_t, uint64_t>> BIT_MAP;
};

///
/// @brief Write bit for PDA specialization for x4 devices
///
template < >
class pdaBitTraits<fapi2::ENUM_ATTR_EFF_DRAM_WIDTH_X8>
{
    public:
        // Two nibbles are needed - the register has one nibble per bit, so two bits
        static constexpr uint64_t NUM_BITS = 2;
        static constexpr uint64_t NUM_DRAMS = MAX_DRAMS_X8;
        static const std::vector<std::pair<uint64_t, uint64_t>> BIT_MAP;
};

///
/// @brief Enables or disables a given DRAM
/// @tparam D the DRAM to enable or disable
/// @param[in,out] io_data the data buffer to modify
/// @param[in] i_state the state to modify the buffer to
///
template< uint64_t D >
inline void set_dram_enable( fapi2::buffer<uint64_t>& io_data, const mss::states& i_state)
{
    io_data.writeBit<D>(i_state);
}

///
/// @brief Configures PDA timings
/// @param[in,out] io_buffer buffer on which the PDA timings live
/// @note Configuring timings is a helper function, as it is used both in PDA and PBA
/// PBA requires that we set the timings, but not enable PDA mode in the WC config, so we have a helper function
///
inline void configure_timings( fapi2::buffer<uint64_t>& io_buffer )
{
    // So we want to:
    // Have a 0 delay between the MRS being sent and starting the 0/1 latching
    mss::wc::set_pda_dq_on_delay(io_buffer, START_DELAY_VALUE);
    // Hold the delay for as long as possible (safer and easier than figuring out how long to hold the values)
    mss::wc::set_pda_dq_off_delay(io_buffer, HOLD_DELAY_VALUE);
}

///
/// @brief Configures PDA timings
/// @param[in] i_target a fapi2::Target MCA
/// @return FAPI2_RC_SUCCESS if and only if ok
///
fapi2::ReturnCode configure_timings_and_enable( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target );

///
/// @brief Configures all DRAM level configuration bits to the inputted settings
/// @param[in] i_target a fapi2::Target MCA
/// @param[in] i_state - OFF - 1's, ON - 0's
/// @return FAPI2_RC_SUCCESS if and only if ok
/// @note PDA is LOW enable, so 0's means ON. ON will configure the register to 0's
///
fapi2::ReturnCode blast_dram_config( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target,
                                     const mss::states& i_state );

///
/// @brief Adds a PDA enable command
/// @param[in] i_target a fapi2::Target DIMM
/// @param[in] i_rank the rank to send to
/// @param[in,out] io_inst the CCS instructions to update
/// @return FAPI2_RC_SUCCESS if and only if ok
///
fapi2::ReturnCode add_enable( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
                              const uint64_t i_rank,
                              std::vector< ccs::instruction_t >& io_inst );

///
/// @brief Enters into and configures PDA mode
/// @param[in] i_target a fapi2::Target DIMM
/// @param[in] i_rank the rank to send to
/// @return FAPI2_RC_SUCCESS if and only if ok
///
fapi2::ReturnCode enter( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
                         const uint64_t i_rank );

///
/// @brief Adds a PDA disable command
/// @param[in] i_target a fapi2::Target DIMM
/// @param[in] i_rank the rank to send to
/// @param[in,out] io_inst the CCS instructions to update
/// @return FAPI2_RC_SUCCESS if and only if ok
///
fapi2::ReturnCode add_disable( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
                               const uint64_t i_rank,
                               std::vector< ccs::instruction_t >& io_inst );

///
/// @brief Exits out of and disables PDA mode
/// @param[in] i_target a fapi2::Target DIMM
/// @param[in] i_rank the rank to send to
/// @return FAPI2_RC_SUCCESS if and only if ok
///
fapi2::ReturnCode exit( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
                        const uint64_t i_rank );

///
/// @brief Helper function for changing the DRAM bit
/// @tparam W the DRAM width
/// @tparam TT the DRAM width traits class
/// @param[in] i_target - the MCA target
/// @param[in] i_dram - the DRAM on which to operate
/// @param[in] i_state - the state to write the bit(s) to
/// @return FAPI2_RC_SUCCESS if and only if ok
///
template< uint8_t W, typename TT = pdaBitTraits<W> >
fapi2::ReturnCode change_dram_bit_helper( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target,
        const uint64_t i_dram,
        const mss::states& i_state)
{
    fapi2::buffer<uint64_t> l_data;

    // Note: the following avoids "undefined reference to" errors due to the set max dram below
    // The use of traits and a const reference messes with it
    constexpr auto NUM_DRAM = TT::NUM_DRAMS;

    // Check bounds
    FAPI_ASSERT(i_dram < NUM_DRAM,
                fapi2::MSS_PDA_DRAM_OUT_OF_RANGE().
                set_MCA_TARGET(i_target).
                set_DRAM(i_dram).
                set_MAX_DRAM(NUM_DRAM),
                "%s was passed DRAM value of %lu which is not below the max value of %lu",
                mss::c_str(i_target), i_dram, NUM_DRAM);

    FAPI_TRY(mss::getScom(i_target, TT::BIT_MAP[i_dram].first, l_data));
    FAPI_TRY(l_data.writeBit(i_state, TT::BIT_MAP[i_dram].second, TT::NUM_BITS));
    FAPI_TRY(mss::putScom(i_target, TT::BIT_MAP[i_dram].first, l_data));

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Writes the data bit enable for the properly inputted DRAM
/// @param[in] i_target - the MCA target
/// @param[in] i_dram - the DRAM on which to operate
/// @param[in] i_state - the state to write the bit(s) to
/// @note PDA is LOW enable, so 0's means ON. ON will configure the register to 0's
///
fapi2::ReturnCode change_dram_bit( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target,
                                   const uint64_t i_dram,
                                   const mss::states& i_state);

///
/// @brief PDA commands container that also has compression
/// @tparam D the MRS command type
///
template< typename D >
class commands
{
    public:
        // Typdefs to make the code more readable
        typedef std::pair<fapi2::Target<fapi2::TARGET_TYPE_DIMM>, uint64_t> rank_target;
        typedef std::map<D, std::vector<uint64_t> > mrs_drams;

        ///
        /// @brief Base constructor
        ///
        commands() = default;

        ///
        /// @brief Base destructor
        ///
        ~commands() = default;

        ///
        /// @brief Adds in a PDA command if need be
        /// @param[in] i_target the MCS target
        /// @param[in] i_rank the rank
        /// @param[in] i_mrs_container the MRS container to add
        /// @param[in] i_dram the DRAM to issue the PDA command to
        /// @return FAPI2_RC_SUCCESS if and only if ok
        ///
        fapi2::ReturnCode add_command( const fapi2::Target<fapi2::TARGET_TYPE_MCA>& i_target,
                                       const uint64_t i_rank,
                                       const D& i_mrs,
                                       const uint64_t i_dram )
        {
            fapi2::Target<fapi2::TARGET_TYPE_DIMM> l_dimm;
            FAPI_TRY( mss::rank::get_dimm_target_from_rank(i_target,
                      i_rank,
                      l_dimm));

            FAPI_TRY(add_command(l_dimm, i_rank, i_mrs, i_dram));

        fapi_try_exit:
            return fapi2::current_err;
        }

        ///
        /// @brief Adds in a PDA command if need be
        /// @param[in] i_target the DIMM target
        /// @param[in] i_rank the rank
        /// @param[in] i_mrs_container the MRS container to add
        /// @param[in] i_dram the DRAM to issue the PDA command to
        /// @return FAPI2_RC_SUCCESS if and only if ok
        ///
        fapi2::ReturnCode add_command( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
                                       const uint64_t i_rank,
                                       const D& i_mrs,
                                       const uint64_t i_dram )
        {
            fapi2::current_err = fapi2::FAPI2_RC_SUCCESS;

            // Check for a valid rank
            FAPI_ASSERT(mss::rank::is_rank_on_dimm(i_target, i_rank),
                        fapi2::MSS_INVALID_RANK().
                        set_PORT_TARGET(mss::find_target<fapi2::TARGET_TYPE_MCA>(i_target)).
                        set_RANK(i_rank).
                        set_FUNCTION(mss::ffdc_function_codes::PDA_ADD_COMMAND),
                        "%s does not have rank %lu", mss::c_str(i_target), i_rank);

            // Does the compression
            iv_commands[ {i_target, i_rank}][i_mrs].push_back(i_dram);

        fapi_try_exit:
            return fapi2::current_err;
        }

        ///
        /// @brief Checks whether the map is empty
        /// @return bool true if empty
        ///
        inline bool empty() const
        {
            return iv_commands.empty();
        }

        ///
        /// @brief Clears the commands
        ///
        inline void clear()
        {
            iv_commands.clear();
        }

        ///
        /// @brief Returns the command information
        /// @return iv_commands
        ///
        inline const typename std::map<rank_target, mrs_drams>& get() const
        {
            return iv_commands;
        }

    private:
        // The following is a map of target/DIMM pairs as the key to a map of
        // the MRS command as the key to the DRAM's to toggle. An explanation as to the data structure is included below

        // PDA compression is a little complex, but is organized to allow us to minimize the number of commands run
        // Each individual map is designed to further minimize the number of commands run
        // The compressed commands consist of a map of pairs within a map
        // The outside map, maps the DIMM/rank to the MRS command and DRAM's that need to be run
        // Basically, it's a list of a specific rank target with all the commands that need to be run
        // The rank-specific target information allows us to just issue the enter/exit commands for PDA for each rank once
        // The MRS commands to the DRAM are then looped over in the inside loop
        // The inside map has a key of the MRS and a value of a map of DRAM's to issue the MRS to
        // CCS does not allow the user to toggle the DQ during an MRS command
        // The DQ information is stored in separate registers in the PHY
        // What this means for issuing the commands is that we have to issue an invocation of CCS for each different MRS command we issue
        // We can issue a single MRS command for multiple DRAM's however
        // Each invocation of CCS creates a noticable increase in time, as the registers need to be configured, CCS needs to be started, and we need to poll for done
        // By only entering into PDA on a DIMM-rank once and by issuing the PDA MRS's to multiple DRAM's at a time, we can save a lot of runtime
        // Note: in shmoo, adding the compression reduced runtime from about 13 minutes down to 3 minutes
        typename std::map<rank_target, mrs_drams> iv_commands;
};

///
/// @brief Performs a PDA WR VREF latch
/// @param[in] i_target a fapi2::Target DIMM
/// @param[in] i_rank the rank to send to
/// @param[in] i_mrs the MRS data to update
/// @param[in] i_drams the DRAM to update
/// @return FAPI2_RC_SUCCESS if and only if ok
/// @note A PDA latch of WR VREF settings is the most common PDA operations
/// This function adds a bit of fanciness (compression) to speed up the overall runtime
///
fapi2::ReturnCode execute_wr_vref_latch( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
        const uint64_t i_rank,
        const mss::ddr4::mrs06_data& i_mrs,
        const std::vector<uint64_t>& i_drams );

///
/// @brief Performs a PDA WR VREF latch
/// @param[in] i_commands the PDA commands to issue and DRAM
/// @return FAPI2_RC_SUCCESS if and only if ok
/// @note A PDA latch of WR VREF settings is the most common PDA operations
/// This function adds a bit of fanciness (compression) to speed up the overall runtime
///
fapi2::ReturnCode execute_wr_vref_latch( const commands<mss::ddr4::mrs06_data>& i_commands );

} // ns pda

} // ns ddr4

} // ns mss

#endif
OpenPOWER on IntegriCloud