Some simplifications, adapted print() method.
This commit is contained in:
@@ -34,6 +34,9 @@
|
||||
* Lukas Steiner
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <bitset>
|
||||
|
||||
#include "AddressDecoder.h"
|
||||
#include "utils.h"
|
||||
#include "../configuration/Configuration.h"
|
||||
@@ -102,7 +105,7 @@ AddressDecoder::AddressDecoder(std::string pathToAddressMapping)
|
||||
SC_REPORT_FATAL("AddressDecoder", "No mapping with ID 0 was found.");
|
||||
|
||||
// get XOR connections
|
||||
// An XOR connection needs two parameters: A bank bit and a Row bit.
|
||||
// An XOR connection needs two parameters: A first bit and a second bit.
|
||||
for (tinyxml2::XMLElement *pXor = pNode->FirstChildElement("XOR");
|
||||
pXor != nullptr; pXor = pXor->NextSiblingElement("XOR"))
|
||||
{
|
||||
@@ -110,100 +113,71 @@ AddressDecoder::AddressDecoder(std::string pathToAddressMapping)
|
||||
getUnsignedAttrFromXMLNode(pXor, "SECOND")));
|
||||
}
|
||||
|
||||
unsigned counter = 0;
|
||||
for (tinyxml2::XMLElement *pChannel = pNode->FirstChildElement("CHANNEL_BIT");
|
||||
pChannel != nullptr; pChannel = pChannel->NextSiblingElement("CHANNEL_BIT"))
|
||||
{
|
||||
unsigned nChannel = getUnsignedTextFromXMLNode(pChannel);
|
||||
vChannelBits.push_back(std::pair<unsigned, unsigned>(counter++, nChannel));
|
||||
}
|
||||
vChannelBits.push_back(getUnsignedTextFromXMLNode(pChannel));
|
||||
|
||||
counter = 0;
|
||||
for (tinyxml2::XMLElement *pRank = pNode->FirstChildElement("RANK_BIT");
|
||||
pRank != nullptr; pRank = pRank->NextSiblingElement("RANK_BIT"))
|
||||
{
|
||||
unsigned nRank = getUnsignedTextFromXMLNode(pRank);
|
||||
vRankBits.push_back(std::pair<unsigned, unsigned>(counter++, nRank));
|
||||
}
|
||||
vRankBits.push_back(getUnsignedTextFromXMLNode(pRank));
|
||||
|
||||
counter = 0;
|
||||
for (tinyxml2::XMLElement *pBankGroup = pNode->FirstChildElement("BANKGROUP_BIT");
|
||||
pBankGroup != nullptr; pBankGroup = pBankGroup->NextSiblingElement("BANKGROUP_BIT"))
|
||||
{
|
||||
unsigned nBankGroup = getUnsignedTextFromXMLNode(pBankGroup);
|
||||
vBankGroupBits.push_back(std::pair<unsigned, unsigned>(counter++, nBankGroup));
|
||||
}
|
||||
vBankGroupBits.push_back(getUnsignedTextFromXMLNode(pBankGroup));
|
||||
|
||||
counter = 0;
|
||||
for (tinyxml2::XMLElement *pBank = pNode->FirstChildElement("BANK_BIT");
|
||||
pBank != nullptr; pBank = pBank->NextSiblingElement("BANK_BIT"))
|
||||
{
|
||||
unsigned nBank = getUnsignedTextFromXMLNode(pBank);
|
||||
vBankBits.push_back(std::pair<unsigned, unsigned>(counter++, nBank));
|
||||
}
|
||||
vBankBits.push_back(getUnsignedTextFromXMLNode(pBank));
|
||||
|
||||
counter = 0;
|
||||
for (tinyxml2::XMLElement *pRow = pNode->FirstChildElement("ROW_BIT");
|
||||
pRow != nullptr; pRow = pRow->NextSiblingElement("ROW_BIT")) {
|
||||
unsigned nRow = getUnsignedTextFromXMLNode(pRow);
|
||||
vRowBits.push_back(std::pair<unsigned, unsigned>(counter++, nRow));
|
||||
}
|
||||
pRow != nullptr; pRow = pRow->NextSiblingElement("ROW_BIT"))
|
||||
vRowBits.push_back(getUnsignedTextFromXMLNode(pRow));
|
||||
|
||||
counter = 0;
|
||||
for (tinyxml2::XMLElement *pColumn = pNode->FirstChildElement("COLUMN_BIT");
|
||||
pColumn != nullptr; pColumn = pColumn->NextSiblingElement("COLUMN_BIT"))
|
||||
{
|
||||
unsigned nColumn = getUnsignedTextFromXMLNode(pColumn);
|
||||
vColumnBits.push_back(std::pair<unsigned, unsigned>(counter++, nColumn));
|
||||
}
|
||||
vColumnBits.push_back(getUnsignedTextFromXMLNode(pColumn));
|
||||
|
||||
counter = 0;
|
||||
for (tinyxml2::XMLElement *pByte = pNode->FirstChildElement("BYTE_BIT");
|
||||
pByte != nullptr; pByte = pByte->NextSiblingElement("BYTE_BIT"))
|
||||
{
|
||||
unsigned nByte = getUnsignedTextFromXMLNode(pByte);
|
||||
vByteBits.push_back(std::pair<unsigned, unsigned>(counter++, nByte));
|
||||
}
|
||||
vByteBits.push_back(getUnsignedTextFromXMLNode(pByte));
|
||||
|
||||
amount.channel = pow(2.0, vChannelBits.size());
|
||||
amount.rank = pow(2.0, vRankBits.size());
|
||||
amount.bankgroup = pow(2.0, vBankGroupBits.size());
|
||||
amount.bank = pow(2.0, vBankBits.size());
|
||||
amount.row = pow(2.0, vRowBits.size());
|
||||
amount.column = pow(2.0, vColumnBits.size());
|
||||
amount.bytes = pow(2.0, vByteBits.size());
|
||||
unsigned channels = pow(2.0, vChannelBits.size());
|
||||
unsigned ranks = pow(2.0, vRankBits.size());
|
||||
unsigned bankgroups = pow(2.0, vBankGroupBits.size());
|
||||
unsigned banks = pow(2.0, vBankBits.size());
|
||||
unsigned rows = pow(2.0, vRowBits.size());
|
||||
unsigned columns = pow(2.0, vColumnBits.size());
|
||||
unsigned bytes = pow(2.0, vByteBits.size());
|
||||
|
||||
banksPerGroup = amount.bank;
|
||||
amount.bank = banksPerGroup * amount.bankgroup * amount.rank;
|
||||
maximumAddress = bytes * columns * rows * banks * bankgroups * ranks * channels - 1;
|
||||
|
||||
bankgroupsPerRank = amount.bankgroup;
|
||||
amount.bankgroup = bankgroupsPerRank * amount.rank;
|
||||
banksPerGroup = banks;
|
||||
banks = banksPerGroup * bankgroups * ranks;
|
||||
|
||||
maximumAddress = amount.bytes * amount.column * amount.row *
|
||||
banksPerGroup * bankgroupsPerRank * amount.rank * amount.channel - 1;
|
||||
bankgroupsPerRank = bankgroups;
|
||||
bankgroups = bankgroupsPerRank * ranks;
|
||||
|
||||
Configuration &config = Configuration::getInstance();
|
||||
MemSpec *memSpec = config.memSpec;
|
||||
|
||||
if (config.numberOfMemChannels != amount.channel || memSpec->numberOfRanks != amount.rank
|
||||
|| memSpec->numberOfBankGroups != amount.bankgroup || memSpec->numberOfBanks != amount.bank
|
||||
|| memSpec->numberOfRows != amount.row || memSpec->numberOfColumns != amount.column
|
||||
|| config.numberOfDevicesOnDIMM * memSpec->bitWidth != amount.bytes * 8)
|
||||
if (config.numberOfMemChannels != channels || memSpec->numberOfRanks != ranks
|
||||
|| memSpec->numberOfBankGroups != bankgroups || memSpec->numberOfBanks != banks
|
||||
|| memSpec->numberOfRows != rows || memSpec->numberOfColumns != columns
|
||||
|| config.numberOfDevicesOnDIMM * memSpec->bitWidth != bytes * 8)
|
||||
SC_REPORT_FATAL("AddressDecoder", "Memspec and address mapping do not match");
|
||||
}
|
||||
|
||||
DecodedAddress AddressDecoder::decodeAddress(uint64_t encAddr)
|
||||
{
|
||||
// if (encAddr > maximumAddress)
|
||||
// SC_REPORT_WARNING("AddressDecoder", ("Address " + std::to_string(encAddr) + " out of range (maximum address is " + std::to_string(maximumAddress) + ")").c_str());
|
||||
if (encAddr > maximumAddress)
|
||||
SC_REPORT_WARNING("AddressDecoder", ("Address " + std::to_string(encAddr) + " out of range (maximum address is " + std::to_string(maximumAddress) + ")").c_str());
|
||||
|
||||
// Apply XOR
|
||||
// For each used xor:
|
||||
// Get the bank bit and row bit. Apply a bitwise xor operator and save it back to the bank bit.
|
||||
// Get the first bit and second bit. Apply a bitwise xor operator and save it back to the first bit.
|
||||
for (auto it = vXor.begin(); it != vXor.end(); it++)
|
||||
{
|
||||
unsigned xoredBit;
|
||||
// Bank Row
|
||||
xoredBit = (((encAddr >> it->first) & 1) ^ ((encAddr >> it->second) & 1));
|
||||
encAddr &= ~(1 << it->first);
|
||||
encAddr |= xoredBit << it->first;
|
||||
@@ -211,29 +185,26 @@ DecodedAddress AddressDecoder::decodeAddress(uint64_t encAddr)
|
||||
|
||||
DecodedAddress decAddr;
|
||||
|
||||
decAddr.channel = 0;
|
||||
for (auto it = vChannelBits.begin(); it != vChannelBits.end(); it++)
|
||||
decAddr.channel |= ((encAddr >> it->second) & 1) << it->first;
|
||||
for (unsigned it = 0; it < vChannelBits.size(); it++)
|
||||
decAddr.channel |= ((encAddr >> vChannelBits[it]) & 1) << it;
|
||||
|
||||
decAddr.rank = 0;
|
||||
for (auto it = vRankBits.begin(); it != vRankBits.end(); it++)
|
||||
decAddr.rank |= ((encAddr >> it->second) & 1) << it->first;
|
||||
for (unsigned it = 0; it < vRankBits.size(); it++)
|
||||
decAddr.rank |= ((encAddr >> vRankBits[it]) & 1) << it;
|
||||
|
||||
decAddr.bankgroup = 0;
|
||||
for (auto it = vBankGroupBits.begin(); it != vBankGroupBits.end(); it++)
|
||||
decAddr.bankgroup |= ((encAddr >> it->second) & 1) << it->first;
|
||||
for (unsigned it = 0; it < vBankGroupBits.size(); it++)
|
||||
decAddr.bankgroup |= ((encAddr >> vBankGroupBits[it]) & 1) << it;
|
||||
|
||||
decAddr.bank = 0;
|
||||
for (auto it = vBankBits.begin(); it != vBankBits.end(); it++)
|
||||
decAddr.bank |= ((encAddr >> it->second) & 1) << it->first;
|
||||
for (unsigned it = 0; it < vBankBits.size(); it++)
|
||||
decAddr.bank |= ((encAddr >> vBankBits[it]) & 1) << it;
|
||||
|
||||
decAddr.row = 0;
|
||||
for (auto it = vRowBits.begin(); it != vRowBits.end(); it++)
|
||||
decAddr.row |= ((encAddr >> it->second) & 1) << it->first;
|
||||
for (unsigned it = 0; it < vRowBits.size(); it++)
|
||||
decAddr.row |= ((encAddr >> vRowBits[it]) & 1) << it;
|
||||
|
||||
decAddr.column = 0;
|
||||
for (auto it = vColumnBits.begin(); it != vColumnBits.end(); it++)
|
||||
decAddr.column |= ((encAddr >> it->second) & 1) << it->first;
|
||||
for (unsigned it = 0; it < vColumnBits.size(); it++)
|
||||
decAddr.column |= ((encAddr >> vColumnBits[it]) & 1) << it;
|
||||
|
||||
for (unsigned it = 0; it < vByteBits.size(); it++)
|
||||
decAddr.byte |= ((encAddr >> vByteBits[it]) & 1) << it;
|
||||
|
||||
decAddr.bankgroup = decAddr.bankgroup + decAddr.rank * bankgroupsPerRank;
|
||||
decAddr.bank = decAddr.bank + decAddr.bankgroup * banksPerGroup;
|
||||
@@ -241,77 +212,89 @@ DecodedAddress AddressDecoder::decodeAddress(uint64_t encAddr)
|
||||
return decAddr;
|
||||
}
|
||||
|
||||
uint64_t AddressDecoder::encodeAddress(DecodedAddress decAddr)
|
||||
{
|
||||
decAddr.bankgroup = decAddr.bankgroup % bankgroupsPerRank;
|
||||
decAddr.bank = decAddr.bank % banksPerGroup;
|
||||
|
||||
uint64_t encAddr = 0;
|
||||
|
||||
for (auto it = vChannelBits.begin(); it != vChannelBits.end(); it++)
|
||||
encAddr |= ((decAddr.channel >> it->first) & 1) << it->second;
|
||||
|
||||
for (auto it = vRankBits.begin(); it != vRankBits.end(); it++)
|
||||
encAddr |= ((decAddr.rank >> it->first) & 1) << it->second;
|
||||
|
||||
for (auto it = vBankGroupBits.begin(); it != vBankGroupBits.end(); it++)
|
||||
encAddr |= ((decAddr.bankgroup >> it->first) & 1) << it->second;
|
||||
|
||||
for (auto it = vBankBits.begin(); it != vBankBits.end(); it++)
|
||||
encAddr |= ((decAddr.bank >> it->first) & 1) << it->second;
|
||||
|
||||
for (auto it = vRowBits.begin(); it != vRowBits.end(); it++)
|
||||
encAddr |= ((decAddr.row >> it->first) & 1) << it->second;
|
||||
|
||||
for (auto it = vColumnBits.begin(); it != vColumnBits.end(); it++)
|
||||
encAddr |= ((decAddr.column >> it->first) & 1) << it->second;
|
||||
|
||||
for (auto it = vByteBits.begin(); it != vByteBits.end(); it++)
|
||||
encAddr |= ((decAddr.bytes >> it->first) & 1) << it->second;
|
||||
|
||||
// Apply XOR
|
||||
// For each used xor:
|
||||
// Get the bank bit and row bit. Apply a bitwise xor operator and save it back to the bank bit.
|
||||
for (auto it = vXor.begin(); it != vXor.end(); it++)
|
||||
{
|
||||
unsigned xoredBit;
|
||||
xoredBit = (((encAddr >> it->first) & 1) ^ ((encAddr >> it->second) & 1));
|
||||
encAddr &= ~(1 << it->first);
|
||||
encAddr |= xoredBit << it->first;
|
||||
}
|
||||
|
||||
return encAddr;
|
||||
}
|
||||
|
||||
void AddressDecoder::print()
|
||||
{
|
||||
std::map<unsigned, std::pair<unsigned, char>> output;
|
||||
|
||||
for (auto it = vBankBits.begin(); it != vBankBits.end(); it++) {
|
||||
output[it->second] = std::pair<unsigned, char>(it->first, 'B');
|
||||
}
|
||||
for (auto it = vRowBits.begin(); it != vRowBits.end(); it++) {
|
||||
output[it->second] = std::pair<unsigned, char>(it->first, 'R');
|
||||
}
|
||||
for (auto it = vColumnBits.begin(); it != vColumnBits.end(); it++) {
|
||||
output[it->second] = std::pair<unsigned, char>(it->first, 'C');
|
||||
}
|
||||
|
||||
// add byte bits
|
||||
output[0] = std::pair<unsigned, char>(0, 'b');
|
||||
output[1] = std::pair<unsigned, char>(1, 'b');
|
||||
output[2] = std::pair<unsigned, char>(2, 'b');
|
||||
|
||||
std::cout << headline << std::endl;
|
||||
std::cout << "Used Address Mapping:" << std::endl;
|
||||
std::cout << std::endl;
|
||||
for (unsigned i = 0; i < 32; i++) {
|
||||
std::cout << " " << i << " ";
|
||||
|
||||
for (int it = vChannelBits.size() - 1; it >= 0; it--)
|
||||
{
|
||||
uint64_t addressBits = (1 << vChannelBits[it]);
|
||||
for (auto it2 : vXor)
|
||||
{
|
||||
if (it2.first == vChannelBits[it])
|
||||
addressBits |= (1 << it2.second);
|
||||
}
|
||||
std::cout << " Ch " << std::setw(2) << it << ": " << std::bitset<64>(addressBits) << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
for (unsigned i = 0; i < 32; i++) {
|
||||
std::cout << " " << output[i].second << "(" << output[i].first << ") ";
|
||||
|
||||
for (int it = vRankBits.size() - 1; it >= 0; it--)
|
||||
{
|
||||
uint64_t addressBits = (1 << vRankBits[it]);
|
||||
for (auto it2 : vXor)
|
||||
{
|
||||
if (it2.first == vRankBits[it])
|
||||
addressBits |= (1 << it2.second);
|
||||
}
|
||||
std::cout << " Ra " << std::setw(2) << it << ": " << std::bitset<64>(addressBits) << std::endl;
|
||||
}
|
||||
|
||||
for (int it = vBankGroupBits.size() - 1; it >= 0; it--)
|
||||
{
|
||||
uint64_t addressBits = (1 << vBankGroupBits[it]);
|
||||
for (auto it2 : vXor)
|
||||
{
|
||||
if (it2.first == vBankGroupBits[it])
|
||||
addressBits |= (1 << it2.second);
|
||||
}
|
||||
std::cout << " Bg " << std::setw(2) << it << ": " << std::bitset<64>(addressBits) << std::endl;
|
||||
}
|
||||
|
||||
for (int it = vBankBits.size() - 1; it >= 0; it--)
|
||||
{
|
||||
uint64_t addressBits = (1 << vBankBits[it]);
|
||||
for (auto it2 : vXor)
|
||||
{
|
||||
if (it2.first == vBankBits[it])
|
||||
addressBits |= (1 << it2.second);
|
||||
}
|
||||
std::cout << " Ba " << std::setw(2) << it << ": " << std::bitset<64>(addressBits) << std::endl;
|
||||
}
|
||||
|
||||
for (int it = vRowBits.size() - 1; it >= 0; it--)
|
||||
{
|
||||
uint64_t addressBits = (1 << vRowBits[it]);
|
||||
for (auto it2 : vXor)
|
||||
{
|
||||
if (it2.first == vRowBits[it])
|
||||
addressBits |= (1 << it2.second);
|
||||
}
|
||||
std::cout << " Ro " << std::setw(2) << it << ": " << std::bitset<64>(addressBits) << std::endl;
|
||||
}
|
||||
|
||||
for (int it = vColumnBits.size() - 1; it >= 0; it--)
|
||||
{
|
||||
uint64_t addressBits = (1 << vColumnBits[it]);
|
||||
for (auto it2 : vXor)
|
||||
{
|
||||
if (it2.first == vColumnBits[it])
|
||||
addressBits |= (1 << it2.second);
|
||||
}
|
||||
std::cout << " Co " << std::setw(2) << it << ": " << std::bitset<64>(addressBits) << std::endl;
|
||||
}
|
||||
|
||||
for (int it = vByteBits.size() - 1; it >= 0; it--)
|
||||
{
|
||||
uint64_t addressBits = (1 << vByteBits[it]);
|
||||
for (auto it2 : vXor)
|
||||
{
|
||||
if (it2.first == vByteBits[it])
|
||||
addressBits |= (1 << it2.second);
|
||||
}
|
||||
std::cout << " By " << std::setw(2) << it << ": " << std::bitset<64>(addressBits) << std::endl;
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,11 +51,11 @@ struct DecodedAddress
|
||||
unsigned bankgroup, unsigned bank,
|
||||
unsigned row, unsigned column, unsigned bytes)
|
||||
: channel(channel), rank(rank), bankgroup(bankgroup),
|
||||
bank(bank), row(row), column(column), bytes(bytes) {}
|
||||
bank(bank), row(row), column(column), byte(bytes) {}
|
||||
|
||||
DecodedAddress()
|
||||
: channel(0), rank(0), bankgroup(0),
|
||||
bank(0), row(0), column(0), bytes(0) {}
|
||||
bank(0), row(0), column(0), byte(0) {}
|
||||
|
||||
unsigned channel;
|
||||
unsigned rank;
|
||||
@@ -63,56 +63,37 @@ struct DecodedAddress
|
||||
unsigned bank;
|
||||
unsigned row;
|
||||
unsigned column;
|
||||
unsigned bytes;
|
||||
unsigned byte;
|
||||
};
|
||||
|
||||
class AddressDecoder
|
||||
{
|
||||
public:
|
||||
AddressDecoder(std::string);
|
||||
|
||||
DecodedAddress decodeAddress(uint64_t addr);
|
||||
uint64_t encodeAddress(DecodedAddress n);
|
||||
|
||||
void print();
|
||||
|
||||
struct Amount
|
||||
{
|
||||
unsigned channel = 1;
|
||||
unsigned rank = 1;
|
||||
unsigned bankgroup = 1;
|
||||
unsigned bank = 1;
|
||||
unsigned row = 1;
|
||||
unsigned column = 1;
|
||||
unsigned bytes = 1;
|
||||
} amount;
|
||||
|
||||
private:
|
||||
unsigned banksPerGroup;
|
||||
unsigned banksPerRank;
|
||||
unsigned bankgroupsPerRank;
|
||||
|
||||
uint64_t maximumAddress;
|
||||
|
||||
// This container stores for each used xor gate a pair which consists of "First/Number of an address bit which corresponds to a bank" and "Second/Number of an address bit which corresponds to a row"
|
||||
std::vector<std::pair<unsigned, unsigned>> vXor;
|
||||
std::vector<std::pair<unsigned, unsigned>> vChannelBits;
|
||||
std::vector<std::pair<unsigned, unsigned>> vRankBits;
|
||||
std::vector<std::pair<unsigned, unsigned>> vBankGroupBits;
|
||||
// This container stores for each bank bit a pair which consists of "First/Number of the bank bit" and "Second/Number of the address bit"
|
||||
std::vector<std::pair<unsigned, unsigned>> vBankBits;
|
||||
// This container stores for each row bit a pair which consists of "First/Number of the row bit" and "Second/Number of the address bit"
|
||||
std::vector<std::pair<unsigned, unsigned>> vRowBits;
|
||||
// This container stores for each column bit a pair which consists of "First/Number of the column bit" and "Second/Number of the address bit"
|
||||
std::vector<std::pair<unsigned, unsigned>> vColumnBits;
|
||||
std::vector<std::pair<unsigned, unsigned>> vByteBits;
|
||||
|
||||
private:
|
||||
tinyxml2::XMLElement *getXMLNode(tinyxml2::XMLElement *pRoot,
|
||||
std::string strName);
|
||||
unsigned int getUnsignedTextFromXMLNode(tinyxml2::XMLElement *pRoot);
|
||||
unsigned int getUnsignedAttrFromXMLNode(tinyxml2::XMLElement *pRoot,
|
||||
std::string strName);
|
||||
|
||||
unsigned banksPerGroup;
|
||||
unsigned bankgroupsPerRank;
|
||||
|
||||
uint64_t maximumAddress;
|
||||
|
||||
// This container stores for each used xor gate a pair of address bits, the first bit is overwritten with the result
|
||||
std::vector<std::pair<unsigned, unsigned>> vXor;
|
||||
std::vector<unsigned> vChannelBits;
|
||||
std::vector<unsigned> vRankBits;
|
||||
std::vector<unsigned> vBankGroupBits;
|
||||
std::vector<unsigned> vBankBits;
|
||||
std::vector<unsigned> vRowBits;
|
||||
std::vector<unsigned> vColumnBits;
|
||||
std::vector<unsigned> vByteBits;
|
||||
};
|
||||
|
||||
#endif // ADDRESSDECODER_H
|
||||
|
||||
@@ -167,7 +167,7 @@ void errorModel::store(tlm::tlm_generic_payload &trans)
|
||||
|
||||
std::stringstream msg;
|
||||
msg << "bank: " << key.bank << " group: " << key.bankgroup << " bytes: " <<
|
||||
key.bytes << " channel: " << key.channel << " column: " << key.column <<
|
||||
key.byte << " channel: " << key.channel << " column: " << key.column <<
|
||||
" rank: " << key.rank << " row: " << key.row;
|
||||
PRINTDEBUGMESSAGE(name(), msg.str());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user