Skip to content
Closed
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
7 changes: 6 additions & 1 deletion src/sst/elements/balar/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ libbalar_la_SOURCES = \
util.cc \
util.h \
testcpu/balarTestCPU.cc \
testcpu/balarTestCPU.h
testcpu/balarTestCPU.h \
testcpu/doorbellTestCPU.cc \
testcpu/doorbellTestCPU.h

EXTRA_DIST = \
tests/refFiles/test_gpgpu_rodinia-2.0-backprop-1024.out \
Expand Down Expand Up @@ -78,6 +80,9 @@ EXTRA_DIST = \
tests/testBalar-doorbell.py \
tests/testBalar-malloc-free.py \
tests/testBalar-wide-packet.py \
tests/testDoorbellCPU-doorbell.py \
tests/testDoorbellCPU-malloc-free.py \
tests/testDoorbellCPU-wide-packet.py \
tests/balar_test_topology.py \
tests/traces/doorbell_malloc.trace \
tests/traces/malloc_free.trace \
Expand Down
900 changes: 900 additions & 0 deletions src/sst/elements/balar/testcpu/doorbellTestCPU.cc

Large diffs are not rendered by default.

169 changes: 169 additions & 0 deletions src/sst/elements/balar/testcpu/doorbellTestCPU.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright 2009-2026 NTESS. Under the terms
// of Contract DE-NA0003525 with NTESS, the U.S.
// Government retains certain rights in this software.
//
// Copyright (c) 2009-2026, 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 BALAR_DOORBELL_TEST_CPU_H
#define BALAR_DOORBELL_TEST_CPU_H

#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>

#include <sst/core/interfaces/stdMem.h>
#include <sst/core/component.h>
#include <sst/core/output.h>
#include "balar_packet.h"

#include "builtin_types.h"
#include "cuda.h"

#include <fstream>
#include <map>
#include <queue>
#include <string>
#include <vector>

namespace SST {
namespace BalarComponent {

class DoorbellTestCPU : public SST::Component {
public:
SST_ELI_REGISTER_COMPONENT(
DoorbellTestCPU,
"balar",
"DoorbellTestCPU",
SST_ELI_ELEMENT_VERSION(1, 0, 0),
"Doorbell-style test CPU: cache_link for scratch+flush, mmio_link for GPU doorbell",
COMPONENT_CATEGORY_PROCESSOR)

SST_ELI_DOCUMENT_PARAMS(
{ "verbose", "(uint) Verbosity", "1" },
{ "clock", "(UnitAlgebra/string) Clock frequency", "1GHz" },
{ "mmio_addr", "(uint) BalarMMIO base address (doorbell)", "0" },
{ "scratch_mem_addr", "(uint) Scratch region for CUDA call packets", "0" },
{ "cache_line_size", "(uint) Cache line size for scratch writes and flush", "64" },
{ "mode", "(string) trace or doorbell_only", "trace" },
{ "trace_file", "(string) CUDA API trace file (trace mode)", "cuda_calls.trace" },
{ "cuda_executable", "(string) CUDA binary for fatbin registration", "" },
{ "enable_memcpy_dump", "(bool) Dump D2H memcpy verification files", "false" })

SST_ELI_DOCUMENT_STATISTICS(
{ "cache_writes", "Scratch packet writes via cache_link", "count", 1 },
{ "mmio_writes", "Doorbell and MMIO writes via mmio_link", "count", 1 },
{ "flush_count", "FlushAddr(inv) issued before doorbell", "count", 1 },
{ "cuda_calls_completed", "Completed CUDA API round-trips", "count", 1 },
{ "total_memD2H_bytes", "Total D2H memcpy bytes checked", "count", 1 },
{ "correct_memD2H_bytes", "Correct D2H memcpy bytes", "count", 1 },
{ "correct_memD2H_ratio", "D2H byte correctness ratio", "count", 1 })

SST_ELI_DOCUMENT_SUBCOMPONENT_SLOTS(
{ "cache_link", "Cache path for scratch writes and return packet reads", "SST::Interfaces::StandardMem" },
{ "mmio_link", "MMIO path for doorbell and status reads", "SST::Interfaces::StandardMem" })

DoorbellTestCPU(SST::ComponentId_t id, SST::Params& params);
~DoorbellTestCPU() override;
void init(unsigned int phase) override;
void setup() override;
void finish() override;
void emergencyShutdown() override;

private:
enum class DriverMode { TRACE, DOORBELL_ONLY };

enum class IfacePath { CACHE, MMIO };

void handleCacheEvent(Interfaces::StandardMem::Request* req);
void handleMmioEvent(Interfaces::StandardMem::Request* req);
bool clockTic(SST::Cycle_t cycle);

void beginPacketIssue(const BalarCudaCallPacket_t& pack);
void sendNextScratchChunk();
void sendNextFlush();
void sendDoorbell();
void sendStartCudaRetRead();
void sendReadRetPacket(uint64_t ret_addr);
void beginCacheRead(uint64_t addr, size_t size, const std::string& type);
void sendNextCacheRead();
uint64_t scratchDataAddr() const;
size_t cacheChunkSize(uint64_t addr, size_t remaining) const;

void onCacheWriteResp(Interfaces::StandardMem::WriteResp* resp);
void onCacheWriteReq(Interfaces::StandardMem::Write* write);
void onCacheFlushResp(Interfaces::StandardMem::FlushResp* resp);
void onCacheReadResp(Interfaces::StandardMem::ReadResp* resp);
void onMmioWriteResp(Interfaces::StandardMem::WriteResp* resp);
void onMmioWriteReq(Interfaces::StandardMem::Write* write);
void onMmioReadResp(Interfaces::StandardMem::ReadResp* resp);

void completeCudaCall(const BalarCudaCallReturnPacket_t* ret_pack);
void finishCudaCall();
void tryFinishSimulation();

static void uint64ToData(uint64_t num, std::vector<uint8_t>* data);
static uint64_t dataToUInt64(std::vector<uint8_t>* data);

Output out;
DriverMode mode_;
uint64_t scratchMemAddr_;
uint64_t mmioAddr_;
uint64_t cacheLineSize_;
bool enable_memcpy_dump_;
std::string cuda_executable_;
// Gates the one-shot initial fatbin issue in DOORBELL_ONLY mode so the
// trigger doesn't depend on a magic clock-tick number.
bool issued_initial_packet_;
// NOTE: redundant with stat_cuda_calls_completed_->getCollectionCount();
// kept as a plain counter to avoid coupling control flow to the stats subsystem.
uint64_t calls_completed_;

Interfaces::StandardMem* cache_link_;
Interfaces::StandardMem* mmio_link_;

TimeConverter clockTC_;
Clock::HandlerBase* clock_handler_;

std::map<Interfaces::StandardMem::Request::id_t, std::pair<std::string, IfacePath>> requests_;

std::vector<uint8_t> packet_buffer_;
size_t scratch_bytes_remaining_;
size_t scratch_write_offset_;
size_t flushes_remaining_;
size_t flushes_outstanding_;
bool packet_issue_active_;
std::vector<uint8_t> scratch_append_payload_;
BalarCudaCallPacket_t active_packet_{};
BalarCudaCallReturnPacket_t return_packet_{};
uint64_t cache_read_addr_;
size_t cache_read_offset_;
std::string cache_read_type_;
std::vector<uint8_t> cache_read_buffer_;

class CudaAPITraceParser;
CudaAPITraceParser* trace_parser_;

class CacheHandlers;
class MmioHandlers;
CacheHandlers* cache_handlers_;
MmioHandlers* mmio_handlers_;

Statistic<uint64_t>* stat_cache_writes_;
Statistic<uint64_t>* stat_mmio_writes_;
Statistic<uint64_t>* stat_flush_count_;
Statistic<uint64_t>* stat_cuda_calls_completed_;
Statistic<uint64_t>* stat_total_memD2H_bytes_;
Statistic<uint64_t>* stat_correct_memD2H_bytes_;
Statistic<double>* stat_correct_memD2H_ratio_;
};

} // namespace BalarComponent
} // namespace SST

#endif // BALAR_DOORBELL_TEST_CPU_H
144 changes: 143 additions & 1 deletion src/sst/elements/balar/tests/balar_test_topology.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Shared SST topology builder for Balar contract tests.
Shared SST topology builders for Balar and Doorbell-pattern contract tests.
"""

import sst
Expand Down Expand Up @@ -100,3 +100,145 @@ def build_testcpu_router(balar_builder, cfg_file, balar_verbosity=0, dma_verbosi
sst.Link("link_mem").connect((mem_nic, "port", "1000ps"), (chiprtr, "port4", "1000ps"))

return cpu, balar_mmio_addr


def build_doorbell_topology(balar_builder, cfg_file, mode="trace", balar_verbosity=0, dma_verbosity=0):
"""
Doorbell-pattern: DoorbellTestCPU cache_link -> L1 -> coherent memory fabric,
and mmio_link -> routed MMIO fabric for balarMMIO doorbells.
"""
# NOTE: hard-coded high MMIO aperture so wide scratch packets cannot overflow
# into the MMIO region. Could derive from max packet size + alignment instead.
balar_mmio_addr = 0x10000000
dma_mmio_addr = balar_mmio_addr + 1024
_, balar_mmio_iface = balar_builder.build(
cfg_file, balar_mmio_addr, dma_mmio_addr, verbosity=balar_verbosity
)

dma = sst.Component("dmaEngine", "balar.dmaEngine")
dma.addParams({
"verbose": dma_verbosity,
"clock": CLOCK,
"mmio_addr": dma_mmio_addr,
"mmio_size": 512,
})
dma_mmio_if = dma.setSubComponent("mmio_iface", "memHierarchy.standardInterface")
dma_mem_if = dma.setSubComponent("mem_iface", "memHierarchy.standardInterface")

l1 = sst.Component("l1", "memHierarchy.Cache")
l1.addParams({
"cache_frequency": CLOCK,
"cache_size": "32KB",
"associativity": 4,
"access_latency_cycles": 2,
"L1": 1,
"coherence_protocol": "mesi",
"replacement_policy": "lru",
"cache_line_size": 64,
"addr_range_start": 0,
"addr_range_end": balar_mmio_addr - 1,
})
l1_cpu = l1.setSubComponent("highlink", "memHierarchy.MemLink")
l1_nic = l1.setSubComponent("lowlink", "memHierarchy.MemNIC")
l1_nic.addParams({
"group": CORE_GROUP,
"destinations": CORE_DST,
"network_bw": NETWORK_BW,
})

cpu = sst.Component("cpu", "balar.DoorbellTestCPU")
cpu.addParams({
"clock": CLOCK,
"verbose": balar_verbosity,
"scratch_mem_addr": 0,
"mmio_addr": balar_mmio_addr,
"cache_line_size": 64,
"mode": mode,
"enable_memcpy_dump": False,
})
cache_if = cpu.setSubComponent("cache_link", "memHierarchy.standardInterface")
cache_if.addParams(DEBUG_PARAMS)
mmio_if = cpu.setSubComponent("mmio_link", "memHierarchy.standardInterface")
mmio_if.addParams(DEBUG_PARAMS)
cpu_mmio_nic = mmio_if.setSubComponent("lowlink", "memHierarchy.MemNIC")
cpu_mmio_nic.addParams({
"group": CORE_GROUP,
"destinations": CORE_DST,
"network_bw": NETWORK_BW,
})

balar_mmio_nic = balar_mmio_iface.setSubComponent("lowlink", "memHierarchy.MemNIC")
balar_mmio_nic.addParams({
"group": MMIO_GROUP,
"sources": MMIO_SRC,
"destinations": MMIO_DST,
"network_bw": NETWORK_BW,
})

dma_mem_nic = dma_mem_if.setSubComponent("lowlink", "memHierarchy.MemNIC")
dma_mem_nic.addParams({
"group": CORE_GROUP,
"destinations": CORE_DST,
"network_bw": NETWORK_BW,
})

dma_mmio_nic = dma_mmio_if.setSubComponent("lowlink", "memHierarchy.MemNIC")
dma_mmio_nic.addParams({
"group": MEMORY_GROUP,
"sources": MEMORY_SRC,
"network_bw": NETWORK_BW,
})

chiprtr = sst.Component("doorbell_chiprtr", "merlin.hr_router")
chiprtr.addParams({
"xbar_bw": "1GB/s",
"id": "0",
"input_buf_size": "1KB",
"num_ports": "6",
"flit_size": "72B",
"output_buf_size": "1KB",
"link_bw": "1GB/s",
"topology": "merlin.singlerouter",
})
chiprtr.setSubComponent("topology", "merlin.singlerouter")

memctrl = sst.Component("memory", "memHierarchy.MemController")
memctrl.addParams({
"clock": "1GHz",
"addr_range_start": 0,
"addr_range_end": balar_mmio_addr - 1,
})
mem_be = memctrl.setSubComponent("backend", "memHierarchy.simpleMem")
mem_be.addParams({
"access_time": "100 ns",
"mem_size": "512MiB",
})
mem_hi = memctrl.setSubComponent("highlink", "memHierarchy.MemLink")

directory = sst.Component("directory", "memHierarchy.DirectoryController")
directory.addParams({
"clock": "1GHz",
"coherence_protocol": "MESI",
"cache_line_size": 64,
"entry_cache_size": 32768,
"mshr_num_entries": 16,
"addr_range_start": 0,
"addr_range_end": balar_mmio_addr - 1,
})
dir_nic = directory.setSubComponent("highlink", "memHierarchy.MemNIC")
dir_nic.addParams({
"group": MEMORY_GROUP,
"sources": MEMORY_SRC,
"network_bw": NETWORK_BW,
})

sst.Link("cpu_l1").connect((cache_if, "port", "1ns"), (l1_cpu, "port", "1ns"))
sst.Link("mem_bus").connect((mem_hi, "port", "1ns"), (directory, "lowlink", "1ns"))
sst.Link("doorbell_l1_rtr").connect((l1_nic, "port", "1ns"), (chiprtr, "port0", "1ns"))
sst.Link("doorbell_cpu_mmio_rtr").connect((cpu_mmio_nic, "port", "1ns"), (chiprtr, "port1", "1ns"))
sst.Link("doorbell_balar_mmio_rtr").connect((balar_mmio_nic, "port", "1ns"), (chiprtr, "port2", "1ns"))
sst.Link("doorbell_dma_mem_rtr").connect((dma_mem_nic, "port", "1ns"), (chiprtr, "port3", "1ns"))
sst.Link("doorbell_dma_mmio_rtr").connect((dma_mmio_nic, "port", "1ns"), (chiprtr, "port4", "1ns"))
sst.Link("doorbell_dir_rtr").connect((dir_nic, "port", "1ns"), (chiprtr, "port5", "1ns"))

return cpu, balar_mmio_addr
Loading
Loading