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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions TOGSim/include/Core.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ class Core {
// to its accumulated footprint bytes (freed when its last reader issues).
size_t _sram_used = 0;
size_t _sram_capacity = 0;
// Free SA weight slots. With _sram_used it keys the issue scan's cursor: an
// instruction it walked past wakes only if one of the two grows.
int _weight_free = 0;
std::unordered_map<int64_t, size_t> _sram_allocs;

// SA weight-buffer throttle (sec 10.4). _weight_slots_used[s] = weights resident
Expand Down
25 changes: 25 additions & 0 deletions TOGSim/include/Tile.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,25 @@ class Tile : public std::enable_shared_from_this<Tile> {
std::deque<std::shared_ptr<Instruction>>& get_instructions() { return _instructions; }
void enqueue_ready(const std::shared_ptr<Instruction>& inst) { _ready_queue.push_back(inst); }
std::list<std::shared_ptr<Instruction>>& get_ready_instructions() { return _ready_queue; }

// Issue-scan cursor (Core::cycle): the scan only walks past instructions blocked on
// free spad bytes or a free weight slot, so it can resume where it left off.
using ReadyIt = std::list<std::shared_ptr<Instruction>>::iterator;
ReadyIt scan_from(size_t sram_used, int weight_free) {
if (_scan_blocked && sram_used >= _scan_sram_used && weight_free <= _scan_weight_free)
return std::next(_scan_blocked_at);
return _ready_queue.begin(); // a resource grew -> the prefix may issue now
}
void note_blocked(ReadyIt it, size_t sram_used, int weight_free) {
_scan_blocked_at = it;
_scan_blocked = true;
_scan_sram_used = sram_used; // it could not issue with this much spad free...
_scan_weight_free = weight_free; // ...nor with this many weight slots free
}
// Kept across a rescan (the prefix blocks again once the freed resource is taken);
// it only dies with its instruction.
bool is_scan_cursor(ReadyIt it) const { return _scan_blocked && it == _scan_blocked_at; }
void drop_scan_cursor() { _scan_blocked = false; }
void print();
size_t nr_insts() { return _nr_insts; }
size_t nr_finshed_insts() { return _nr_finished_insts; }
Expand All @@ -61,6 +80,12 @@ class Tile : public std::enable_shared_from_this<Tile> {
size_t _nr_finished_insts = 0;
std::deque<std::shared_ptr<Instruction>> _instructions;
std::list<std::shared_ptr<Instruction>> _ready_queue;
// Only dereferenced while resources are no better than at note_blocked, and a
// still-blocked instruction is never erased.
ReadyIt _scan_blocked_at;
bool _scan_blocked = false;
size_t _scan_sram_used = 0;
int _scan_weight_free = 0;
std::vector<std::shared_ptr<Tile>> _child_tiles;
void *_custom_data=NULL;
bool _stonne_tile=false;
Expand Down
11 changes: 9 additions & 2 deletions TOGSim/src/Core.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Core::Core(uint32_t id, SimulationConfig config)
exit(EXIT_FAILURE);
}
_weight_slots_used.resize(_num_systolic_array_per_core, 0);
_weight_free = _num_systolic_array_per_core * (int)_weight_slot_depth;
}

// Round-robin a systolic array that still has a free weight slot; -1 if all full
Expand All @@ -42,7 +43,7 @@ int Core::pick_free_weight_sa() {
void Core::apply_due(const DueAction& a) {
switch (a.kind) {
case DueAction::FreeWeightSlot:
if (--a.token->refcount <= 0) { _weight_slots_used[a.token->sa]--; _issue_dirty = true; } // weight slot freed -> re-arm
if (--a.token->refcount <= 0) { _weight_slots_used[a.token->sa]--; _weight_free++; _issue_dirty = true; } // weight slot freed -> re-arm
break;
case DueAction::WakeBar: {
auto bar = a.bar; // async load data arrived -> fire its MEMORY_BAR
Expand Down Expand Up @@ -286,7 +287,8 @@ void Core::cycle() {

for (int i=0; i<_tiles.size() && !issued; i++) {
auto& instructions = _tiles[i]->get_ready_instructions();
for (auto it=instructions.begin(); it!=instructions.end();) {
// Resume after the prefix already known to be blocked (Tile::scan_from).
for (auto it=_tiles[i]->scan_from(_sram_used, _weight_free); it!=instructions.end();) {
auto& inst = *it;

switch (inst->get_opcode()) {
Expand Down Expand Up @@ -365,6 +367,7 @@ void Core::cycle() {
exit(EXIT_FAILURE);
}
_weight_slots_used[sa_idx]++;
_weight_free--;
auto tok = std::make_shared<WeightToken>(WeightToken{sa_idx, n_consumers});
for (auto& c : inst->get_deps(DepEvent::ISSUE))
if (c->get_compute_type() == MATMUL) {
Expand Down Expand Up @@ -408,6 +411,7 @@ void Core::cycle() {
inst->finish_instruction();
static_cast<Tile*>(inst->get_owner())->inc_finished_inst();
_stat_tot_skipped_inst.at(static_cast<size_t>(inst->get_opcode()))++;
if (_tiles[i]->is_scan_cursor(it)) _tiles[i]->drop_scan_cursor();
it = instructions.erase(it); // erase returns the next iterator; the
continue; // old code fell through to it++ on the
// erased (invalidated) iterator -> UB
Expand Down Expand Up @@ -461,9 +465,12 @@ void Core::cycle() {

if (issued) {
_stat_inst_count.at(static_cast<size_t>(inst->get_opcode()))++;
if (_tiles[i]->is_scan_cursor(it)) _tiles[i]->drop_scan_cursor();
instructions.erase(it);
break;
}
// Did not issue -> blocked on spad or a weight slot (the only two stalls).
_tiles[i]->note_blocked(it, _sram_used, _weight_free);
it++;
}
}
Expand Down