InterSim2/allocators/ — Switch & VC Allocation Algorithms
Allocators — Switch & VC Allocation Algorithms
Resolves contention when multiple input VCs compete for the same output port or output VC during the switch-allocation (SA) and VC-allocation (VA) stages of the router pipeline.
📚 What is this & why does it exist?
Inside every router on the Network-on-Chip, multiple packets arriving on different input ports may want to leave through the same output port at the same time. The allocators sub-module decides who wins that competition each cycle. Without fair, fast allocation the crossbar switch would be wasted and packets would stall needlessly.
💡 Real-world analogy
Imagine a busy airport with many gates (inputs) and a limited number of runways (outputs). Each gate wants to send a plane to a runway, but only one plane can use each runway at a time. The allocator is the air traffic controller: it collects all departure requests, applies a fairness policy (round-robin, priority, or random), and grants exactly one gate per runway each cycle — maximizing throughput while preventing starvation.
Contents
Architecture
classDiagram
class Module {
+Name()
+FullName()
}
class Allocator {
#int _inputs
#int _outputs
#vector~int~ _inmatch
#vector~int~ _outmatch
+AddRequest(in, out, label, in_pri, out_pri)
+Allocate()*
+OutputAssigned(in) int
+InputAssigned(out) int
+NewAllocator()$ Allocator*
}
class DenseAllocator {
#vector~vector~sRequest~~ _request
+ReadRequest()
}
class SparseAllocator {
#vector~map~ _in_req
#vector~map~ _out_req
+ReadRequest()
}
Module <|-- Allocator
Allocator <|-- DenseAllocator
Allocator <|-- SparseAllocator
DenseAllocator <|-- Wavefront
DenseAllocator <|-- PIM
DenseAllocator <|-- LOA
DenseAllocator <|-- MaxSizeMatch
SparseAllocator <|-- iSLIP_Sparse
SparseAllocator <|-- SeparableAllocator
SparseAllocator <|-- SelAlloc
SeparableAllocator <|-- SeparableInputFirstAllocator
SeparableAllocator <|-- SeparableOutputFirstAllocator
Allocation Flow
flowchart LR
A["IQRouter: SA/VA stage"] -->|"AddRequest(in,out,pri)"| B["Allocator"]
B --> C{"Allocate()"}
C -->|iSLIP| D["Grant/Accept\nround-robin pointers"]
C -->|Wavefront| E["Diagonal sweep\non dense matrix"]
C -->|Separable| F["Input Arb → Output Arb\n(or vice-versa)"]
C -->|PIM| G["Random parallel\nmatching iterations"]
D --> H["OutputAssigned(in)\nInputAssigned(out)"]
E --> H
F --> H
G --> H
H --> I["Router crossbar\ntraversal"]
Key Classes
| Class | File | Purpose |
|---|---|---|
Allocator | allocator.hpp | Abstract base class — defines AddRequest(), Allocate(), OutputAssigned(), InputAssigned(); factory method NewAllocator() |
DenseAllocator | allocator.hpp | Stores full N×M request matrix in vector<vector<sRequest>>; base for Wavefront, PIM, LOA, MaxSize |
SparseAllocator | allocator.hpp | Sparse request storage via map<int, sRequest> per input/output; base for iSLIP, Separable, SelAlloc |
iSLIP_Sparse | islip.hpp | Iterative round-robin matching — _gptrs (grant) / _aptrs (accept) pointers rotated on match |
Wavefront | wavefront.hpp | Diagonal-sweep matching on dense matrix; _pri offset, _skip_diags optimization flag |
SeparableAllocator | separable.hpp | Two-phase allocation using per-input _input_arb and per-output _output_arb Arbiter objects |
SeparableInputFirstAllocator | separable_input_first.hpp | Input-first variant: each input arbiter picks best output, then output arbiters resolve conflicts |
SeparableOutputFirstAllocator | separable_output_first.hpp | Output-first variant: each output arbiter picks best input, then input arbiters resolve |
PIM | pim.hpp | Parallel Iterative Matching — _PIM_iter iterations of randomized grant/accept on dense matrix |
LOA | loa.hpp | Lonely Output Allocator — prioritizes outputs with fewest requests via _counts vector |
SelAlloc | selalloc.hpp | Selection-based allocator with _outmask for masked outputs and iterated grant/accept |
MaxSizeMatch | maxsize.hpp | Maximum-size matching via augmenting paths (_ShortestAugmenting()); guarantees optimal matching |
Data Structures
| Type | Name | Description |
|---|---|---|
| struct | sRequest | Request record: port, label, in_pri, out_pri |
| vector<int> | _inmatch / _outmatch | Result arrays — _inmatch[i] = output granted to input i (-1 if none) |
| vector<int> | _gptrs / _aptrs | iSLIP grant and accept round-robin pointers (one per output/input) |
| set<pair> | _priorities | Wavefront priority set for diagonal start positions |
| vector<Arbiter*> | _input_arb / _output_arb | Per-port arbiter objects used by SeparableAllocator |
| vector<int> | _counts | LOA per-output request count for lonely-output prioritization |
| int* | _s / _ns | MaxSizeMatch BFS stacks for augmenting path search |
| vector<int> | _outmask | SelAlloc bitmask to disable specific outputs during allocation |
Interactions
| Direction | Partner | Interface | Description |
|---|---|---|---|
| ← | IQRouter | NewAllocator() | Router creates VC and SW allocators via factory at construction |
| ← | IQRouter | AddRequest(in, out, pri) | SA/VA stages register competing requests each cycle |
| → | IQRouter | OutputAssigned(in) | Router reads matching result after Allocate() |
| → | IQRouter | InputAssigned(out) | Router reads which input won each output port |
| ← | IQRouter | Clear() | Request matrix reset at the start of each cycle |
| ↔ | Arbiter | AddRequest() / Arbitrate() | SeparableAllocator delegates to per-port Arbiter objects |
| ← | Configuration | alloc_type string | Allocator type selected via config: islip, wavefront, separable, etc. |
Configuration
| Knob / Parameter | Default | Description |
|---|---|---|
sw_allocator | islip | Switch allocator type: islip, wavefront, separable, pim, loa, selalloc, maxsize |
vc_allocator | islip | VC allocator type (same choices as sw_allocator) |
sw_alloc_iters | 1 | Number of iSLIP / PIM / SelAlloc iterations per cycle |
vc_alloc_iters | 1 | Number of VC allocation iterations per cycle |
arb_type | round_robin | Arbiter type used inside separable allocators |
speculative | 0 | Enable speculative SA (allocate before VA completes) |
spec_check_elig | 1 | Check VC eligibility during speculative allocation |
spec_check_cred | 1 | Check credit availability during speculative allocation |
spec_mask_by_reqs | 0 | Mask speculative requests by non-speculative results |
hold_switch_for_packet | 0 | Hold switch grant across all flits of a multi-flit packet |
vc_busy_when_full | 0 | Mark VC as busy (not eligible for allocation) when buffer is full |
num_vcs | 4 | Number of virtual channels per input port (affects allocator matrix dimensions) |
Directory Map
Source tree for allocators/
allocators/
├── allocator.hpp / .cpp ← Base class, DenseAllocator, SparseAllocator, factory
├── islip.hpp / .cpp ← iSLIP iterative round-robin matching
├── wavefront.hpp / .cpp ← Diagonal-sweep dense matching
├── separable.hpp / .cpp ← SeparableAllocator base (holds Arbiter vectors)
├── separable_input_first.hpp/.cpp ← Input-first separable allocation
├── separable_output_first.hpp/.cpp← Output-first separable allocation
├── pim.hpp / .cpp ← Parallel Iterative Matching
├── loa.hpp / .cpp ← Lonely Output Allocator
├── selalloc.hpp / .cpp ← Selection-based allocator with output masking
└── maxsize.hpp / .cpp ← Maximum-size matching via augmenting paths
Deep-Dive Highlights
Core allocation algorithms extracted for in-depth exploration. Each topic covers one algorithm family with diagrams, code walks, and configuration details.
Deep-Dive Pages
iSLIP Round-Robin Matching Focus
Iterative grant-accept protocol with rotating pointers that guarantees fairness and converges in O(1) iterations for uniform traffic.
Separable Two-Phase Allocation Focus
Decomposes NxM matching into per-input and per-output arbiter phases. Configurable with any arbiter type (round-robin, matrix, tree).
Wavefront & PIM Matching Focus
Dense-matrix algorithms: wavefront sweeps diagonals for O(N) matching; PIM uses randomized parallel iterations for distributed matching.
Code Examples
Example: Allocator Factory & Usage in IQRouter
// Router constructs allocators at init time via factory
_vc_allocator = Allocator::NewAllocator(
this, "vc_allocator", config.GetStr("vc_allocator"),
_vcs * _inputs, _vcs * _outputs, &config);
_sw_allocator = Allocator::NewAllocator(
this, "sw_allocator", config.GetStr("sw_allocator"),
_inputs * _input_speedup, _outputs * _output_speedup, &config);
Example: iSLIP Grant-Accept Loop
// Simplified iSLIP Allocate() — one iteration
void iSLIP_Sparse::Allocate() {
for (int iter = 0; iter < _iSLIP_iter; ++iter) {
// Phase 1: Grant — each output scans from _gptrs[out] round-robin
for (int out = 0; out < _outputs; ++out) {
auto &req = _out_req[out];
int best = -1;
for (auto &r : req) {
if (_inmatch[r.first] == -1) { // input not yet matched
if (best == -1 || /* round-robin priority */ ...)
best = r.first;
}
}
if (best != -1) { _outmatch[out] = best; }
}
// Phase 2: Accept — each input picks best granted output
for (int in = 0; in < _inputs; ++in) {
// accept from _aptrs[in], update pointers on match
}
}
}