diff --git a/TOGSim/include/Core.h b/TOGSim/include/Core.h index dbc91950..8b812368 100644 --- a/TOGSim/include/Core.h +++ b/TOGSim/include/Core.h @@ -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 _sram_allocs; // SA weight-buffer throttle (sec 10.4). _weight_slots_used[s] = weights resident diff --git a/TOGSim/include/Tile.h b/TOGSim/include/Tile.h index b11b9f8e..3ba97cbd 100644 --- a/TOGSim/include/Tile.h +++ b/TOGSim/include/Tile.h @@ -39,6 +39,25 @@ class Tile : public std::enable_shared_from_this { std::deque>& get_instructions() { return _instructions; } void enqueue_ready(const std::shared_ptr& inst) { _ready_queue.push_back(inst); } std::list>& 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>::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; } @@ -61,6 +80,12 @@ class Tile : public std::enable_shared_from_this { size_t _nr_finished_insts = 0; std::deque> _instructions; std::list> _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> _child_tiles; void *_custom_data=NULL; bool _stonne_tile=false; diff --git a/TOGSim/src/Core.cc b/TOGSim/src/Core.cc index 8588fede..41fe856e 100644 --- a/TOGSim/src/Core.cc +++ b/TOGSim/src/Core.cc @@ -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 @@ -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 @@ -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()) { @@ -365,6 +367,7 @@ void Core::cycle() { exit(EXIT_FAILURE); } _weight_slots_used[sa_idx]++; + _weight_free--; auto tok = std::make_shared(WeightToken{sa_idx, n_consumers}); for (auto& c : inst->get_deps(DepEvent::ISSUE)) if (c->get_compute_type() == MATMUL) { @@ -408,6 +411,7 @@ void Core::cycle() { inst->finish_instruction(); static_cast(inst->get_owner())->inc_finished_inst(); _stat_tot_skipped_inst.at(static_cast(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 @@ -461,9 +465,12 @@ void Core::cycle() { if (issued) { _stat_inst_count.at(static_cast(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++; } }