Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions src/sst/elements/carcosa/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# -*- Makefile -*-
#
#

AM_CPPFLAGS += \
$(MPI_CPPFLAGS) \
-I$(top_srcdir)/src

compdir = $(pkglibdir)
comp_LTLIBRARIES = libcarcosa.la
libcarcosa_la_SOURCES = \
injectors/faultInjectorBase.cc \
injectors/faultInjectorBase.h \
injectors/stuckAtFaultInjector.cc \
injectors/stuckAtFaultInjector.h \
injectors/corruptMemFaultInjector.cc \
injectors/corruptMemFaultInjector.h \
injectors/randomDropFaultInjector.cc \
injectors/randomDropFaultInjector.h \
injectors/randomFlipFaultInjector.cc \
injectors/randomFlipFaultInjector.h \
injectors/dropFlipFaultInjector.cc \
injectors/dropFlipFaultInjector.h \
faultlogic/faultBase.cc \
faultlogic/faultBase.h \
faultlogic/stuckAtFault.cc \
faultlogic/stuckAtFault.h \
faultlogic/corruptMemFault.cc \
faultlogic/corruptMemFault.h \
faultlogic/randomDropFault.cc \
faultlogic/randomDropFault.h \
faultlogic/randomFlipFault.cc \
faultlogic/randomFlipFault.h

EXTRA_DIST = \
tests/testCorruptMemBasic.py \
tests/testCorruptMemDouble.py \
tests/testCorruptMemDoubleOverlap.py \
tests/testRandomDrop.py \
tests/testRandomFlip.py \
tests/testStuckAtBasic.py \
tests/testStuckAtMultiple.py \
tests/testStuckAtOverlap.py \
tests/testStuckAtSameByte.py

sstdir = $(includedir)/sst/elements/carcosa
nobase_sst_HEADERS = \
injectors/faultInjectorBase.h \
injectors/stuckAtFaultInjector.h \
injectors/corruptMemFaultInjector.h \
injectors/randomDropFaultInjector.h \
injectors/randomFlipFaultInjector.h \
injectors/dropFlipFaultInjector.h \
faultlogic/faultBase.h \
faultlogic/stuckAtFault.h \
faultlogic/corruptMemFault.h \
faultlogic/randomDropFault.h \
faultlogic/randomFlipFault.h

libcarcosa_la_LDFLAGS = -module -avoid-version
libcarcosa_la_LIBADD =

AM_CPPFLAGS += $(HMC_FLAG)
install-exec-hook:
$(SST_REGISTER_TOOL) SST_ELEMENT_SOURCE carcosa=$(abs_srcdir)
$(SST_REGISTER_TOOL) SST_ELEMENT_TESTS carcosa=$(abs_srcdir)/tests
9 changes: 9 additions & 0 deletions src/sst/elements/carcosa/configure.m4
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
dnl -*- Autoconf -*-
dnl vim:ft=config
dnl

AC_DEFUN([SST_carcosa_CONFIG], [
carcosa_happy="yes"

AS_IF([test "$carcosa_happy" = "yes"], [$1], [$2])
])
125 changes: 125 additions & 0 deletions src/sst/elements/carcosa/faultlogic/corruptMemFault.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Copyright 2009-2025 NTESS. Under the terms
// of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// Copyright (c) 2009-2025, NTESS
// All rights reserved.
//
// This file is part of the SST software package. For license
// information, see the LICENSE file in the top level directory of the
// distribution.

#include "sst/elements/carcosa/faultlogic/corruptMemFault.h"

using namespace SST::Carcosa;

CorruptMemFault::CorruptMemFault(Params& params, FaultInjectorBase* injector) : FaultBase(params, injector) {
#ifdef __SST_DEBUG_OUTPUT__
getSimulationDebug()->debug(CALL_INFO_LONG, 1, 0, "Fault type: Corrupt Memory Region\n");
#endif
// read in data regions
std::vector<std::string> regionVec;

// parameter format: {"regions": ["start_addr0, end_addr0", "start_addr1, end_addr1",...]}
params.find_array<std::string>("regions", regionVec);

// process entries into region
for (std::string region: regionVec) {
std::pair<uint64_t,uint64_t> region_pair = convertString(region);

// check validity
if (region_pair.first > region_pair.second) {
getSimulationOutput()->fatal(CALL_INFO_LONG, -1, "Invalid corruption region: [0x%zx, 0x%zx].\n",
region_pair.first, region_pair.second);
}

corruptionRegions_.push_back(region_pair);
#ifdef __SST_DEBUG_OUTPUT__
getSimulationDebug()->debug(CALL_INFO_LONG, 1, 0, "Inserted corruption region: [0x%zx, 0x%zx]\n",
region_pair.first, region_pair.second);
#endif
}
}

bool CorruptMemFault::faultLogic(Event*& ev) {
SST::MemHierarchy::MemEvent* mem_ev = convertMemEvent(ev);

Addr base_addr = mem_ev->getBaseAddr();
dataVec original_payload = mem_ev->getPayload();
dataVec new_payload(original_payload);
for (int r: regionsToUse_) {
auto& region = corruptionRegions_[r];
size_t payload_sz = mem_ev->getPayloadSize();
int32_t start = computeStartIndex(base_addr, payload_sz, region.first);
if (start < 0) {
getSimulationOutput()->fatal(CALL_INFO_LONG, -1, "No valid start index for corruption.\n");
}
int32_t end = computeEndIndex(base_addr, payload_sz, region.second);
if (end < 0) {
getSimulationOutput()->fatal(CALL_INFO_LONG, -1, "No valid start index for corruption.\n");
}
for (int i = start; i < end; i++) {
new_payload[i] = static_cast<uint8_t>(injector_->randUInt32(0,255));
}
}
setMemEventPayload(ev, new_payload);
return true;
}

std::pair<uint64_t,uint64_t> CorruptMemFault::convertString(std::string& region) {
std::stringstream ss(region);
uint64_t addr0, addr1;

ss >> std::hex >> addr0;
if (ss.peek() == ','){
ss.ignore();
}
ss >> std::hex >> addr1;

#ifdef __SST_DEBUG_OUTPUT__
getSimulationDebug()->debug(CALL_INFO_LONG, 2, 0, "Extracted region pair: [0x%zx, 0x%zx]\n",
addr0, addr1);
#endif
return make_pair(addr0, addr1);
}

int32_t CorruptMemFault::computeStartIndex(Addr base_addr, size_t payload_sz, Addr region_start) {
// start index is always the first byte of this payload in the corruption region
int payload_bytes = payload_sz / 8;
Addr addr = base_addr;
for (int i = 0; i < payload_bytes; i++, addr+=8) {
if (addr >= region_start) {
return addr - base_addr;
}
}
return -1;
}

int32_t CorruptMemFault::computeEndIndex(Addr base_addr, size_t payload_sz, Addr region_end) {
// end index is either the final addr's final byte, or the region end's addr's final byte
int payload_bytes = payload_sz / 8;
Addr addr = base_addr + ((payload_bytes - 1) * 8);
for (int i = payload_bytes; i >= 0; i--, addr-=8) {
if (addr <= region_end) {
return addr - base_addr + 8;
}
}
return -1;
}

std::vector<uint32_t>* CorruptMemFault::checkAddrUsage(Event*& ev) {
Addr base_addr = convertMemEvent(ev)->getBaseAddr();
for (int i = 0; i < corruptionRegions_.size(); i++) {
auto& region = corruptionRegions_[i];
// check if message contains ANY address in this region
int payload_bytes = convertMemEvent(ev)->getPayloadSize() / 8;
Addr addr = base_addr;
for (int j = 0; j < payload_bytes; addr+=8, j++) {
if ((addr >= region.first) && (addr <= region.second)) {
regionsToUse_.push_back(i);
break;
}
}
}
return &regionsToUse_;
}
72 changes: 72 additions & 0 deletions src/sst/elements/carcosa/faultlogic/corruptMemFault.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2009-2025 NTESS. Under the terms
// of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// Copyright (c) 2009-2025, NTESS
// All rights reserved.
//
// This file is part of the SST software package. For license
// information, see the LICENSE file in the top level directory of the
// distribution.

#ifndef SST_ELEMENTS_CARCOSA_CORRUPTMEMFAULT_H
#define SST_ELEMENTS_CARCOSA_CORRUPTMEMFAULT_H

#include "sst/elements/carcosa/faultlogic/faultBase.h"
#include "sst/core/rng/mersenne.h"
#include <vector>
#include <utility>
#include <string>
#include <sstream>

namespace SST::Carcosa {

typedef std::vector<uint8_t> dataVec;
typedef SST::MemHierarchy::Addr Addr;

/**
* This fault is intended to be placed on the input/output ports
* of memory components such as DRAM or HBM. Events that pass through
* it, and whose data addresses fall within the ranges set in this
* module's parameters, will have their payloads randomly altered
* to simulate corruption in the affected region of memory.
*/
class CorruptMemFault : public FaultBase
{
public:

CorruptMemFault(Params& params, FaultInjectorBase* injector);

CorruptMemFault() = default;
~CorruptMemFault() {}

/**
* 1. Read in event
* 2. Test if event is in specified region
* 3. Corrupt event payload if necessary
* 4. Replace payload
*/
bool faultLogic(Event*& ev) override;

std::vector<uint32_t>* checkAddrUsage(Event*& ev);
protected:

std::vector<std::pair<uint64_t, uint64_t>> corruptionRegions_;

std::vector<uint32_t> regionsToUse_;

std::pair<uint64_t,uint64_t> convertString(std::string& region);

int32_t computeStartIndex(Addr base_addr, size_t payload_sz, Addr region_start);
int32_t computeEndIndex(Addr base_addr, size_t payload_sz, Addr region_end);

void serialize_order(SST::Core::Serialization::serializer& ser) override {
FaultBase::serialize_order(ser);
SST_SER(corruptionRegions_);
SST_SER(regionsToUse_);
}
ImplementVirtualSerializable(CorruptMemFault)
}; // CorruptMemFault
} // namespace SST::Carcosa

#endif // SST_ELEMENTS_CARCOSA_CORRUPTMEMFAULT_H
77 changes: 77 additions & 0 deletions src/sst/elements/carcosa/faultlogic/faultBase.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2009-2025 NTESS. Under the terms
// of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// Copyright (c) 2009-2025, NTESS
// All rights reserved.
//
// This file is part of the SST software package. For license
// information, see the LICENSE file in the top level directory of the
// distribution.

#include "sst/elements/carcosa/faultlogic/faultBase.h"

using namespace SST::Carcosa;

FaultBase::FaultBase(Params& params, FaultInjectorBase* injector) : injector_(injector) {
//
}

bool FaultBase::faultLogic(Event*& ev) {
return true;
}

SST::Output* FaultBase::getSimulationOutput() {
return injector_->getOutput();
}

SST::Output* FaultBase::getSimulationDebug() {
return injector_->getDebug();
}

SST::MemHierarchy::MemEvent* FaultBase::convertMemEvent(Event*& ev) {
SST::MemHierarchy::MemEvent* mem_ev = dynamic_cast<SST::MemHierarchy::MemEvent*>(ev);

if (mem_ev == nullptr) {
getSimulationOutput()->fatal(CALL_INFO_LONG, -1, "Attempting to inject mem fault on a non-MemEvent type.\n");
}

#ifdef __SST_DEBUG_OUTPUT__
getSimulationDebug()->debug(CALL_INFO_LONG, 3, 0, "Intercepted event %zu/%d\n", mem_ev->getID().first, mem_ev->getID().second);
#endif
return mem_ev;
}

dataVec& FaultBase::getMemEventPayload(Event*& ev) {
return convertMemEvent(ev)->getPayload();
}

void FaultBase::setMemEventPayload(Event*& ev, dataVec newPayload) {
#ifdef __SST_DEBUG_OUTPUT__
getSimulationDebug()->debug(CALL_INFO_LONG, 2, 0, "Payload before replacement:\n");
SST::MemHierarchy::MemEvent* mem_ev = convertMemEvent(ev);
dataVec payload = getMemEventPayload(ev);
for (int i = 0; i < payload.size(); i+=8) {
getSimulationDebug()->debug(CALL_INFO_LONG, 2, 0, "\n0x%zx: [\t", mem_ev->getBaseAddr() + i);
for (int j = i; j < (i+8); j++) {
getSimulationDebug()->debug(CALL_INFO_LONG, 2, 0, "%d\t", payload[j]);
}
getSimulationDebug()->debug(CALL_INFO_LONG, 2, 0, "]\n");
}
#endif
convertMemEvent(ev)->setPayload(newPayload);

#ifdef __SST_DEBUG_OUTPUT__
getSimulationDebug()->debug(CALL_INFO_LONG, 2, 0, "Payload after replacement:\n");
mem_ev = convertMemEvent(ev);
payload = getMemEventPayload(ev);
for (int i = 0; i < payload.size(); i+=8) {
getSimulationDebug()->debug(CALL_INFO_LONG, 2, 0, "\n0x%zx: [\t", mem_ev->getBaseAddr() + i);
for (int j = i; j < (i+8); j++) {
getSimulationDebug()->debug(CALL_INFO_LONG, 2, 0, "%d\t", payload[j]);
}
getSimulationDebug()->debug(CALL_INFO_LONG, 2, 0, "]\n");
}
#endif
}

Loading
Loading