blob: 6f36c604d7baff6b5fc4148fd26994b957791247 [file] [log] [blame] [edit]
#include "ddimm_parser.hpp"
#include "constants.hpp"
#include "exceptions.hpp"
#include <cmath>
#include <cstdint>
#include <iostream>
#include <numeric>
#include <string>
namespace vpd
{
static constexpr auto SDRAM_DENSITY_PER_DIE_24GB = 24;
static constexpr auto SDRAM_DENSITY_PER_DIE_32GB = 32;
static constexpr auto SDRAM_DENSITY_PER_DIE_48GB = 48;
static constexpr auto SDRAM_DENSITY_PER_DIE_64GB = 64;
static constexpr auto SDRAM_DENSITY_PER_DIE_UNDEFINED = 0;
static constexpr auto PRIMARY_BUS_WIDTH_32_BITS = 32;
static constexpr auto PRIMARY_BUS_WIDTH_UNUSED = 0;
static constexpr auto DRAM_MANUFACTURER_ID_OFFSET = 0x228;
static constexpr auto DRAM_MANUFACTURER_ID_LENGTH = 0x02;
bool DdimmVpdParser::checkValidValue(uint8_t i_ByteValue, uint8_t i_shift,
uint8_t i_minValue, uint8_t i_maxValue)
{
bool l_isValid = true;
uint8_t l_ByteValue = i_ByteValue >> i_shift;
if ((l_ByteValue > i_maxValue) || (l_ByteValue < i_minValue))
{
logging::logMessage(
"Non valid Value encountered value[" + std::to_string(l_ByteValue) +
"] range [" + std::to_string(i_minValue) + ".." +
std::to_string(i_maxValue) + "] found ");
return false;
}
return l_isValid;
}
uint8_t DdimmVpdParser::getDdr5DensityPerDie(uint8_t i_ByteValue)
{
uint8_t l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED;
if (i_ByteValue < constants::VALUE_5)
{
l_densityPerDie = i_ByteValue * constants::VALUE_4;
}
else
{
switch (i_ByteValue)
{
case constants::VALUE_5:
l_densityPerDie = SDRAM_DENSITY_PER_DIE_24GB;
break;
case constants::VALUE_6:
l_densityPerDie = SDRAM_DENSITY_PER_DIE_32GB;
break;
case constants::VALUE_7:
l_densityPerDie = SDRAM_DENSITY_PER_DIE_48GB;
break;
case constants::VALUE_8:
l_densityPerDie = SDRAM_DENSITY_PER_DIE_64GB;
break;
default:
logging::logMessage(
"default value encountered for density per die");
l_densityPerDie = SDRAM_DENSITY_PER_DIE_UNDEFINED;
break;
}
}
return l_densityPerDie;
}
uint8_t DdimmVpdParser::getDdr5DiePerPackage(uint8_t i_ByteValue)
{
uint8_t l_DiePerPackage = constants::VALUE_0;
if (i_ByteValue < constants::VALUE_2)
{
l_DiePerPackage = i_ByteValue + constants::VALUE_1;
}
else
{
l_DiePerPackage =
pow(constants::VALUE_2, (i_ByteValue - constants::VALUE_1));
}
return l_DiePerPackage;
}
size_t DdimmVpdParser::getDdr5BasedDdimmSize(
types::BinaryVector::const_iterator i_iterator)
{
size_t l_dimmSize = 0;
do
{
if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] &
constants::MASK_BYTE_BITS_01,
constants::SHIFT_BITS_0, constants::VALUE_1,
constants::VALUE_3) ||
!checkValidValue(i_iterator[constants::SPD_BYTE_235] &
constants::MASK_BYTE_BITS_345,
constants::SHIFT_BITS_3, constants::VALUE_1,
constants::VALUE_3))
{
logging::logMessage(
"Capacity calculation failed for channels per DIMM. DDIMM Byte "
"235 value [" +
std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]");
break;
}
uint8_t l_channelsPerPhy =
(((i_iterator[constants::SPD_BYTE_235] &
constants::MASK_BYTE_BITS_01)
? constants::VALUE_1
: constants::VALUE_0) +
((i_iterator[constants::SPD_BYTE_235] &
constants::MASK_BYTE_BITS_345)
? constants::VALUE_1
: constants::VALUE_0));
uint8_t l_channelsPerDdimm =
(((i_iterator[constants::SPD_BYTE_235] &
constants::MASK_BYTE_BIT_6) >>
constants::VALUE_6) +
((i_iterator[constants::SPD_BYTE_235] &
constants::MASK_BYTE_BIT_7) >>
constants::VALUE_7)) *
l_channelsPerPhy;
if (!checkValidValue(i_iterator[constants::SPD_BYTE_235] &
constants::MASK_BYTE_BITS_012,
constants::SHIFT_BITS_0, constants::VALUE_1,
constants::VALUE_3))
{
logging::logMessage(
"Capacity calculation failed for bus width per channel. DDIMM "
"Byte 235 value [" +
std::to_string(i_iterator[constants::SPD_BYTE_235]) + "]");
break;
}
uint8_t l_busWidthPerChannel =
(i_iterator[constants::SPD_BYTE_235] &
constants::MASK_BYTE_BITS_012)
? PRIMARY_BUS_WIDTH_32_BITS
: PRIMARY_BUS_WIDTH_UNUSED;
if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] &
constants::MASK_BYTE_BITS_567,
constants::SHIFT_BITS_5, constants::VALUE_0,
constants::VALUE_5))
{
logging::logMessage(
"Capacity calculation failed for die per package. DDIMM Byte 4 "
"value [" +
std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]");
break;
}
uint8_t l_diePerPackage = getDdr5DiePerPackage(
(i_iterator[constants::SPD_BYTE_4] &
constants::MASK_BYTE_BITS_567) >>
constants::VALUE_5);
if (!checkValidValue(i_iterator[constants::SPD_BYTE_4] &
constants::MASK_BYTE_BITS_01234,
constants::SHIFT_BITS_0, constants::VALUE_1,
constants::VALUE_8))
{
logging::logMessage(
"Capacity calculation failed for SDRAM Density per Die. DDIMM "
"Byte 4 value [" +
std::to_string(i_iterator[constants::SPD_BYTE_4]) + "]");
break;
}
uint8_t l_densityPerDie = getDdr5DensityPerDie(
i_iterator[constants::SPD_BYTE_4] &
constants::MASK_BYTE_BITS_01234);
uint8_t l_ranksPerChannel = 0;
if (((i_iterator[constants::SPD_BYTE_234] &
constants::MASK_BYTE_BIT_7) >>
constants::VALUE_7))
{
l_ranksPerChannel = ((i_iterator[constants::SPD_BYTE_234] &
constants::MASK_BYTE_BITS_345) >>
constants::VALUE_3) +
constants::VALUE_1;
}
else if (((i_iterator[constants::SPD_BYTE_235] &
constants::MASK_BYTE_BIT_6) >>
constants::VALUE_6))
{
l_ranksPerChannel = (i_iterator[constants::SPD_BYTE_234] &
constants::MASK_BYTE_BITS_012) +
constants::VALUE_1;
}
if (!checkValidValue(i_iterator[constants::SPD_BYTE_6] &
constants::MASK_BYTE_BITS_567,
constants::SHIFT_BITS_5, constants::VALUE_0,
constants::VALUE_3))
{
logging::logMessage(
"Capacity calculation failed for dram width DDIMM Byte 6 value "
"[" +
std::to_string(i_iterator[constants::SPD_BYTE_6]) + "]");
break;
}
uint8_t l_dramWidth =
constants::VALUE_4 *
(constants::VALUE_1 << ((i_iterator[constants::SPD_BYTE_6] &
constants::MASK_BYTE_BITS_567) >>
constants::VALUE_5));
// DDIMM size is calculated in GB
l_dimmSize = (l_channelsPerDdimm * l_busWidthPerChannel *
l_diePerPackage * l_densityPerDie * l_ranksPerChannel) /
(8 * l_dramWidth);
} while (false);
return constants::CONVERT_GB_TO_KB * l_dimmSize;
}
size_t DdimmVpdParser::getDdr4BasedDdimmSize(
types::BinaryVector::const_iterator i_iterator)
{
size_t l_dimmSize = 0;
try
{
uint8_t l_tmpValue = 0;
// Calculate SDRAM capacity
l_tmpValue = i_iterator[constants::SPD_BYTE_4] &
constants::JEDEC_SDRAM_CAP_MASK;
/* Make sure the bits are not Reserved */
if (l_tmpValue > constants::JEDEC_SDRAMCAP_RESERVED)
{
throw std::runtime_error(
"Bad data in VPD byte 4. Can't calculate SDRAM capacity and so "
"dimm size.\n ");
}
uint16_t l_sdramCapacity = 1;
l_sdramCapacity = (l_sdramCapacity << l_tmpValue) *
constants::JEDEC_SDRAMCAP_MULTIPLIER;
/* Calculate Primary bus width */
l_tmpValue = i_iterator[constants::SPD_BYTE_13] &
constants::JEDEC_PRI_BUS_WIDTH_MASK;
if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
{
throw std::runtime_error(
"Bad data in VPD byte 13. Can't calculate primary bus width "
"and so dimm size.");
}
uint8_t l_primaryBusWid = 1;
l_primaryBusWid = (l_primaryBusWid << l_tmpValue) *
constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER;
/* Calculate SDRAM width */
l_tmpValue = i_iterator[constants::SPD_BYTE_12] &
constants::JEDEC_SDRAM_WIDTH_MASK;
if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
{
throw std::runtime_error(
"Bad data in VPD byte 12. Can't calculate SDRAM width and so "
"dimm size.");
}
uint8_t l_sdramWidth = 1;
l_sdramWidth = (l_sdramWidth << l_tmpValue) *
constants::JEDEC_SDRAM_WIDTH_MULTIPLIER;
/* Calculate Number of ranks */
l_tmpValue = i_iterator[constants::SPD_BYTE_12] &
constants::JEDEC_NUM_RANKS_MASK;
l_tmpValue >>= constants::JEDEC_RESERVED_BITS;
if (l_tmpValue > constants::JEDEC_RESERVED_BITS)
{
throw std::runtime_error(
"Bad data in VPD byte 12, can't calculate number of ranks. Invalid data found.");
}
uint8_t l_logicalRanksPerDimm = l_tmpValue + 1;
// Determine is single load stack (3DS) or not
l_tmpValue = i_iterator[constants::SPD_BYTE_6] &
constants::JEDEC_SIGNAL_LOADING_MASK;
if (l_tmpValue == constants::JEDEC_SINGLE_LOAD_STACK)
{
// Fetch die count
l_tmpValue = i_iterator[constants::SPD_BYTE_6] &
constants::JEDEC_DIE_COUNT_MASK;
l_tmpValue >>= constants::JEDEC_DIE_COUNT_RIGHT_SHIFT;
uint8_t l_dieCount = l_tmpValue + 1;
l_logicalRanksPerDimm *= l_dieCount;
}
l_dimmSize =
(l_sdramCapacity / constants::JEDEC_PRI_BUS_WIDTH_MULTIPLIER) *
(l_primaryBusWid / l_sdramWidth) * l_logicalRanksPerDimm;
// Converting dimm size from MB to KB
l_dimmSize *= constants::CONVERT_MB_TO_KB;
}
catch (const std::exception& l_ex)
{
// TODO:: Need an error log here
logging::logMessage("DDR4 DDIMM calculation is failed, reason: " +
std::string(l_ex.what()));
}
return l_dimmSize;
}
size_t DdimmVpdParser::getDdimmSize(
types::BinaryVector::const_iterator i_iterator)
{
size_t l_dimmSize = 0;
if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR5)
{
l_dimmSize = getDdr5BasedDdimmSize(i_iterator);
}
else if (i_iterator[constants::SPD_BYTE_2] == constants::SPD_DRAM_TYPE_DDR4)
{
l_dimmSize = getDdr4BasedDdimmSize(i_iterator);
}
else
{
logging::logMessage(
"Error: DDIMM is neither DDR4 nor DDR5. DDIMM Byte 2 value [" +
std::to_string(i_iterator[constants::SPD_BYTE_2]) + "]");
}
return l_dimmSize;
}
void DdimmVpdParser::readKeywords(
types::BinaryVector::const_iterator i_iterator)
{
// collect DDIMM size value
auto l_dimmSize = getDdimmSize(i_iterator);
if (!l_dimmSize)
{
throw(DataException("Error: Calculated dimm size is 0."));
}
m_parsedVpdMap.emplace("MemorySizeInKB", l_dimmSize);
// point the i_iterator to DIMM data and skip "11S"
advance(i_iterator, constants::DDIMM_11S_BARCODE_START +
constants::DDIMM_11S_FORMAT_LEN);
types::BinaryVector l_partNumber(i_iterator,
i_iterator + constants::PART_NUM_LEN);
advance(i_iterator, constants::PART_NUM_LEN);
types::BinaryVector l_serialNumber(i_iterator,
i_iterator + constants::SERIAL_NUM_LEN);
advance(i_iterator, constants::SERIAL_NUM_LEN);
types::BinaryVector l_ccin(i_iterator, i_iterator + constants::CCIN_LEN);
types::BinaryVector l_mfgId(DRAM_MANUFACTURER_ID_LENGTH);
std::copy_n((m_vpdVector.cbegin() + DRAM_MANUFACTURER_ID_OFFSET),
DRAM_MANUFACTURER_ID_LENGTH, l_mfgId.begin());
m_parsedVpdMap.emplace("FN", l_partNumber);
m_parsedVpdMap.emplace("PN", move(l_partNumber));
m_parsedVpdMap.emplace("SN", move(l_serialNumber));
m_parsedVpdMap.emplace("CC", move(l_ccin));
m_parsedVpdMap.emplace("DI", move(l_mfgId));
}
types::VPDMapVariant DdimmVpdParser::parse()
{
try
{
// Read the data and return the map
auto l_iterator = m_vpdVector.cbegin();
readKeywords(l_iterator);
return m_parsedVpdMap;
}
catch (const std::exception& exp)
{
logging::logMessage(exp.what());
throw exp;
}
}
} // namespace vpd