Files
DRAMSys/src/libdramsys/DRAMSys/simulation/AddressDecoder.cpp

476 lines
19 KiB
C++

/*
* Copyright (c) 2025, RPTU Kaiserslautern-Landau
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Authors:
* Johannes Feldmann
* Lukas Steiner
* Luiza Correa
* Derek Christ
* Thomas Zimmermann
*/
#include "AddressDecoder.h"
#include "DRAMSys/config/AddressMapping.h"
#include <bit>
#include <bitset>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <cstdint>
#include <set>
namespace DRAMSys
{
/********************/
/* Helper Functions */
/********************/
/**
* @brief Creates a bitmask and stores it in a uint64_t.
*
* @param numBits The number of bits to set to 1.
* @param startIndex The index of the first bit to set to 1.
* @return result The uint64_t where the bitmask will be stored.
*/
uint64_t createBitmask(unsigned numBits, unsigned startIndex) {
// Create the mask by shifting 1's to the correct position
return ((UINT64_C(1) << numBits) - 1) << startIndex;
}
std::vector<std::bitset<AddressDecoder::ADDRESS_WIDTH>> AddressDecoder::transposeMatrix(const std::vector<std::bitset<ADDRESS_WIDTH>>& matrix) {
size_t size = matrix.size();
std::vector<std::bitset<ADDRESS_WIDTH>> transposedMatrix(size);
for (size_t i = 0; i < size; ++i) {
for (size_t j = 0; j < ADDRESS_WIDTH; ++j) {
if (matrix[i].test(j))
transposedMatrix[j].set(i);
}
}
return transposedMatrix;
}
uint64_t AddressDecoder::gf2Multiplication(const uint64_t& inputVec, const std::vector<std::bitset<ADDRESS_WIDTH>>& matrix) const
{
#if defined(__clang__) || defined(__GNUC__)
uint64_t result = 0;
for (size_t i = 0; i < matrix.size(); ++i) {
uint64_t row = matrix[i].to_ullong();
uint64_t val = inputVec & row;
bool parity = __builtin_parityll(val);
result |= (uint64_t(parity) << i);
}
return result;
#else
std::bitset<ADDRESS_WIDTH> resultBits;
std::bitset<ADDRESS_WIDTH> inputBits(inputVec);
for (size_t i = 0; i < matrix.size(); ++i) {
resultBits[i] = (inputBits & matrix[i]).count() % 2;
}
return resultBits.to_ullong();
#endif
// Print input, mapping matrix and output in a readable way (useful for debugging)
// std::cout << "Vec " << ":\t" << std::bitset<ADDRESS_WIDTH>(vector[0]) << std::endl << std::endl;
// for (size_t i = 0; i < mappingMatrix.size(); ++i) {
// std::cout << "Row " << i << ":\t" << mappingMatrix[i] << " | " << resultBits[i] << std::endl;
// }
}
/****************************/
/* AddressDecoder Functions */
/****************************/
AddressDecoder::AddressDecoder(const DRAMSys::Config::AddressMapping& addressMapping) :
highestBitValue(addressMapping.getHighestBit())
{
mappingMatrix = std::vector<std::bitset<ADDRESS_WIDTH>>(highestBitValue + 1);
upperBoundAddress = std::pow(2, highestBitValue + 1) - 1;
auto addBitsToMatrix = [&](const std::optional<std::vector<Config::AddressMapping::BitEntry>> bits, int *rowIndex, std::string_view name)
{
if (!bits.has_value())
return AddressComponent(-1, 0, name);
for (auto row : bits.value()) {
for (unsigned int bit : row) {
mappingMatrix[*rowIndex][bit] = true;
}
(*rowIndex)++;
}
// Care: The rowIndex has been changed. We want the lowest bit, so we must subtract the length!
return AddressComponent(*rowIndex - bits.value().size(), bits.value().size(), name);
};
int rowIndex = 0;
byteBits = addBitsToMatrix(addressMapping.BYTE_BIT, &rowIndex, "By");
columnBits = addBitsToMatrix(addressMapping.COLUMN_BIT, &rowIndex, "Co");
bankGroupBits = addBitsToMatrix(addressMapping.BANKGROUP_BIT, &rowIndex, "BG");
bankBits = addBitsToMatrix(addressMapping.BANK_BIT, &rowIndex, "Ba");
rowBits = addBitsToMatrix(addressMapping.ROW_BIT, &rowIndex, "Ro");
pseudochannelBits = addBitsToMatrix(addressMapping.PSEUDOCHANNEL_BIT, &rowIndex, "PC");
channelBits = addBitsToMatrix(addressMapping.CHANNEL_BIT, &rowIndex, "Ch");
rankBits = addBitsToMatrix(addressMapping.RANK_BIT, &rowIndex, "Ra");
stackBits = addBitsToMatrix(addressMapping.STACK_BIT, &rowIndex, "St");
transposedMappingMatrix = transposeMatrix(mappingMatrix);
bankgroupsPerRank = std::lround(std::pow(2, bankGroupBits.length));
banksPerGroup = std::lround(std::pow(2, bankBits.length));
}
void AddressDecoder::plausibilityCheck(const MemSpec& memSpec)
{
(*this).memSpec = &memSpec;
np2Flag = not allComponentsArePowerOfTwo(memSpec);
// Check if all address bits are used
// TODO: Check if every bit occurs ~exactly~ once or just at least once?
std::bitset<ADDRESS_WIDTH> orBitset(0);
for (auto bitset: mappingMatrix) {
orBitset |= bitset;
}
std::bitset<ADDRESS_WIDTH> mask((1ULL << (highestBitValue + 1)) - 1);
if (orBitset != mask) {
SC_REPORT_FATAL("AddressDecoder", "Not all address bits are used");
}
// Check if the byte bits are continous and starting from 0
uint64_t row = 0;
for (size_t i = 0; i < byteBits.length; i++) {
row |= mappingMatrix[byteBits.idx + i].to_ullong();
}
if (row != ((1ULL << byteBits.length) - 1)) {
SC_REPORT_FATAL("AddressDecoder", "Not all address bits occur exactly once");
}
// Check if the addresss mapping is capable of matching the requirements of the memSpec
checkMemSpecCompatibility(memSpec);
checkMemorySize(memSpec);
checkByteBits(memSpec);
checkBurstLengthBits(memSpec);
}
bool AddressDecoder::allComponentsArePowerOfTwo(const MemSpec& memSpec) const {
// TODO: What parts do we need to check?
return isPowerOfTwo(memSpec.numberOfChannels) &&
isPowerOfTwo(memSpec.ranksPerChannel) &&
isPowerOfTwo(memSpec.bankGroupsPerChannel) &&
isPowerOfTwo(memSpec.banksPerChannel) &&
isPowerOfTwo(memSpec.devicesPerRank) &&
isPowerOfTwo(memSpec.columnsPerRow);
}
void AddressDecoder::checkMemorySize(const MemSpec& memSpec) {
bool isMemorySizeMismatch = memSpec.getSimMemSizeInBytes() > upperBoundAddress + 1 ||
(memSpec.getSimMemSizeInBytes() < upperBoundAddress + 1 && !np2Flag);
if (isMemorySizeMismatch) {
SC_REPORT_FATAL("AddressDecoder", "The mapped bits do not match the memory size");
}
}
void AddressDecoder::checkMemSpecCompatibility(const MemSpec& memSpec) {
unsigned channels = std::lround(std::pow(2, channelBits.length));
unsigned ranks = std::lround(std::pow(2, rankBits.length));
unsigned rows = std::lround(std::pow(2, rowBits.length));
unsigned columns = std::lround(std::pow(2, columnBits.length));
unsigned pseudochannels = std::lround(std::pow(2, pseudochannelBits.length));
unsigned absoluteBankGroups = bankgroupsPerRank * (ranks * pseudochannels);
unsigned absoluteBanks = banksPerGroup * absoluteBankGroups;
// Depending on the NP2 flag we must adapt the strictness of this check
if (np2Flag) {
if (memSpec.numberOfChannels > channels || memSpec.ranksPerChannel > (ranks * pseudochannels) ||
memSpec.bankGroupsPerChannel > absoluteBankGroups ||
memSpec.banksPerChannel > absoluteBanks || memSpec.rowsPerBank > rows ||
memSpec.columnsPerRow > columns)
SC_REPORT_FATAL("AddressDecoder", "Memspec and address mapping do not match");
}
else {
if (memSpec.numberOfChannels != channels || memSpec.ranksPerChannel != (ranks * pseudochannels) ||
memSpec.bankGroupsPerChannel != absoluteBankGroups ||
memSpec.banksPerChannel != absoluteBanks || memSpec.rowsPerBank != rows ||
memSpec.columnsPerRow != columns)
SC_REPORT_FATAL("AddressDecoder", "Memspec and address mapping do not match");
}
}
void AddressDecoder::checkAddressableLimits(const MemSpec& memSpec) {
validateAddressableLimit(memSpec.numberOfChannels, calculateAddressableElements(channelBits.length), "Channel");
validateAddressableLimit(memSpec.ranksPerChannel, calculateAddressableElements(bankBits.length), "Rank");
unsigned addressableBankGroups = calculateAddressableElements(bankGroupBits.length) * calculateAddressableElements(rankBits.length);
unsigned absoluteBanks = calculateAddressableElements(bankBits.length) * addressableBankGroups;
validateAddressableLimit(memSpec.bankGroupsPerChannel, addressableBankGroups, "Bank group");
validateAddressableLimit(memSpec.banksPerChannel, absoluteBanks, "Bank");
validateAddressableLimit(memSpec.rowsPerBank, calculateAddressableElements(rowBits.length), "Row");
validateAddressableLimit(memSpec.columnsPerRow, calculateAddressableElements(columnBits.length), "Column");
}
unsigned AddressDecoder::calculateAddressableElements(unsigned bitSize) const {
return std::lround(std::pow(2, bitSize));
}
void AddressDecoder::validateAddressableLimit(unsigned memSpecValue, unsigned addressableValue, const std::string& name) {
if (memSpecValue > addressableValue || memSpecValue <= (addressableValue >> 1)) {
SC_REPORT_FATAL("AddressDecoder", (name + " bit mapping does not match the memspec configuration").c_str());
}
}
bool AddressDecoder::isPowerOfTwo(unsigned value) const {
return value != 0 && (value & (value - 1)) == 0;
}
unsigned AddressDecoder::checkByteBits(const MemSpec& memSpec) {
unsigned bytesPerBeat = memSpec.dataBusWidth / 8;
unsigned numOfByteBits = std::ceil(std::log2(memSpec.dataBusWidth / 8.0));
if (!isPowerOfTwo(bytesPerBeat)) {
SC_REPORT_WARNING("AddressDecoder",
("Bytes per beat are not power of two! \nAssuming " +
std::to_string(numOfByteBits) + " reserved byte bits.").c_str());
}
if (byteBits.length < numOfByteBits) {
SC_REPORT_FATAL("AddressDecoder",
("Byte bits are not continuous starting from 0. (bytesPerBeat: " +
std::to_string(bytesPerBeat) +
"B -> number of byte-bits: " +
std::to_string(numOfByteBits) + ")").c_str());
}
return numOfByteBits;
}
void AddressDecoder::checkBurstLengthBits(const MemSpec& memSpec) {
unsigned numOfMaxBurstLengthBits = std::ceil(std::log2(memSpec.maxBurstLength));
burstBitMask = createBitmask(numOfMaxBurstLengthBits, byteBits.length);
if (!isPowerOfTwo(memSpec.maxBurstLength)) {
SC_REPORT_WARNING("AddressDecoder",
("Maximum burst length (" + std::to_string(memSpec.maxBurstLength) +
") is not power of two! \nAssuming " +
std::to_string(numOfMaxBurstLengthBits) +
" reserved burst bits.").c_str());
}
std::bitset<ADDRESS_WIDTH> burstBitset(((1 << numOfMaxBurstLengthBits) - 1) << columnBits.idx);
std::bitset<ADDRESS_WIDTH> columnBitset;
for (size_t i = 0; i < columnBits.length; i++) {
columnBitset |= mappingMatrix[columnBits.idx + i];
}
if ((columnBits.length < numOfMaxBurstLengthBits) || ((columnBitset & burstBitset) != burstBitset)) {
SC_REPORT_FATAL("AddressDecoder",
("No continuous column bits for maximum burst length (maximumBurstLength: " +
std::to_string(memSpec.maxBurstLength) +
" -> required number of burst bits: " +
std::to_string(numOfMaxBurstLengthBits) + ")").c_str());
}
}
DecodedAddress AddressDecoder::decodeAddress(uint64_t address) const
{
uint64_t encAddr = address;
if (encAddr > upperBoundAddress)
{
SC_REPORT_WARNING("AddressDecoder",
("Address " + std::to_string(encAddr) +
" out of range (maximum address is " + std::to_string(upperBoundAddress) +
")")
.c_str());
}
uint64_t result = gf2Multiplication(encAddr, mappingMatrix);
/**
* @brief Extracts a specific AddressComponent from the result address.
*/
auto get_component = [&result](const AddressComponent& component) -> unsigned {
if (component.idx < 0 || component.length <= 0) {
return static_cast<unsigned>(0);
}
// Create mask
uint64_t mask = (1ULL << component.length) - 1;
// Shift and apply the mask
return static_cast<unsigned>((result >> component.idx) & mask);
};
DecodedAddress decAddr;
decAddr.channel = get_component(channelBits);
decAddr.rank = get_component(rankBits);
decAddr.rank |= get_component(pseudochannelBits);
decAddr.stack = get_component(stackBits);
decAddr.bankgroup = get_component(bankGroupBits);
decAddr.bank = get_component(bankBits);
decAddr.row = get_component(rowBits);
decAddr.column= get_component(columnBits);
decAddr.byte = get_component(byteBits);
if (np2Flag && !isAddressValid(decAddr))
{
SC_REPORT_WARNING("AddressDecoder",
("Address " + std::to_string(encAddr) + " invalid)").c_str());
}
// Important: This offsets must be added after(!) the address validation!
decAddr.bankgroup = decAddr.bankgroup + decAddr.rank * bankgroupsPerRank;
decAddr.bank = decAddr.bank + decAddr.bankgroup * banksPerGroup;
return decAddr;
}
bool AddressDecoder::isAddressValid(const DecodedAddress& decAddr) const
{
unsigned it;
// Check if burst address is within limits
auto mask = burstBitMask;
for (it = 0; ((mask >> it) & 1) == 0; it++) { }
if ((decAddr.column & (mask >> it)) >= memSpec->maxBurstLength)
{
SC_REPORT_WARNING("AddressDecoder", ("Burst address out of bounds (given: " +
std::to_string((decAddr.column & (mask >> it))) +
", MemSpec: " + std::to_string(memSpec->maxBurstLength) + ")")
.c_str()
);
return false;
}
// Check all address components for validity
if ((decAddr.channel >= memSpec->numberOfChannels) ||
(decAddr.rank >= memSpec->ranksPerChannel) ||
(decAddr.bankgroup >= memSpec->bankGroupsPerChannel) ||
(decAddr.bank >= memSpec->banksPerGroup) ||
(decAddr.row >= memSpec->rowsPerBank) ||
(decAddr.column >= memSpec->columnsPerRow))
{
SC_REPORT_WARNING("AddressDecoder",
"Invalid address: channel, rank, bankgroup, bank, row or column exeeds memSpec limits.");
return false;
}
return true;
}
unsigned AddressDecoder::decodeChannel(uint64_t encAddr) const
{
if (encAddr > upperBoundAddress)
SC_REPORT_WARNING("AddressDecoder",
("Address " + std::to_string(encAddr) +
" out of range (maximum address is " + std::to_string(upperBoundAddress) +
")")
.c_str());
uint64_t result = gf2Multiplication(encAddr, mappingMatrix);
/**
* @brief Extracts a specific AddressComponent from the result address.
*/
auto get_component = [&result](const AddressComponent& component) -> unsigned {
if (component.idx < 0 || component.length <= 0) {
return static_cast<unsigned>(0);
}
// Create mask
uint64_t mask = (1ULL << component.length) - 1;
// Shift and apply the mask
return static_cast<unsigned>((result >> component.idx) & mask);
};
return get_component(channelBits);
}
uint64_t AddressDecoder::encodeAddress(DecodedAddress decAddr) const
{
// Convert absolute addressing for bank, bankgroup to relative
decAddr.bankgroup = decAddr.bankgroup % bankgroupsPerRank;
decAddr.bank = decAddr.bank % banksPerGroup;
uint64_t mappedAddr = 0;
/**
* @brief Inserts a specific AddressComponent to the mappedAddress.
*/
auto set_component = [&mappedAddr](const AddressComponent& component, const unsigned int value) -> unsigned {
if (component.idx < 0 || component.length <= 0) {
return mappedAddr;
}
// Shift and add to mappedAddress
return static_cast<unsigned>((value << component.idx) | mappedAddr);
};
mappedAddr = set_component(channelBits, decAddr.channel);
mappedAddr = set_component(rankBits, decAddr.rank);
mappedAddr = set_component(pseudochannelBits, decAddr.rank);
mappedAddr = set_component(stackBits, decAddr.stack);
mappedAddr = set_component(bankGroupBits, decAddr.bankgroup);
mappedAddr = set_component(bankBits, decAddr.bank);
mappedAddr = set_component(rowBits, decAddr.row);
mappedAddr = set_component(columnBits, decAddr.column);
mappedAddr = set_component(byteBits, decAddr.byte);
return gf2Multiplication(mappedAddr, transposedMappingMatrix);
}
void AddressDecoder::print() const
{
std::cout << headline << std::endl;
std::cout << "Used Address Mapping:" << std::endl;
std::cout << std::endl;
auto printBits = [&](const AddressComponent& component) {
int startIdx = component.idx;
int length = component.length;
if (startIdx < 0) return;
for (int i = 0; i<length; ++i) {
std::cout << " " << component.name << " " << std::setw(2) << mappingMatrix[startIdx + i] << std::endl;
}
};
printBits(byteBits);
printBits(columnBits);
printBits(rowBits);
printBits(bankBits);
printBits(bankGroupBits);
printBits(stackBits);
printBits(rankBits);
printBits(pseudochannelBits);
printBits(channelBits);
}
} // namespace DRAMSys