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
8 changes: 8 additions & 0 deletions libpimeval/src/libpimeval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,14 @@ pimShiftBitsLeft(PimObjId src, PimObjId dest, unsigned shiftAmount)
return ok ? PIM_OK : PIM_ERROR;
}

//! @brief Execute fused PIM APIs
PimStatus
pimFuse(PimProg prog)
{
bool ok = pimSim::get()->pimFuse(prog);
return ok ? PIM_OK : PIM_ERROR;
}

//! @brief BitSIMD-V: Read a row to SA
PimStatus
pimOpReadRowToSa(PimObjId src, unsigned ofst)
Expand Down
14 changes: 14 additions & 0 deletions libpimeval/src/libpimeval.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include <cstdint>
#include <cstdarg>
#include <vector>
#include <functional>

//! @brief PIM API return status
enum PimStatus {
Expand Down Expand Up @@ -197,6 +199,18 @@ PimStatus pimShiftBitsRight(PimObjId src, PimObjId dest, unsigned shiftAmount);
PimStatus pimShiftBitsLeft(PimObjId src, PimObjId dest, unsigned shiftAmount);


////////////////////////////////////////////////////////////////////////////////
// Experimental Feature: PIM API Fusion //
////////////////////////////////////////////////////////////////////////////////
struct PimProg {
template <typename... Args>
void add(PimStatus(*api)(Args...), Args... args) {
m_apis.push_back([=]() { return api(args...); });
}
std::vector<std::function<PimStatus()>> m_apis;
};
PimStatus pimFuse(PimProg prog);

////////////////////////////////////////////////////////////////////////////////
// Warning: Avoid using below customized APIs for functional simulation //
// Some are PIM architecture dependent, some are in progress //
Expand Down
42 changes: 42 additions & 0 deletions libpimeval/src/pimCmdFuse.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// File: pimCmdFuse.cpp
// PIMeval Simulator - PIM API Fusion
// Copyright (c) 2024 University of Virginia
// This file is licensed under the MIT License.
// See the LICENSE file in the root of this repository for more details.

#include "pimCmdFuse.h"
#include <cstdio>


//! @brief Pim CMD: PIM API Fusion
bool
pimCmdFuse::execute()
{
if (m_debugCmds) {
std::printf("PIM-Cmd: API Fusion\n");
}

// Functional simulation
// TODO: skip original updateStats
bool success = true;
for (auto& api : m_prog.m_apis) {
PimStatus status = api();
if (status != PIM_OK) {
success = false;
break;
}
}

// Analyze API fusion opportunities
success = success && updateStats();
return success;
}

//! @brief Pim CMD: PIM API Fusion - update stats
bool
pimCmdFuse::updateStats() const
{
// TODO: Parse m_prog and update stats
return true;
}

28 changes: 28 additions & 0 deletions libpimeval/src/pimCmdFuse.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// File: pimCmdFuse.h
// PIMeval Simulator - PIM API Fusion
// Copyright (c) 2024 University of Virginia
// This file is licensed under the MIT License.
// See the LICENSE file in the root of this repository for more details.

#ifndef LAVA_PIM_CMD_FUSE_H
#define LAVA_PIM_CMD_FUSE_H

#include "libpimeval.h"
#include "pimCmd.h"


//! @class pimCmdFuse
//! @brief Pim CMD: PIM API Fusion
class pimCmdFuse : public pimCmd
{
public:
pimCmdFuse(PimProg prog) : pimCmd(PimCmdEnum::NOOP), m_prog(prog) {}
virtual ~pimCmdFuse() {}
virtual bool execute() override;
virtual bool updateStats() const override;
private:
PimProg m_prog;
};

#endif

10 changes: 10 additions & 0 deletions libpimeval/src/pimSim.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "pimSim.h"
#include "pimCmd.h"
#include "pimCmdFuse.h"
#include "pimParamsDram.h"
#include "pimStats.h"
#include "pimUtils.h"
Expand Down Expand Up @@ -874,6 +875,15 @@ pimSim::pimShiftBitsLeft(PimObjId src, PimObjId dest, unsigned shiftAmount)
return m_device->executeCmd(std::move(cmd));
}

bool
pimSim::pimFuse(PimProg prog)
{
pimPerfMon perfMon("pimFuse");
if (!isValidDevice()) { return false; }
std::unique_ptr<pimCmd> cmd = std::make_unique<pimCmdFuse>(prog);
return m_device->executeCmd(std::move(cmd));
}

bool
pimSim::pimOpReadRowToSa(PimObjId objId, unsigned ofst)
{
Expand Down
3 changes: 3 additions & 0 deletions libpimeval/src/pimSim.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ class pimSim
bool pimShiftBitsRight(PimObjId src, PimObjId dest, unsigned shiftAmount);
bool pimShiftBitsLeft(PimObjId src, PimObjId dest, unsigned shiftAmount);

// PIM API Fusion
bool pimFuse(PimProg prog);

// BitSIMD-V micro ops
bool pimOpReadRowToSa(PimObjId src, unsigned ofst);
bool pimOpWriteSaToRow(PimObjId src, unsigned ofst);
Expand Down
19 changes: 19 additions & 0 deletions tests/test-api-fusion/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Makefile: Test PIM API Fusion
# Copyright (c) 2024 University of Virginia
# This file is licensed under the MIT License.
# See the LICENSE file in the root of this repository for more details.

PROJ_ROOT = ../..
include ${PROJ_ROOT}/Makefile.common

EXEC := test-api-fusion.out
SRC := test-api-fusion.cpp

debug perf dramsim3_integ: $(EXEC)

$(EXEC): $(SRC) $(DEPS)
$(CXX) $< $(CXXFLAGS) -o $@

clean:
rm -rf $(EXEC) *.dSYM

104 changes: 104 additions & 0 deletions tests/test-api-fusion/test-api-fusion.cpp
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another test case should be overwriting the PIMobject data and pushing it to pim prog and pim prog should not fuse those. Just a sanity check! :)

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Test: Test PIM API Fusion
// Copyright (c) 2024 University of Virginia
// This file is licensed under the MIT License.
// See the LICENSE file in the root of this repository for more details.

#include "libpimeval.h"
#include <iostream>
#include <vector>
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cstdlib>
#include <cstdio>


bool testFused(PimDeviceEnum deviceType)
{
// 1GB capacity
unsigned numRanks = 1;
unsigned numBankPerRank = 1;
unsigned numSubarrayPerBank = 8;
unsigned numRows = 1024;
unsigned numCols = 8192;

uint64_t numElements = 16 * 1024;

std::vector<int> src1(numElements);
std::vector<int> src2(numElements);
std::vector<int> dest1(numElements);
std::vector<int> dest2(numElements);
const int scalarVal = -123;

for (uint64_t i = 0; i < numElements; ++i) {
src1[i] = static_cast<int>(i);
src2[i] = static_cast<int>(i + 1);
}

PimStatus status = pimCreateDevice(deviceType, numRanks, numBankPerRank, numSubarrayPerBank, numRows, numCols);
assert(status == PIM_OK);

PimObjId objSrc1 = pimAlloc(PIM_ALLOC_AUTO, numElements, PIM_INT32);
PimObjId objSrc2 = pimAllocAssociated(objSrc1, PIM_INT32);
PimObjId objDest1 = pimAllocAssociated(objSrc1, PIM_INT32);
PimObjId objDest2 = pimAllocAssociated(objSrc1, PIM_INT32);
assert(objSrc1 != -1 && objSrc2 != -1 && objDest1 != -1 && objDest2 != -1);

// copy host to device
status = pimCopyHostToDevice((void*)src1.data(), objSrc1);
assert(status == PIM_OK);
status = pimCopyHostToDevice((void*)src2.data(), objSrc2);
assert(status == PIM_OK);


// Fused PIM APIs
PimProg prog;
prog.add(pimMulScalar, objSrc1, objDest1, static_cast<uint64_t>(scalarVal));
prog.add(pimAdd, objDest1, objSrc2, objDest1);
status = pimFuse(prog);
assert(status == PIM_OK);

// Direct APIs
status = pimScaledAdd(objSrc1, objSrc2, objDest2, static_cast<uint64_t>(scalarVal));
assert(status == PIM_OK);


status = pimCopyDeviceToHost(objDest1, (void*)dest1.data());
assert(status == PIM_OK);
status = pimCopyDeviceToHost(objDest2, (void*)dest2.data());
assert(status == PIM_OK);

bool ok = true;
for (uint64_t i = 0; i < numElements; ++i) {
if (dest1[i] != (src1[i] * scalarVal + src2[i]) || dest1[i] != dest2[i]) {
ok = false;
std::printf("Fused Test Error: src1 %d src2 %d dest1 %d dest2 %d\n", src1[i], src2[i], dest1[i], dest2[i]);
}
}

pimFree(objSrc1);
pimFree(objSrc2);
pimFree(objDest1);
pimFree(objDest2);

pimShowStats();
pimResetStats();
pimDeleteDevice();

std::cout << "Fused Test " << (ok ? "PASSED" : "FAILED") << std::endl;
return ok;
}

int main()
{
std::cout << "PIM Regression Test: PIM fused operations" << std::endl;

bool ok = true;
ok &= testFused(PIM_DEVICE_BITSIMD_V);
ok &= testFused(PIM_DEVICE_FULCRUM);
ok &= testFused(PIM_DEVICE_BANK_LEVEL);

std::cout << (ok ? "PASSED" : "FAILED") << std::endl;
return 0;
}