diff --git a/configs/supernetwork/SRNoC.py b/configs/supernetwork/SRNoC.py new file mode 100644 index 0000000000..86ffaa6e48 --- /dev/null +++ b/configs/supernetwork/SRNoC.py @@ -0,0 +1,364 @@ +# Copyright (c) 2025 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# 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; +# neither the name of the copyright holders 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 +# OWNER 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. + +import argparse +from enum import Enum as PyEnum + +import m5 +from m5.objects import * +from m5.params import * + + +class NetworkDelays(PyEnum): + CROSSPOINT_DELAY = 4.1 # picoseconds + MERGER_DELAY = 8.84 + SPLITTER_DELAY = 2.06 + CIRCUIT_VARIABILITY = 1.2 + VARIABILITY_COUNTING_NETWORK = 4.38 + CROSSPOINT_SETUP_TIME = 8.0 + CROSSPOINT_HOLD_TIME = 8.0 + + +class ComponentPower(PyEnum): + # Static power consumption in microwatts + SPLITTER_STATIC = 5.98 + MERGER_STATIC = 5.0 + CROSSPOINT_STATIC = 7.9 + COUNTING_NETWORK_STATIC = 66.82 + TFF_STATIC = 10.8 + + # Active power consumption in nanowatts + SPLITTER_ACTIVE = 83.2 + MERGER_ACTIVE = 69.6 + CROSSPOINT_ACTIVE = 60.7 + COUNTING_NETWORK_ACTIVE = 163.0 + TFF_ACTIVE = 105.6 + + +class ComponentJJ(PyEnum): + # Number of Josephson Junctions (JJs) + SPLITTER = 3 + MERGER = 5 + CROSSPOINT = 13 + COUNTING_NETWORK = 60 + TFF = 10 + + +def calculate_power_and_area(radix): + # Scale counting network power based on network radix + counting_network_ratio = (radix / 2) // 4.0 + print(f"Counting network ratio: {counting_network_ratio}") + counting_network_active_power = ( + ComponentPower.COUNTING_NETWORK_ACTIVE.value * counting_network_ratio + ) + counting_network_static_power = ( + ComponentPower.COUNTING_NETWORK_STATIC.value * counting_network_ratio + ) + counting_network_jj = int( + ComponentJJ.COUNTING_NETWORK.value * counting_network_ratio + ) + + # Calculate number of components based on network radix + r = radix // 2 + num_counting_networks = r + num_crosspoints = r * r + num_splitters = r * (r - 1) + num_mergers = r * (r - 1) + + # Calculate active power consumption + active_power = ( + num_counting_networks * counting_network_active_power + + num_crosspoints * ComponentPower.CROSSPOINT_ACTIVE.value + + num_splitters * ComponentPower.SPLITTER_ACTIVE.value + + num_mergers * ComponentPower.MERGER_ACTIVE.value + ) + + # Calculate static power consumption + static_power = ( + num_counting_networks * counting_network_static_power + + num_crosspoints * ComponentPower.CROSSPOINT_STATIC.value + + num_splitters * ComponentPower.SPLITTER_STATIC.value + + num_mergers * ComponentPower.MERGER_STATIC.value + ) + + # Convert power units + active_power *= 1e-9 # nanowatts to watts + static_power *= 1e-6 # microwatts to watts + total_power = active_power + static_power + + # Calculate total Josephson Junctions + total_jj = ( + num_counting_networks * counting_network_jj + + num_crosspoints * ComponentJJ.CROSSPOINT.value + + num_splitters * ComponentJJ.SPLITTER.value + + num_mergers * ComponentJJ.MERGER.value + ) + + # Log and store power and area statistics + print(f"Active power: {active_power:.6f} W") + print(f"Static power: {static_power:.6f} W") + print(f"Total power: {total_power:.6f} W") + print(f"Total JJ: {total_jj}") + + return { + "active_power": active_power, + "static_power": static_power, + "total_power": total_power, + "total_jj": total_jj, + } + + +def create_shared_parser(): + """ + Parser for arguments shared across all traffic modes. + """ + shared_parser = argparse.ArgumentParser(add_help=False) + shared_parser.add_argument( + "--frequencies-per-layer", + type=str, + nargs="+", + required=True, + help="Number of time slots per connection window", + ) + return shared_parser + + +def create_base_parser(): + parser = argparse.ArgumentParser(description="SuperNetwork Simulation") + parser.add_argument( + "--num-ports", type=int, default=10, help="Number of BufferedPorts" + ) + parser.add_argument( + "--maximum-values", type=int, default=0, help="Maximum values" + ) + parser.add_argument( + "--buffer-depth", + type=int, + default=1, + help="Buffer depth (number of values in the buffer)", + ) + parser.add_argument( + "--values-per-port-per-window", + type=int, + default=1, + help="values per port per window", + ) + + # add mutually exclusive group for active source selection + active_src_group = parser.add_mutually_exclusive_group() + active_src_group.add_argument( + "--active-src-count", + type=int, + default=-1, + help="Number of active sources in the network (-1 for all)", + ) + active_src_group.add_argument( + "--active-src-fraction", + type=float, + default=1.0, + help="Fraction of active sources in the network (default is 1 for all)", + ) + parser.add_argument( + "--active-src-sequence", + action="store_true", + help="If true, active sources are selected in a sequence; otherwise, randomly", + ) + return parser + + +def parse_arguments(): + shared_parser = create_shared_parser() + base_parser = create_base_parser() + subparsers = base_parser.add_subparsers(dest="traffic_mode", required=True) + + # random traffic + random_parser = subparsers.add_parser( + "random", parents=[shared_parser], help="Random traffic mode" + ) + + bit_complement_parser = subparsers.add_parser( + "bit-complement", + parents=[shared_parser], + help="Bit-complement traffic mode", + ) + + nearest_neighbor_parser = subparsers.add_parser( + "nearest-neighbor", + parents=[shared_parser], + help="Nearest-neighbor traffic mode", + ) + nearest_neighbor_parser.add_argument( + "--shuffle", + action="store_true", + help="Shuffle the destinations for nearest-neighbor mode", + ) + + # all-to-all traffic + all_to_all_parser = subparsers.add_parser( + "all-to-all", parents=[shared_parser], help="All-to-all traffic mode" + ) + all_to_all_parser.add_argument( + "--shuffle", + action="store_true", + help="Shuffle the destinations for all-to-all mode", + ) + + tornado_parser = subparsers.add_parser( + "tornado", parents=[shared_parser], help="Tornado traffic mode" + ) + + # hotspot traffic + hotspot_parser = subparsers.add_parser( + "hotspot", parents=[shared_parser], help="Hotspot traffic mode" + ) + hotspot_parser.add_argument( + "--hotspot-addr", type=int, required=True, help="Hotspot address" + ) + hotspot_parser.add_argument( + "--hotspot-fraction", + type=float, + required=True, + help="Fraction of hotspot traffic", + ) + + # file traffic + file_parser = subparsers.add_parser( + "file", parents=[shared_parser], help="File-based traffic mode" + ) + file_parser.add_argument( + "--file-path", type=str, required=True, help="Path to the traffic file" + ) + + return base_parser.parse_args() + + +def main(): + args = parse_arguments() + + # Create the root SimObject and system. + root = Root(full_system=False) + root.system = System() + + # Set up the clock and voltage domains. + root.system.clk_domain = SrcClockDomain() + root.system.clk_domain.clock = "1.4GHz" + root.system.clk_domain.voltage_domain = VoltageDomain() + + # Create the BufferedPorts. + num_ports = args.num_ports + buffered_ports = [BufferedPort() for _ in range(num_ports)] + + if args.traffic_mode != "file": + schedule_path = "" # Force schedule_path to be empty if not using file-based traffic mode. + else: + schedule_path = args.file_path + + # Create Layers + layers = [ + Layer( + buffered_ports=buffered_ports, + max_values=args.maximum_values, + schedule_path=schedule_path, + crosspoint_delay=NetworkDelays.CROSSPOINT_DELAY.value, + merger_delay=NetworkDelays.MERGER_DELAY.value, + splitter_delay=NetworkDelays.SPLITTER_DELAY.value, + circuit_variability=NetworkDelays.CIRCUIT_VARIABILITY.value, + variability_counting_network=NetworkDelays.VARIABILITY_COUNTING_NETWORK.value, + crosspoint_setup_time=NetworkDelays.CROSSPOINT_SETUP_TIME.value, + hold_time=NetworkDelays.CROSSPOINT_HOLD_TIME.value, + values_per_port_per_window=args.values_per_port_per_window, + buffer_depth=args.buffer_depth, + clk_domain=SrcClockDomain( + clock=frequency, + voltage_domain=VoltageDomain(), + ), + active_src_count=args.active_src_count, + active_src_frac=args.active_src_fraction, + active_src_seq=args.active_src_sequence, + ) + for frequency in args.frequencies_per_layer + ] + + # Create the SuperNetwork and add the layers. + super_network = SuperNetwork() + super_network.layers = layers + root.system.super_network = super_network + + # Print test configuration. + print("SRNoC Test Configuration") + print("==============================") + print(f"Number of BufferedPort: {num_ports}") + print(f"Frequencies per Layer: {args.frequencies_per_layer}") + print() + print("Power and Area Statistics") + print("==============================") + power_and_area = calculate_power_and_area(radix=(num_ports * 2)) + print() + + if args.maximum_values: + print(f"Maximum values: {args.maximum_values}") + if getattr(args, "file_path", None): + print(f"File Path: {args.file_path}") + print() + + # Instantiate the simulation. + m5.instantiate() + + # Set the traffic mode based on the chosen sub-command. + if args.traffic_mode == "hotspot": + for layer in layers: + layer.setHotspotTrafficMode( + args.hotspot_addr, args.hotspot_fraction + ) + elif args.traffic_mode == "all-to-all": + for layer in layers: + if args.shuffle: + layer.setShuffle() + layer.setAllToAllTrafficMode() + elif args.traffic_mode == "random": + for layer in layers: + layer.setRandomTrafficMode() + elif args.traffic_mode == "tornado": + for layer in layers: + layer.setTornadoTrafficMode() + elif args.traffic_mode == "bit-complement": + for layer in layers: + layer.setBitComplementTrafficMode() + elif args.traffic_mode == "nearest-neighbor": + for layer in layers: + if args.shuffle: + layer.setShuffle() + layer.setNearestNeighborTrafficMode() + else: # file mode selected. + for layer in layers: + layer.setRandomTrafficMode() + + exit_event = m5.simulate() + print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}") + + +if __name__ == "__m5_main__": + main() diff --git a/src/network/BufferedPort.py b/src/network/BufferedPort.py new file mode 100644 index 0000000000..12615839d4 --- /dev/null +++ b/src/network/BufferedPort.py @@ -0,0 +1,35 @@ +# Copyright (c) 2025 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# 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; +# neither the name of the copyright holders 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 +# OWNER 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. + +from m5.objects.ClockedObject import ClockedObject +from m5.params import * +from m5.proxy import * + + +class BufferedPort(ClockedObject): + type = "BufferedPort" + cxx_header = "network/buffered_port.hh" + cxx_class = "gem5::BufferedPort" diff --git a/src/network/Layer.py b/src/network/Layer.py new file mode 100644 index 0000000000..c4caea74de --- /dev/null +++ b/src/network/Layer.py @@ -0,0 +1,84 @@ +# Copyright (c) 2025 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# 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; +# neither the name of the copyright holders 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 +# OWNER 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. + +from m5.objects.ClockedObject import ClockedObject +from m5.params import * +from m5.util.pybind import PyBindMethod + + +class Layer(ClockedObject): + type = "Layer" + cxx_header = "network/layer.hh" + cxx_class = "gem5::Layer" + + # Vector of ports in the network + buffered_ports = VectorParam.BufferedPort("I/O ports in the network") + + # max_values: 0 means not provided + max_values = Param.Int( + -1, "Maximum number of values to schedule; -1 means infinite" + ) + + # schedule_path: empty string means not provided + schedule_path = Param.String( + "", "File path for schedule (empty means not provided)" + ) + + crosspoint_delay = Param.Float(-1, "Crosspoint delay in picoseconds") + merger_delay = Param.Float(-1, "Merger delay in picoseconds") + splitter_delay = Param.Float(-1, "Splitter delay in picoseconds") + circuit_variability = Param.Float(-1, "Circuit variability in picoseconds") + variability_counting_network = Param.Float( + -1, "Variability in counting network in picoseconds" + ) + crosspoint_setup_time = Param.Float( + -1, "Crosspoint setup time in picoseconds" + ) + hold_time = Param.Float(-1, "Hold time in picoseconds") + values_per_port_per_window = Param.Int(1, "Values per port per window") + buffer_depth = Param.Int( + 1, "Buffer depth (number of values in the buffer)" + ) + active_src_count = Param.Int( + -1, "Number of active sources in the network (default is -1 for all)" + ) + active_src_frac = Param.Float( + 1.0, "Fraction of active sources in the network (default is 1 for all)" + ) + active_src_seq = Param.Bool( + False, + "If true, active sources are selected in a sequence; otherwise, randomly", + ) + + cxx_exports = [ + PyBindMethod("setRandomTrafficMode"), + PyBindMethod("setHotspotTrafficMode"), + PyBindMethod("setAllToAllTrafficMode"), + PyBindMethod("setTornadoTrafficMode"), + PyBindMethod("setBitComplementTrafficMode"), + PyBindMethod("setNearestNeighborTrafficMode"), + PyBindMethod("setShuffle"), + ] diff --git a/src/network/SConscript b/src/network/SConscript new file mode 100644 index 0000000000..cee7b44a8e --- /dev/null +++ b/src/network/SConscript @@ -0,0 +1,15 @@ +Import("*") + +SimObject("BufferedPort.py", sim_objects=["BufferedPort"]) +SimObject("SuperNetwork.py", sim_objects=["SuperNetwork"]) +SimObject("Layer.py", sim_objects=["Layer"]) + +Source("buffered_port.cc") +Source("super_network.cc") +Source("layer.cc") +Source("network_scheduler.cc") + +DebugFlag("SuperNetwork") +DebugFlag("BufferedPort") +DebugFlag("NetworkScheduler") +DebugFlag("Layer") diff --git a/src/network/SuperNetwork.py b/src/network/SuperNetwork.py new file mode 100644 index 0000000000..9280a9bf90 --- /dev/null +++ b/src/network/SuperNetwork.py @@ -0,0 +1,37 @@ +# Copyright (c) 2025 The Regents of the University of California +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer; +# 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; +# neither the name of the copyright holders 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 +# OWNER 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. + +from m5.objects.ClockedObject import ClockedObject +from m5.params import * + + +class SuperNetwork(ClockedObject): + type = "SuperNetwork" + cxx_header = "network/super_network.hh" + cxx_class = "gem5::SuperNetwork" + + # Vector of layers in the network + layers = VectorParam.Layer("Layers in the network") diff --git a/src/network/buffered_port.cc b/src/network/buffered_port.cc new file mode 100644 index 0000000000..b8aa89872e --- /dev/null +++ b/src/network/buffered_port.cc @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2025 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + */ + +#include "network/buffered_port.hh" + +#include "debug/BufferedPort.hh" +#include "sim/sim_exit.hh" +#include "sim/stats.hh" +#include "sim/system.hh" + +namespace gem5 +{ + +// Constructor for BufferedPort class +// Initializes the port with default values +BufferedPort::BufferedPort(const BufferedPortParams& params) : + ClockedObject(params), + addr(0) // Initialize address to 0 +{ +} + +// Sets the address of the port +void +BufferedPort::setAddr(uint64_t addr) +{ + this->addr = addr; +} + +// Retrieves the address of the port +uint64_t +BufferedPort::getAddr() +{ + return addr; +} + +// Assigns a value to the port by pushing the dest address into queue +void +BufferedPort::assignValue(uint64_t dest_addr) +{ + valueQueue.emplace(ValueEntry{dest_addr, curTick()}); + DPRINTF(BufferedPort, + "BufferedPort %d assigned value " + "to destination %d\n", + addr, dest_addr + ); +} + +// Clear the queue of values +void +BufferedPort::clearQueue() +{ + while (!valueQueue.empty()) { + valueQueue.pop(); + } +} + +// Retrieves and removes the next value from the queue +// Returns 0 if no values are available +ValueEntry +BufferedPort::getNextValue() +{ + if (!valueQueue.empty()) { + // Get the next value and remove it from the queue + ValueEntry nextValue = valueQueue.front(); + valueQueue.pop(); + return nextValue; + } + return ValueEntry{0, 0}; // No value available +} + +// Checks if there are any values in the queue +bool +BufferedPort::hasValues() const +{ + return !valueQueue.empty(); +} + +// Increments the missed value count +void +BufferedPort::incrementMissedValues() +{ + missedValues++; + DPRINTF(BufferedPort, "BufferedPort %d missed values: %d\n", + addr, missedValues + ); +} + +// Handles receiving data by updating the last received values and stats +void +BufferedPort::receiveData(uint64_t received_data, uint64_t src_addr) +{ + DPRINTF(BufferedPort, "BufferedPort %d received data %d from source %d\n", + addr, received_data, src_addr); + + lastReceivedData = received_data; // Store the last received data + lastReceivedFrom = src_addr; // Store the source of the data +} + +// Returns the next value without removing it from the queue +// Returns -1 if no values are available +uint64_t +BufferedPort::peekNextValue() const +{ + if (!valueQueue.empty()) { + return valueQueue.front().dest; + } + return -1; // No value available +} + +} // namespace gem5 diff --git a/src/network/buffered_port.hh b/src/network/buffered_port.hh new file mode 100644 index 0000000000..6d4229632f --- /dev/null +++ b/src/network/buffered_port.hh @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2025 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + */ + +#ifndef __NETWORK_BUFFEREDPORT_HH__ +#define __NETWORK_BUFFEREDPORT_HH__ + +#include +#include + +#include "params/BufferedPort.hh" +#include "sim/clocked_object.hh" + +namespace gem5 +{ +struct ValueEntry +{ + uint64_t dest; + Tick enqueueTick; +}; +// The BufferedPort class represents a single node in the network +// responsible for storing, sending, and receiving values. +// It tracks its data, address, and maintains statistics for +// sent and received values. +class BufferedPort : public ClockedObject +{ + private: + // The address identifier of the port + uint64_t addr; + + // Queue to hold values (destination addresses) assigned to the port + std::queue valueQueue; + + // Variables to store the most recent received data and source addr + uint64_t lastReceivedData = 0; + uint64_t lastReceivedFrom = 0; + + uint64_t missedValues = 0; // Count of missed values + + public: + // Constructor: Initializes the BufferedPort with parameters + BufferedPort(const BufferedPortParams& params); + + // Sets the address of the port + void setAddr(uint64_t addr); + + // Retrieves the address of the port + uint64_t getAddr(); + + // Assigns a value to the queue with the given destination address + // The value is stored with the current tick as the enqueue time + void assignValue(uint64_t dest); + + // Clears the queue of values + void clearQueue(); + + // Receives data from another port + // Stores the received value and source + void receiveData(uint64_t received_data, uint64_t src_addr); + + // Retrieves and removes the next value from the queue + ValueEntry getNextValue(); + + // Checks if the port has any values in its queue + bool hasValues() const; + + // Getters for the last received data and source information + uint64_t getLastReceivedData() const { return lastReceivedData; } + uint64_t getLastReceivedFrom() const { return lastReceivedFrom; } + + // Getter for the size of the value queue + size_t queueSize() const { return valueQueue.size(); } + + uint64_t allToAllCursor = -1; // Cursor for all-to-all traffic mode + + void shuffleQueue() + { + if (valueQueue.size() < 2) return; + + std::vector tmp; + while (!valueQueue.empty()) { + tmp.push_back(valueQueue.front()); + valueQueue.pop(); + } + std::random_device rd; + std::mt19937 gen(rd()); + std::shuffle(tmp.begin(), tmp.end(), gen); + for (auto &e : tmp) valueQueue.push(e); + } + + + // Getter for missed values count + uint64_t getMissedValues() const { return missedValues; } + // Increments the missed values count + void incrementMissedValues(); + + // Peeks at the next value without removing it from the queue + uint64_t peekNextValue() const; +}; + +} // namespace gem5 + +#endif // __NETWORK_BUFFEREDPORT_HH__ diff --git a/src/network/enums.cc b/src/network/enums.cc new file mode 100644 index 0000000000..9b0a538f95 --- /dev/null +++ b/src/network/enums.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + */ + +#include "network/enums.hh" + +namespace gem5 +{ + +const char* TrafficModeNames[NUM_TRAFFIC_MODES] = { + "RANDOM", + "HOTSPOT", + "ALL_TO_ALL", + "TORNADO", + "BIT_COMPLEMENT", + "NEAREST_NEIGHBOR" +}; + +} // namespace gem5 diff --git a/src/network/enums.hh b/src/network/enums.hh new file mode 100644 index 0000000000..8201f690dc --- /dev/null +++ b/src/network/enums.hh @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + */ + +#ifndef __NETWORK_ENUMS_HH__ +#define __NETWORK_ENUMS_HH__ + +namespace gem5 +{ + +enum TrafficMode +{ + RANDOM, + HOTSPOT, + ALL_TO_ALL, + TORNADO, + BIT_COMPLEMENT, + NEAREST_NEIGHBOR, + NUM_TRAFFIC_MODES +}; +extern const char* TrafficModeNames[NUM_TRAFFIC_MODES]; +} // namespace gem5 + +#endif // __NETWORK_ENUMS_HH__ diff --git a/src/network/layer.cc b/src/network/layer.cc new file mode 100644 index 0000000000..168d075521 --- /dev/null +++ b/src/network/layer.cc @@ -0,0 +1,861 @@ +/* + * Copyright (c) 2025 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + */ + +#include "network/layer.hh" + +#include +#include +#include +#include +#include +#include + +#include "debug/Layer.hh" +#include "network/network_scheduler.hh" +#include "network/super_network.hh" +#include "sim/eventq.hh" +#include "sim/sim_exit.hh" +#include "sim/stats.hh" +#include "sim/system.hh" + +namespace gem5 { + +// Constructor for the Layer class +// Initializes the network with given parameters, sets up ports, +// and prepares for value scheduling +Layer::Layer(const LayerParams& params) : + ClockedObject(params), + maxValues(params.max_values), + schedulePath(params.schedule_path), + scheduler(params.max_values, + params.schedule_path, + params.buffered_ports + ), + crosspointDelay(params.crosspoint_delay), + mergerDelay(params.merger_delay), + splitterDelay(params.splitter_delay), + circuitVariability(params.circuit_variability), + variabilityCountingNetwork(params.variability_counting_network), + crosspointSetupTime(params.crosspoint_setup_time), + holdTime(params.hold_time), + valuesDelivered(0), + currentTimeSlotIndex(0), + valuesPerPortPerWindow(params.values_per_port_per_window), + // targetMMPS(params.target_mps), + isFinished(false), + fileMode(false), + shuffleEnabled(false), + bufferDepth(params.buffer_depth), + size(params.buffered_ports.size()), + activeSrcCount(params.active_src_count), + activeSrcFraction(params.active_src_frac), + activeSrcSeq(params.active_src_seq), + // Event for processing the next network event + nextNetworkEvent([this]{ processNextNetworkEvent(); }, + name() + ".nextNetworkEvent"), + stats(this) +{ + assert(params.crosspoint_delay >= 0); + assert(params.merger_delay >= 0); + assert(params.splitter_delay >= 0); + assert(params.circuit_variability >= 0); + assert(params.variability_counting_network >= 0); + assert(params.crosspoint_setup_time >= 0); + + // Initialize ports + initializeBufferedPorts(params.buffered_ports); + + // Initialize the network scheduler + std::queue> + schedule_queue = scheduler.initialize(); + + if (!schedule_queue.empty()) { + fileMode = true; + maxValues = schedule_queue.size(); + while (!schedule_queue.empty()) { + const auto& entry = schedule_queue.front(); + + uint64_t src_addr = entry.first; + uint64_t dest_addr = entry.second; + + BufferedPort* port = getBufferedPort(src_addr); + if (port != nullptr) { + port->assignValue(dest_addr); + DPRINTF(Layer, + "BufferedPort %d: addr=%d, value assigned to %d\n", + src_addr, port->getAddr(), dest_addr); + } + schedule_queue.pop(); + } + } + + // for active source selection + uint64_t want; + if (params.active_src_frac < 0.0 || + params.active_src_frac > 1.0) { + fatal("Layer %s: active_src_frac (%f) must be in [0, 1]!\n", + name(), params.active_src_frac); + } + if (params.active_src_count <= 0 && + params.active_src_count != -1.0) { + fatal("Layer %s: active_src_count (%d) must be > 0!\n", + name(), params.active_src_count); + } + if (params.active_src_count > 0) { + want = std::min(params.active_src_count, size); + } else { + want = std::ceil(params.active_src_frac * size); + } + if (want >= size) // all active ⇒ keep set empty + ; + else if (params.active_src_seq) { // sequential + for (uint64_t i = 0; i < want; ++i) + activeSrc.insert(i); + } else { // random but repeatable + std::vector ids(size); + std::iota(ids.begin(), ids.end(), 0); + std::mt19937 rng(42); // fixed seed for repeatability + std::shuffle(ids.begin(), ids.end(), rng); + for (uint64_t i = 0; i < want; ++i) + activeSrc.insert(ids[i]); + } + + // debug output for active sources + DPRINTF(Layer, "Active sources: "); + for (const auto& src : activeSrc) { + DPRINTF(Layer, "%lu ", src); + } +} + +// Initialize ports with unique addresses +void +Layer::initializeBufferedPorts(const std::vector& ports) +{ + // Skip if no ports are provided + if (ports.empty()) { + return; + } + + // Set network radix + setRadix(ports.size() * 2); + + // Populate ports with addresses + for (uint64_t i = 0; i < ports.size(); i++) { + BufferedPort* port = ports[i]; + port->setAddr(i); + + DPRINTF(Layer, "BufferedPort %d: addr=%d\n", + i, port->getAddr()); + + // Add the port to the network + addBufferedPort(port); + } +} + +// Compute key network parameters like time slot and connection window +void +Layer::computeTimingParameters() +{ + // Calculate and assign the time slot + assignTimeSlot(); + DPRINTF(Layer, "Time slot: %d ps\n", + getTimeSlot() + ); + + DPRINTF(Layer, + "Clock period: %lu\n", + clockPeriod() + ); + + if (getTimeSlot() <= 0) { + fatal("Layer %s: Time slot (%lu) must be greater than 0!\n", + name(), getTimeSlot()); + } + if (getTimeSlot() > clockPeriod()) { + fatal("Layer %s: Time slot (%lu) is greater than ", + "clock period (%lu)!\n", + name(), getTimeSlot(), clockPeriod()); + } + + // Calculate the number of time slots possible + // given the clock period and time slot duration + // Round up to the nearest whole number + rlTimeSlots = std::ceil( + static_cast(clockPeriod()) / getTimeSlot() + ); + + // We can only use a fraction of the time slots + maxValuesPerWindow = std::floor(rlTimeSlots - + (rlTimeSlots / std::exp(1.0))); + + DPRINTF(Layer, + "RL Time slots: %lu\n", + rlTimeSlots + ); + + DPRINTF(Layer, + "Max values per window: %lu\n", + maxValuesPerWindow * size + ); + + // Calculate and assign the connection window + setConnectionWindow(rlTimeSlots * getTimeSlot()); + DPRINTF(Layer, "Connection window: %d ps\n", + getConnectionWindow() + ); + + // if (targetMMPS >= 0) { + // double values_this_window = + // static_cast(targetMMPS) * 1e6 * + // (static_cast(getConnectionWindow()) / 1e12); + + // DPRINTF(Layer, + // "Values this window: %f\n", + // values_this_window + // ); + + // // spread them evenly across ports, round *up* + // valuesPerPortPerWindow = + // std::ceil(values_this_window / static_cast(size)); + + // DPRINTF(Layer, + // "Value per port per window: %lu\n", + // valuesPerPortPerWindow + // ); + + // // safety: never let it exceed the hard structural cap + // valuesPerPortPerWindow = + // std::min(valuesPerPortPerWindow, + // maxValuesPerWindow * size); + // DPRINTF(Layer, + // "Target MMPS: %f\n", + // targetMMPS + // ); + // DPRINTF(Layer, + // "Values per port per window: %lu\n", + // valuesPerPortPerWindow + // ); + // if (valuesPerPortPerWindow == 0) { + // fatal("Layer %s: Target MMPS (%f) is too low!\n", + // name(), targetMMPS); + // } + // if (valuesPerPortPerWindow > (maxValuesPerWindow * size)) { + // warn("Layer %s: Target MMPS (%f) is too high!\n", + // name(), targetMMPS); + // warn("Layer %s: Setting values per port per \ + // window to %lu\n", + // name(),(maxValuesPerWindow * size)); + // } + // } + + if (valuesPerPortPerWindow > maxValues) { + fatal("Values per port per window (%lu) \ + exceeds max values (%lu)\n", + name(), valuesPerPortPerWindow, maxValues); + } + +} + +// Calculate the time slot based on network component delays +void +Layer::assignTimeSlot() +{ + // Adjust setup time considering circuit variability + double se_adjusted = std::max(0.0, + crosspointSetupTime - circuitVariability + ); + + // Calculate time slot considering delays of various network components + double calculated_time_slot = std::ceil(circuitVariability * ( + crosspointDelay + se_adjusted + + splitterDelay * (radix - 1) + + mergerDelay * (radix - 1) + + variabilityCountingNetwork + ) + crosspointSetupTime); + + // Round up the calculated time slot + this->timeSlot = calculated_time_slot; +} + +// Add a port to the network's port collection +void +Layer::addBufferedPort(BufferedPort* port) +{ + bufferedPorts.push_back(port); + uint64_t addr = port->getAddr(); + bufferedPortsMap[addr] = port; +} + +// Retrieve a port by its address +BufferedPort* +Layer::getBufferedPort(uint64_t addr) +{ + auto it = bufferedPortsMap.find(addr); + return (it != bufferedPortsMap.end()) ? it->second : nullptr; +} + +// Process the next network event in the simulation +void +Layer::processNextNetworkEvent() +{ + if (isFinished) { + return; + } + uint64_t values_processed_this_window = 0; + + // Build a static schedule for the current time slot + std::unordered_map static_schedule = + buildStaticSchedule(); + + // Process values according to the static schedule + // Add hold time before processing values + schedule(new EventFunctionWrapper( + [this, static_schedule, values_processed_this_window]() mutable { + processValues(static_schedule, values_processed_this_window); + }, + "processValuesEvent"), + curTick() + holdTime); + + stats.totalWindowsUsed++; + + // Advance the time slot for the next event + currentTimeSlotIndex++; + + // Schedule next network event. + // In infinite mode (maxValues == -1) we always schedule the next event. + if (maxValues == static_cast(-1) + || valuesDelivered < maxValues) { + scheduleNextNetworkEvent(curTick() + + ((connectionWindow))); + } +} + +uint64_t +Layer::fillQueue(BufferedPort* port, TrafficMode mode) +{ + int count = 0; + while (isBuffered() ? port->queueSize() < bufferDepth : count < 1) { + count++; + uint64_t src = port->getAddr(); + uint64_t dest = 0; + + switch (mode) { + case TrafficMode::RANDOM: + dest = scheduler.generateRandomValue(src); + DPRINTF(Layer, + "BufferedPort %lu: generated random value for %lu\n", + src, dest + ); + break; + + case TrafficMode::HOTSPOT: + dest = scheduler.generateHotspotValue(src, hotspotAddr, + hotspotFraction); + DPRINTF(Layer, + "BufferedPort %lu: generated hotspot value for %lu\n", + src, dest + ); + break; + + case TrafficMode::BIT_COMPLEMENT: + dest = scheduler.generateBitComplementValue(src); + DPRINTF(Layer, + "BufferedPort %lu: generated bit complement value \ + for %lu\n", + src, dest + ); + break; + + case TrafficMode::TORNADO: + dest = scheduler.generateTornadoValue(src); + DPRINTF(Layer, + "BufferedPort %lu: generated tornado value for %lu\n", + src, dest + ); + break; + + case TrafficMode::NEAREST_NEIGHBOR: { + /* pick left or right at random so we don’t exceed + injectionsPerPortPerWindow == 1 unintentionally */ + uint64_t neighbor = + (random() & 1) ? (src + 1) % size + : (src + size - 1) % size; + dest = neighbor; + DPRINTF(Layer, + "BufferedPort %lu: generated nearest-neighbor value \ + for %lu\n", + src, dest + ); + break; + } + + case TrafficMode::ALL_TO_ALL: { + /* cycle through the permutation instead of enqueuing them all */ + uint64_t next = + (port->allToAllCursor + 1) % size; // store cursor in port + port->allToAllCursor = next; + dest = next; + DPRINTF(Layer, + "BufferedPort %lu: generated all-to-all value for %lu\n", + src, dest + ); + break; + } + + default: + fatal("unknown traffic mode"); + } + port->assignValue(dest); + } + + // if shuffleEnabled is true, shuffle the queue + if (shuffleEnabled) { + port->shuffleQueue(); + DPRINTF(Layer, + "BufferedPort %lu: shuffled queue\n", + port->getAddr() + ); + } + + return port->peekNextValue(); +} + + +// Build a static schedule for value transmission in the current time slot +std::unordered_map +Layer::buildStaticSchedule() +{ + std::unordered_map static_schedule; + + // Determine allowed destination for each port + for (BufferedPort* port : bufferedPorts) { + uint64_t src_addr = port->getAddr(); + uint64_t allowed_dest = + (src_addr + currentTimeSlotIndex) % bufferedPorts.size(); + static_schedule[src_addr] = allowed_dest; + + DPRINTF(Layer, + "Window %lu: allowed transmission from BufferedPort %lu to %lu\n", + currentTimeSlotIndex, src_addr, allowed_dest + ); + } + + return static_schedule; +} + +// Process values according to the static schedule +bool +Layer::processValues( + const std::unordered_map& static_schedule, + uint64_t& values_processed_this_window) +{ + Tick payload_specific_delay = 0; + // Determine if we are in infinite mode + bool infinite_mode = (maxValues == static_cast(-1)); + + // Iterate through all ports + for (BufferedPort* port : bufferedPorts) { + if (!isSrcActive(port->getAddr())) + continue; + std::vector used_payloads; + if (values_processed_this_window >= (maxValuesPerWindow * size)) { + DPRINTF(Layer, + "Window %lu: reached max values (%lu), stopping.\n", + currentTimeSlotIndex, values_processed_this_window + ); + break; + } + if (valuesPerPortPerWindow > rlTimeSlots) { + warn("Layer %s: values per port per window (%lu) " + "exceeds RL time slots (%lu)\n", + name(), valuesPerPortPerWindow, rlTimeSlots); + warn("Layer %s: setting values per port per window to %lu\n", + name(), rlTimeSlots); + valuesPerPortPerWindow = rlTimeSlots; + } + // Check if we've already reached the maximum values + if (valuesDelivered >= maxValues && !infinite_mode) { + break; // Exit the loop immediately if we've reached max values + } + + uint64_t src_addr = port->getAddr(); + uint64_t allowed_dest = static_schedule.at(src_addr); + + uint64_t value_dest = -1; + // Check if there's already a value in the buffer first + if (port->hasValues() && isBuffered()) { + // Peek the next value from the port's queue + value_dest = port->peekNextValue(); + } else { + if (!fileMode) { + value_dest = fillQueue(port, trafficMode); + } + // if (trafficMode == TrafficMode::RANDOM) { + // // Generate a random value destination + // value_dest = scheduler.generateRandomValue(src_addr); + // } else if (trafficMode == TrafficMode::HOTSPOT) { + // // Use the static schedule for the current time slot + // value_dest = scheduler.generateHotspotValue( + // src_addr, hotspotAddr, hotspotFraction + // ); + // } else if (trafficMode == TrafficMode::BIT_COMPLEMENT) { + // // Generate a bit complement value + // value_dest = scheduler.generateBitComplementValue( + // src_addr + // ); + // } else if (trafficMode == TrafficMode::NEAREST_NEIGHBOR) { + // // only generate once per port + // if (!port->hasValues()) { + // // compute wrap‑around neighbors + // uint64_t left = (src_addr + size - 1) % size; + // uint64_t right = (src_addr + 1) % size; + + // // pack them into a small vector + // std::vector neighbors = { left, right }; + + // // optionally randomize order + // if (shuffleEnabled) { + // std::random_device rd; + // std::mt19937 g(rd()); + // std::shuffle(neighbors.begin(), + // neighbors.end(), g + // ); + // } + + // // enqueue neighbor values + // for (auto dest : neighbors) { + // port->assignValue(dest); + // DPRINTF(Layer, + // "BufferedPort %lu: enqueued nearest-neighbor " + // "value for destination %lu\n", + // src_addr, dest + // ); + // } + // } + // value_dest = port->peekNextValue(); + // DPRINTF(Layer, + // "BufferedPort %lu: nearest-neighbor value for %lu\n", + // src_addr, value_dest + // ); + // } else if (trafficMode == TrafficMode::ALL_TO_ALL) { + // // Check if the port already has queued destinations. + // if (!port->hasValues()) { + // // Create a vector to hold all destination indices. + // std::vector destinations; + // destinations.reserve(size); + // for (uint64_t dest = 0; dest < size; dest++) { + // destinations.push_back(dest); + // } + + // // Conditionally shuffle the vector + // if (shuffleEnabled) { + // // Create a random number generator. + // std::random_device rd; + // std::mt19937 g(rd()); + // // Shuffle the destinations. + // std::shuffle(destinations.begin(), + // destinations.end(), g + // ); + // } + + // // Enqueue each destination from the vector. + // // vector can be shuffled or not + // for (auto dest : destinations) { + // port->assignValue(dest); + // DPRINTF(Layer, + // "BufferedPort %lu: enqueued all-to-all " + // "value for destination %lu\n", + // src_addr, dest + // ); + // } + // } + // // Peek the next destination from the port's queue. + // value_dest = port->peekNextValue(); + // DPRINTF(Layer, + // "BufferedPort %lu: all-to-all value for %lu\n", + // src_addr, value_dest + // ); + // } else if (trafficMode == TrafficMode::TORNADO) { + // // Generate a tornado value + // value_dest = scheduler.generateTornadoValue(src_addr); + // } else { + // // Handle unknown traffic mode + // fatal("Unknown traffic mode: %d\n", trafficMode); + // } + // DPRINTF(Layer, + // "BufferedPort %lu: generated value for %lu\n", + // src_addr, value_dest + // ); + // + // assert(value_dest != -1); + // } else { + // // In file mode, don't generate a new value destination. + // // Optionally, log that no new value was generated. + // DPRINTF(Layer, + // "BufferedPort %lu: file mode active," + // "skipping value generation\n", + // src_addr + // ); + // } + } + stats.totalValuesAttempted++; + // Check if value can be sent in the current time slot + if (value_dest == allowed_dest) { + // Remove the value if it was from the buffer + Tick enqueue_tick = 0; + if (port->hasValues()) { + auto entry = port->getNextValue(); + value_dest = entry.dest; + enqueue_tick = entry.enqueueTick; + } else { + enqueue_tick = curTick(); + } + + // We can clear out the buffer + if (isBuffered()) { + port->clearQueue(); + } + + if (valuesPerPortPerWindow > rlTimeSlots) { + // If the number of values per port per window + // is greater than the number of time slots, + // we need to generate unique payloads + warn("Layer %s: valuesPerPortPerWindow (%lu) is greater than " + "rlTimeSlots (%lu), using rlTimeSlots instead\n", + name(), valuesPerPortPerWindow, rlTimeSlots); + valuesPerPortPerWindow = rlTimeSlots; + } + + uint64_t payload; + used_payloads.reserve(valuesPerPortPerWindow); + while (used_payloads.size() < valuesPerPortPerWindow) { + uint64_t p = scheduler.generateRandomPayload(rlTimeSlots); + if (std::find(used_payloads.begin(), + used_payloads.end(), + p) + == used_payloads.end()) { + used_payloads.push_back(p); + } + } + + // Sort the payloads in ascending order + std::sort(used_payloads.begin(), used_payloads.end()); + + // print the payloads + DPRINTF(Layer, + "BufferedPort %lu: payloads for %lu: ", + src_addr, value_dest + ); + for (auto p : used_payloads) { + DPRINTF(Layer, "%lu ", p); + } + + for (auto p : used_payloads) { + // Calculate precise delivery time + // within the connection window + // Use the payload value -> RACE LOGIC + // - Payload: (payload + 1) time slots + // - Splitter delay: + // * 1 stage if dest == 0 + // * (dest + 1) stages if dest < size - 1 + // * dest stages if dest == size - 1 + // - Merger delay: + // * (size - 1) stages unless src == size - 1, + // in which case it's 1 stage + // - Crosspoint delay is constant + payload_specific_delay = + ((p + 1) * getTimeSlot()) + + splitterDelay * ( + (value_dest == 0) ? 1 : + (value_dest < size - 1) ? (value_dest + 1) : + value_dest + ) + + mergerDelay * ( + (src_addr == size - 1) ? 1 : + (size - 1) + ) + + crosspointDelay + + variabilityCountingNetwork; + + DPRINTF(Layer, + "Processing value: src=%lu, dest=%lu, \ + data=%lu, specific delay=%lu ps\n", + src_addr, allowed_dest, p, payload_specific_delay + ); + + stats.totalValuesProcessed++; + valuesDelivered++; + values_processed_this_window++; + + + // Schedule value delivery with payload-specific timing + schedule(new EventFunctionWrapper([this, + src_addr, allowed_dest, p, enqueue_tick]() { + deliverValue(src_addr, allowed_dest, p, enqueue_tick); + }, "deliverValueEvent"), curTick() + payload_specific_delay); + + // Check if we've reached max values after processing this one + // If not in infinite mode, check for termination condition. + if (!infinite_mode && valuesDelivered >= maxValues) { + break; + } + } + } else if (!fileMode) { + // Value not allowed in the current time slot + if (isBuffered()) { + // If in buffered mode + // Assign the value to the buffer + port->assignValue(value_dest); + } + // Increment missed values for the BufferedPort + port->incrementMissedValues(); + stats.missedValuesPerBufferedPort.sample( + port->getMissedValues() + ); + DPRINTF(Layer, + "BufferedPort %lu: value for %lu not " + "scheduled (allowed: %lu)\n", + src_addr, value_dest, allowed_dest + ); + DPRINTF(Layer, + "BufferedPort %lu: value for %lu assigned to buffer\n", + src_addr, value_dest + ); + } + } + + stats.pktsPerWindow.sample(values_processed_this_window); + + // Only exit simulation if not in infinite mode + if (!infinite_mode && valuesDelivered >= maxValues) { + DPRINTF(Layer, + "All values processed in window %lu\n", + currentTimeSlotIndex + ); + DPRINTF(Layer, + "Payload specific delay: %lu\n", + payload_specific_delay + ); + isFinished = true; + if (superNetwork != nullptr) { + // Schedule the notification after the delay + schedule(new EventFunctionWrapper([this]() { + superNetwork->notifyLayerFinished(this); + }, "layerFinishedEvent"), curTick() + payload_specific_delay); + } + } + + return infinite_mode ? true : (valuesDelivered < maxValues); +} + +// Deliver a value to its destination port +void +Layer::deliverValue(uint64_t src_addr, + uint64_t dest_addr, uint64_t payload, + Tick enqueue_tick) +{ + Tick total_latency = curTick() - enqueue_tick; + DPRINTF(Layer, + "Value delivery: src=%lu, dest=%lu, data=%lu, latency=%lu\n", + src_addr, dest_addr, payload, total_latency + ); + // Find the destination port + BufferedPort* dest_port = getBufferedPort(dest_addr); + if (dest_port != nullptr) { + // Receive data at the destination port + dest_port->receiveData(payload, src_addr); + stats.valueLatency.sample(total_latency); + DPRINTF(Layer, + "Value delivered: src=%lu, dest=%lu, data=%lu\n", + src_addr, dest_addr, payload + ); + } else { + DPRINTF(Layer, + "Error: Destination port %lu not found\n", + dest_addr + ); + } +} + +// Schedule the next network event +void +Layer::scheduleNextNetworkEvent(Tick when) +{ + // Schedule only if not already scheduled + if (!nextNetworkEvent.scheduled()) { + schedule(nextNetworkEvent, when); + } +} + +// Constructor for Layer statistics +Layer::LayerStats::LayerStats( + Layer* layer + ) : statistics::Group(layer), + parentLayer(layer), + ADD_STAT(totalValuesProcessed, statistics::units::Count::get(), + "Total values processed"), + ADD_STAT(totalWindowsUsed, statistics::units::Count::get(), + "Number of connection windows used"), + ADD_STAT(pktsPerWindow, statistics::units::Count::get(), + "Distribution of values per window"), + ADD_STAT(missedValuesPerBufferedPort, statistics::units::Count::get(), + "Distribution of missed values per BufferedPort"), + ADD_STAT(valueLatency, statistics::units::Count::get(), + "Distribution of value latency (ps)"), + ADD_STAT(totalValuesAttempted, statistics::units::Count::get(), + "Total values attempted to be sent") +{ +} + +// Register statistics for detailed tracking and reporting +void +Layer::LayerStats::regStats() +{ + using namespace statistics; + + // Configure statistics with names, descriptions, and formatting + + totalValuesProcessed.name("totalValuesProcessed") + .desc("Total number of values processed"); + + totalWindowsUsed.name("totalWindowsUsed") + .desc("Number of connection windows used"); + + totalValuesAttempted.name("totalValuesAttempted") + .desc("Total values attempted to be sent"); + + pktsPerWindow.init(parentLayer->size + 1); + + missedValuesPerBufferedPort.init(64); + + valueLatency.init(64) + .name("valueLatency") + .desc("End-to-end latency (ps) = buffer delay + network"); +} + +} // namespace gem5 diff --git a/src/network/layer.hh b/src/network/layer.hh new file mode 100644 index 0000000000..e4cd51a1a2 --- /dev/null +++ b/src/network/layer.hh @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2025 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + */ + +#ifndef __NETWORK_Layer_HH__ +#define __NETWORK_Layer_HH__ + +#include +#include +#include +#include + +#include "base/statistics.hh" +#include "base/stats/group.hh" +#include "network/buffered_port.hh" +#include "network/enums.hh" +#include "network/layer.hh" +#include "network/network_scheduler.hh" +#include "params/Layer.hh" +#include "sim/clocked_object.hh" +#include "sim/eventq.hh" + +namespace gem5 +{ + +// Forward declaration of the SuperNetwork class +class SuperNetwork; + +class Layer : public ClockedObject +{ +private: + std::vector bufferedPorts; // all ports in the network + std::unordered_map bufferedPortsMap; + std::unordered_set activeSrc; // Set of active source addresses + + // network delay parameters + double crosspointDelay; + double mergerDelay; + double splitterDelay; + double circuitVariability; + double variabilityCountingNetwork; + double crosspointSetupTime; + double holdTime; + + // Network configuration parameters + uint64_t maxValuesPerWindow; // Maximum values per window + uint64_t radix; // Radix for the network, used in the topology + uint64_t rlTimeSlots; // Number of time slots per connection win. + uint64_t timeSlot; // Time slot for scheduling values + uint64_t connectionWindow; // Connection window + uint64_t currentTimeSlotIndex; // Current index for the time slot + int maxValues; // Maximum number of values, -1 means no limit + int valuesDelivered; // Number of value deliveries + uint32_t valuesPerPortPerWindow; // Values per port per window + int bufferDepth; // Depth of the buffer + int activeSrcCount; // Count of active source addresses + double activeSrcFraction; // Fraction of active source addresses + bool activeSrcSeq; // Flag for sequential active source addresses + int size; // Size of the network + bool isFinished; // Flag to indicate if the layer has finished + bool fileMode; // Flag to indicate if a file is used for scheduling + bool shuffleEnabled; // Flag to indicate if shuffling is enabled + std::string schedulePath; // Path to the schedule file + NetworkScheduler scheduler; // Scheduler for the network + SuperNetwork* superNetwork; // Pointer to the super network + TrafficMode trafficMode; // Traffic mode for the network + + // Hotspot parameters + uint64_t hotspotAddr; // Address of the hotspot + double hotspotFraction; // Fraction of values targeting the hotspot + + // Initialization methods to set up buffered ports + void initializeBufferedPorts( + const std::vector& buffered_ports + ); + + // Method to calculate the time slot based on network parameters + void assignTimeSlot(); + + // Methods for processing values + // Builds a static schedule for the current time slot + std::unordered_map buildStaticSchedule(); + bool processValues( + const std::unordered_map& static_schedule, + uint64_t& values_processed_this_window + ); + // Method for delivering a value to its destination + void deliverValue(uint64_t src_addr, + uint64_t dest_addr, uint64_t payload, + Tick enqueue_tick + ); + + // Struct to hold statistics related to the Layer + struct LayerStats: public statistics::Group + { + // pointer to the Layer instance + Layer *parentLayer; + + // Statistics for round-robin scheduling + + // Total number of values processed + statistics::Scalar totalValuesProcessed; + // Number of scheduling windows used + statistics::Scalar totalWindowsUsed; + // Number of values attempted to be sent + statistics::Scalar totalValuesAttempted; + // Distribution of values processed per time window + statistics::Histogram pktsPerWindow; + // Distribution of missed values per BufferedPort + statistics::Histogram missedValuesPerBufferedPort; + // Value latency distribution + statistics::Histogram valueLatency; + + // Constructor that links stats to the Layer instance + LayerStats(Layer* layer); + + // Registers the statistics with the simulator + void regStats() override; + }; + + LayerStats stats; + EventFunctionWrapper nextNetworkEvent; + void processNextNetworkEvent(); // Processes the next network event + +public: + // Constructor for initializing a Layer with parameters + Layer(const LayerParams& params); + + // Methods for computing layer timing parameters + void computeTimingParameters(); + + // Method to fill the queue of a port with values + uint64_t fillQueue(BufferedPort* port, TrafficMode mode); + + // Methods for adding and retrieving ports in the network + void addBufferedPort(BufferedPort* buffered_port); + BufferedPort* getBufferedPort(uint64_t addr); + + // Methods for getting certain network parameters + uint64_t getMaximumValuesPerWindow() const + { + return maxValuesPerWindow; + } + + uint64_t getRLTimeSlots() const + { + return rlTimeSlots; + } + + // Methods for assigning and retrieving radix, + // time slot, and connection window values + void setRadix(uint64_t radix) { this->radix = radix; } + uint64_t getRadix() const { return radix; } + + uint64_t getTimeSlot() const { return timeSlot; } + void setTimeSlot(uint64_t timeSlot) { this->timeSlot = timeSlot; } + + void setConnectionWindow(uint64_t window) { connectionWindow = window; } + int getConnectionWindow() const { return connectionWindow; } + + void scheduleNextNetworkEvent(Tick when); // Schedules the next event + + void registerSuperNetwork(SuperNetwork* superNetwork) + { + this->superNetwork = superNetwork; + } + SuperNetwork* getSuperNetwork() const { return superNetwork; } + + bool hasFinished() const { return isFinished; } + + // setters for TrafficMode + void setRandomTrafficMode() + { + trafficMode = TrafficMode::RANDOM; + } + + void setAllToAllTrafficMode() + { + trafficMode = TrafficMode::ALL_TO_ALL; + } + + void setTornadoTrafficMode() + { + trafficMode = TrafficMode::TORNADO; + } + + void setBitComplementTrafficMode() + { + trafficMode = TrafficMode::BIT_COMPLEMENT; + } + + void setNearestNeighborTrafficMode() + { + trafficMode = TrafficMode::NEAREST_NEIGHBOR; + } + + void setHotspotTrafficMode(uint64_t hotspotAddr, + double hotspotFraction) + { + this->hotspotAddr = hotspotAddr; + this->hotspotFraction = hotspotFraction; + trafficMode = TrafficMode::HOTSPOT; + } + + void setShuffle() { shuffleEnabled = true; } + + bool isBuffered() { + return (bufferDepth > 0); + } + + bool + isSrcActive(uint64_t id) const + { + /* empty set -> “all active” */ + return activeSrc.empty() || activeSrc.count(id); + } +}; + +} // namespace gem5 + +#endif // __NETWORK_Layer_HH__ diff --git a/src/network/network_scheduler.cc b/src/network/network_scheduler.cc new file mode 100644 index 0000000000..59954422c6 --- /dev/null +++ b/src/network/network_scheduler.cc @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2025 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + */ + +#include "network/network_scheduler.hh" + +#include // For random number generation + +#include "debug/NetworkScheduler.hh" + +namespace gem5 { + +// Constructor for NetworkScheduler +// Initializes the scheduler with maximum values, +// schedule path, and list of BufferedPorts +NetworkScheduler::NetworkScheduler( + uint64_t max_values, + const std::string& schedule_path, + const std::vector& buffered_ports +) +: maxValues(max_values), + schedulePath(schedule_path), + bufferedPorts(buffered_ports), + infiniteMode(max_values == static_cast(-1)) +{} + +// Initializes the scheduler by either generating or loading a schedule +std::queue> +NetworkScheduler::initialize() +{ + if (!maxValues) { + if (schedulePath.empty()) { + fatal("Either max_values or schedule_path must be provided.\n"); + } else { + loadSchedule(); + } + } else if (!schedulePath.empty()) { + fatal("Both max_values and schedule_path are specified.\n"); + } + return scheduleQueue; +} + +uint64_t +NetworkScheduler::generateRandomPayload(uint64_t dynamic_range) { + // Create a random number generator. + static std::random_device rd; + static std::mt19937 gen(rd()); + std::uniform_int_distribution dist(0, dynamic_range - 1); + return dist(gen); +} + + +// Generates a random value using the given src. +// It picks a random destination from BufferedPorts. +uint64_t +NetworkScheduler::generateRandomValue(uint64_t src) +{ + if (bufferedPorts.empty()) { + warn("No BufferedPorts available for scheduling.\n"); + return static_cast(-1); + } + + // Random number generator setup + std::random_device rd; + std::mt19937 gen(rd()); + + uint64_t dest_addr = src; + if (bufferedPorts.size() > 1) { + std::uniform_int_distribution<> dist(0, bufferedPorts.size() - 1); + int dest_index = dist(gen); + dest_addr = bufferedPorts[dest_index]->getAddr(); + } + + return dest_addr; +} + +// Generates a bit complement value using the given src. +// It generates a value targeting the port +// at the bit complement of the source address. +uint64_t +NetworkScheduler::generateBitComplementValue(uint64_t src) +{ + if (bufferedPorts.empty()) { + warn("No BufferedPorts available for scheduling.\n"); + return -1; + } + + if (src >= bufferedPorts.size()) { + fatal("Invalid source address %lu.\n", src); + return -1; + } + + uint64_t dest_addr = ~src; + dest_addr %= bufferedPorts.size(); + + return dest_addr; +} + + +// Generates a tornado value using the given src. +// It generates a value targeting the port +// roughly halfway around the network. +// stresses bisection bandwidth. +uint64_t +NetworkScheduler::generateTornadoValue(uint64_t src) +{ + if (bufferedPorts.empty()) { + warn("No BufferedPorts available for scheduling.\n"); + return -1; + } + + // Check if the source address is valid + if (src >= bufferedPorts.size()) { + fatal("Invalid source address %lu.\n", src); + return -1; + } + + // Generate a value targeting the port + // roughly halfway around the network. + uint64_t dest_addr = (src + bufferedPorts.size() / 2) + % bufferedPorts.size(); + + return dest_addr; +} + +// Generates a hotspot value using the +// given src, hotspot_addr and hotspot_fraction. +// It generates a value targeting the hotspot with +// probability hotspot_fraction. +uint64_t +NetworkScheduler::generateHotspotValue( + uint64_t src, + uint64_t hotspot_addr, + double hotspot_fraction +) +{ + if (bufferedPorts.empty()) { + warn("No BufferedPorts available for scheduling.\n"); + return -1; + } + + // Check if the hotspot address is valid + if (hotspot_addr >= bufferedPorts.size()) { + fatal("Invalid hotspot address %lu.\n", hotspot_addr); + return -1; + } + + // Random number generator setup + std::random_device rd; + std::mt19937 gen(rd()); + + // Generate a value targeting the hotspot + // with probability hotspot_fraction. + if (gen() % 100 < hotspot_fraction * 100) { + return hotspot_addr; + } + + // Generate a random value targeting a random destination. + uint64_t destAddr = src; + if (bufferedPorts.size() > 1) { + int destIndex = gen() % bufferedPorts.size(); + destAddr = bufferedPorts[destIndex]->getAddr(); + } + + return destAddr; +} + + +// Loads a schedule from a file +void +NetworkScheduler::loadSchedule() +{ + std::ifstream ifs(schedulePath); + if (!ifs.is_open()) { + // Fatal error if the file cannot be opened for reading + fatal("Failed to open schedule file %s for reading.\n", schedulePath); + } + + // Clear any existing schedule + clear(); + + std::vector> file_entries; + uint64_t src, dest; + + // Read src-dest pairs from the file + while (ifs >> src >> dest) { + file_entries.emplace_back(src, dest); + } + + // Load the read entries into the schedule queue + uint64_t value_count = loadScheduleEntries(file_entries); + DPRINTF(NetworkScheduler, + "Schedule loaded from %s with %d entries.\n", + schedulePath, value_count); +} + +// Adds schedule entries from the file into the queue +uint64_t +NetworkScheduler::loadScheduleEntries(const std::vector>& file_entries) +{ + uint64_t value_count = 0; + + if (maxValues > 0) { + // If maxValues is specified, only load up to that limit + uint64_t remaining = maxValues; + + // Add entries in rounds until the maxValues limit is reached + while (remaining > 0) { + uint64_t entriesThisRound = std::min(remaining, + (uint64_t)file_entries.size()); + + for (uint64_t i = 0; i < entriesThisRound; i++) { + scheduleQueue.push(file_entries[i]); + value_count++; + } + + remaining -= entriesThisRound; + } + } else { + // If no maxValues limit, load all entries + for (const auto& entry : file_entries) { + scheduleQueue.push(entry); + value_count++; + } + } + + return value_count; +} + +// Checks if the schedule file exists +bool +NetworkScheduler::fileExists(const std::string& path) const +{ + std::ifstream ifs(path); + return ifs.good(); +} + +// Checks if there are values remaining in the schedule +bool +NetworkScheduler::hasValues() const +{ + return infiniteMode ? true : (!scheduleQueue.empty()); +} + +// Clears the current schedule by swapping with an empty queue +void +NetworkScheduler::clear() +{ + std::queue> empty; + std::swap(scheduleQueue, empty); +} + +} // namespace gem5 diff --git a/src/network/network_scheduler.hh b/src/network/network_scheduler.hh new file mode 100644 index 0000000000..facd77876b --- /dev/null +++ b/src/network/network_scheduler.hh @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2025 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + */ + +#ifndef __NETWORK_SCHEDULER_HH__ +#define __NETWORK_SCHEDULER_HH__ + +#include +#include +#include +#include +#include +#include +#include + +#include "base/logging.hh" +#include "network/buffered_port.hh" + +namespace gem5 { + +// NetworkScheduler class handles scheduling values between BufferedPorts +class NetworkScheduler +{ +public: + // Constructor that initializes the scheduler + NetworkScheduler(uint64_t max_values, + const std::string& schedule_path, + const std::vector& buffered_ports + ); + + // Initializes the scheduler + std::queue> initialize(); + + // Generates a random value using the given src + uint64_t generateRandomValue(uint64_t src); + + // Generates a tornado value + // using the given src + uint64_t generateTornadoValue(uint64_t src); + + // Generates a hotspot value + // using the given src, hotspot_addr and hotspot_fraction + uint64_t generateHotspotValue(uint64_t src, + uint64_t hotspot_addr, + double hotspot_fraction + ); + + // Generates a bit complement value + // using the given src + uint64_t generateBitComplementValue(uint64_t src); + + // Generates a random payload + uint64_t generateRandomPayload(uint64_t dynamic_range); + + // Saves the current schedule to a specified file + void saveSchedule(); + + // Loads a schedule from a specified file + void loadSchedule(); + + // Loads schedule entries from a given list of source-destination pairs + uint64_t loadScheduleEntries(const std::vector>& file_entries + ); + + // Checks if there are any values left in the schedule + bool hasValues() const; + + // Clears the current schedule, effectively resetting the scheduler + void clear(); + +private: + // To check if we are in infinite mode + bool infiniteMode; + + // Checks if a given file exists at the specified path + bool fileExists(const std::string& path) const; + + // Maximum number of values to be scheduled + uint64_t maxValues; + // Path to the file where the schedule is saved/loaded + std::string schedulePath; + // List of BufferedPorts involved in the network + const std::vector& bufferedPorts; + // Scheduled values (source-destination pairs) + std::queue> scheduleQueue; + // Dynamic range of the layer + uint64_t dynamicRange; +}; + +} // namespace gem5 + +#endif // __NETWORK_SCHEDULER_HH__ diff --git a/src/network/super_network.cc b/src/network/super_network.cc new file mode 100644 index 0000000000..e83c920fb3 --- /dev/null +++ b/src/network/super_network.cc @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2025 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + */ + +#include "network/super_network.hh" + +#include +#include +#include +#include +#include + +#include "debug/SuperNetwork.hh" +#include "network/layer.hh" +#include "network/network_scheduler.hh" +#include "sim/eventq.hh" +#include "sim/sim_exit.hh" +#include "sim/stats.hh" +#include "sim/system.hh" + +namespace gem5 +{ + SuperNetwork::SuperNetwork(const SuperNetworkParams& params) + : ClockedObject(params), + numLayers(params.layers.size()), + finishedLayers(0) + { + int layer_index = 0; + for (auto layer : params.layers) { + layer->computeTimingParameters(); + DPRINTF(SuperNetwork, "Layer %d: time slot = %d\n", + layer_index, layer->getTimeSlot() + ); + DPRINTF(SuperNetwork, "Layer %d: connection window = %d\n", + layer_index, layer->getConnectionWindow() + ); + layer->scheduleNextNetworkEvent(curTick()); + layer->registerSuperNetwork(this); + managedLayers.push_back(layer); + layer_index++; + } + } + + void + SuperNetwork::notifyLayerFinished(Layer* layer) + { + + DPRINTF(SuperNetwork, "Received finish notification from Layer"); + + // Increment the counter + finishedLayers++; + + DPRINTF(SuperNetwork, + "Finished layers: %d / %d\n", + finishedLayers, numLayers + ); + + // Check if all layers are now finished + checkCompletionAndExit(); + } + + void + SuperNetwork::checkCompletionAndExit() + { + if (finishedLayers < 0 || finishedLayers > numLayers) { + panic("SuperNetwork finishedLayers count (%d) \ + is out of bounds [0, %d]!", + finishedLayers, numLayers + ); + } + + if (finishedLayers == numLayers) { + DPRINTF(SuperNetwork, + "All %d layers have finished processing.\n", + numLayers + ); + // Exit the simulation loop + exitSimLoop("SuperNetwork: \ + All layers finished processing values." + ); + } + } + +} // namespace gem5 diff --git a/src/network/super_network.hh b/src/network/super_network.hh new file mode 100644 index 0000000000..67265a13cd --- /dev/null +++ b/src/network/super_network.hh @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * 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; + * neither the name of the copyright holders 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 + * OWNER 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. + */ + +#ifndef __NETWORK_SUPERNETWORK_HH__ +#define __NETWORK_SUPERNETWORK_HH__ + +#include "network/buffered_port.hh" +#include "network/layer.hh" +#include "network/network_scheduler.hh" +#include "params/SuperNetwork.hh" +#include "sim/clocked_object.hh" +#include "sim/eventq.hh" +#include "sim/sim_exit.hh" +#include "sim/stats.hh" + +namespace gem5 +{ + +class SuperNetwork : public ClockedObject +{ + private: + std::vector layers; + + Cycles timeSlot; + + std::vector managedLayers; // Store pointers to layers + const int numLayers; // Total number of layers managed + int finishedLayers; // Counter for finished layers + + public: + // Constructor: Initializes the SuperNetwork with given parameters + SuperNetwork(const SuperNetworkParams& params); + + // Function for layers to notify when they finish processing + void notifyLayerFinished(Layer* layer); + + // Function to check if all layers have finished processing + // and exit the simulation if they have + void checkCompletionAndExit(); +}; + +} // namespace gem5 + +#endif // __NETWORK_SUPERNETWORK_HH__