The Sparta Modeling Framework
Loading...
Searching...
No Matches
RegisterBankTable.hpp
1// <RegisterBankTable> -*- C++ -*-
2
3#pragma once
4
5#include <iostream>
6#include <unordered_map>
7
8#include "sparta/functional/Register.hpp"
12
13
14namespace sparta
15{
29template <typename RegisterT>
31{
37 static constexpr typename RegisterT::bank_idx_type WARN_MAX_BANK_IDX = 64;
38
44 static constexpr typename RegisterT::bank_idx_type ERROR_MAX_BANK_IDX = 256;
45
46public:
47
52 typedef std::unordered_map<Register::group_idx_type, RegisterT *> RegisterMap;
53 typedef std::vector<RegisterT *> RegisterVector;
54
59 typedef std::vector<RegisterMap> GroupVector;
60
65 typedef std::vector<GroupVector> BankVector;
66
71 num_groups_(0),
72 num_regs_(0)
73 {
74 // Create an empty GroupVector because BANK_IDX_DEFAULT is
75 // guaranteed to exist and is 0. If not 0, change this code to
76 // ensure it exists
77 static_assert(RegisterT::BANK_IDX_DEFAULT==0, "BANK_IDX_DEFAULT must be 0");
78 banks_.push_back({});
79 }
80
84 virtual ~RegisterBankTable() = default;
85
91 typename RegisterT::bank_idx_type getNumBanks() const
92 {
93 return banks_.size();
94 }
95
100 typename RegisterT::group_idx_type getNumGroups() const
101 {
102 return num_groups_;
103 }
104
114 uint32_t getGroupSize(typename RegisterT::group_num_type group_num,
115 typename RegisterT::bank_idx_type bank_num)
116 {
117 if(group_num == RegisterT::GROUP_NUM_NONE){
118 return 0;
119 }
120 if(bank_num >= banks_.size()){
121 throw SpartaException("Cannot get group size of bank ")
122 << bank_num << " group " << group_num << " because there are only "
123 << banks_.size() << " banks in bank table " << stringize();
124 }
125 if(group_num >= banks_[bank_num].size()){
126 return 0;
127 //throw SpartaException("Cannot get group size of bank ")
128 // << bank_num << " group " << group_num << " because there are only "
129 // << banks_[bank_num].size() << " groups in bank table " << stringize();
130 }
131 return banks_[bank_num][group_num].size();
132 }
133
134 GroupVector& operator[](typename RegisterT::bank_idx_type bank_idx)
135 {
136 return banks_[bank_idx];
137 }
138
139 GroupVector& at(typename RegisterT::bank_idx_type bank_idx)
140 {
141 return banks_.at(bank_idx);
142 }
143
148 uint32_t getNumRegisters() const {
149 return num_regs_;
150 }
151
169 extendBanks_(min_idx + 1);
170 }
171
189 void addRegister(RegisterT *r)
190 {
191 sparta_assert(r != nullptr);
192
193 const auto &rdef = r->getDefinition();
194
195 if(rdef.group_num == RegisterT::GROUP_NUM_NONE){
196 if(rdef.bank_membership.size() != 0){
197 throw SpartaException("A register has no group number so it cannot be looked up "
198 "through a bank, but but does have bank membership information. This is "
199 "probably a mistake in one of these two fields. Error while adding unbanked "
200 "register ") << *r;
201 }
202 }else{
203
204 if(rdef.group_num > num_groups_ && rdef.group_num >= 300){
206 std::cerr << "WARNING: Register " << r->getLocation()
207 << " Group num is very large: " << rdef.group_num
208 << ". This requires a vector to be allocated of this size and "
209 "probably wastes memory";
210 }
211
212 if(r->isBanked() == false){
213 // 1 bank is expected (from construction) so that this register can
214 // be tested against other registers in the banks without needing to
215 // compare against the unbanked_regs_ list
216 sparta_assert(banks_.size() > 0, "1 or more banks expected before any addRegister calls");
217
218 // Unbanked register
219 // Check for collisions in all existing banks
220 for(size_t bank_idx = 0; bank_idx < banks_.size(); ++bank_idx){
221 if(canLookupRegister(rdef.group_num, rdef.group_idx, bank_idx)){
222 throw SpartaException("A regsiter already exists in bank ") << bank_idx
223 << " with group num " << rdef.group_num << " and group idx "
224 << rdef.group_idx << ". Error while adding unbanked register: " << *r;
225 }
226 }
227
228 // Safely add the register to the table
229 unbanked_regs_.push_back(r);
230
231 for(GroupVector& gv : banks_){
232 insertRegisterInBank_(r, gv); // Should never throw
233 }
234 }else{
235 // Banked register
236 // Check for collisions in existing banks for which
237 // this register is accessible
238 typename RegisterT::bank_idx_type max_bank_idx = 0;
239 for(typename RegisterT::bank_idx_type bank_idx : rdef.bank_membership){
240 max_bank_idx = std::max(max_bank_idx, bank_idx);
241 if(bank_idx < banks_.size()){
242 // Bank currently exists, check for collisions
243 if(canLookupRegister(rdef.group_num, rdef.group_idx, bank_idx)){
244 throw SpartaException("A regsiter already exists in bank ") << bank_idx
245 << " with group num " << rdef.group_num << " and group idx "
246 << rdef.group_idx << ". Error while adding banked register: " << *r;
247 }
248 }
249 }
250
251 if(max_bank_idx >= WARN_MAX_BANK_IDX){
253 std::cerr << "WARNING: Register " << r->getLocation()
254 << " bank membership number contains a large value: " << max_bank_idx
255 << ". This requires a vector to be allocated of this size and "
256 "probably wastes memory";
257 }
258 if(max_bank_idx >= ERROR_MAX_BANK_IDX){
260 throw SpartaException("Register ") << r->getLocation()
261 << " bank membership number contains a very large value: " << max_bank_idx
262 << ". This requires a vector to be allocated of this size and is likely a "
263 "mistake. If not, increase RegisterBankTable::ERROR_MAX_BANK_IDX";
264 }
265
266 // Check for collisions with unbanked registers
267 for(auto ubr : unbanked_regs_){
268 if(ubr->getGroupNum() == r->getGroupNum()
269 && ubr->getGroupIdx() == r->getGroupIdx()){
270 throw SpartaException("An unbanked regsiter already exists in this set with ")
271 << "group num " << r->getGroupNum() << " and group idx "
272 << r->getGroupIdx() << ". Error while adding banked register: " << *r;
273 }
274 }
275
276 // Extend all banks to fit the max bank index of this register
277 extendBanks_(max_bank_idx + 1);
278
279 // Insert this register into each bank of which it is a member.
280 // banks_ will be large enough to contain each bank idx
281 for(auto bank_idx : rdef.bank_membership){
282 GroupVector& bank = banks_[bank_idx];
283 insertRegisterInBank_(r, bank); // Should never throw
284 }
285 }
286
287 num_groups_ = std::max(num_groups_, rdef.group_num+1);
288 ++num_regs_;
289 }
290 }
291
292 bool canLookupRegister(typename RegisterT::group_num_type group_num,
293 typename RegisterT::group_idx_type group_idx,
294 typename RegisterT::bank_idx_type bank_idx) const noexcept
295 {
296 return banks_.size() > bank_idx
297 && banks_[bank_idx].size() > group_num
298 && banks_[bank_idx][group_num].count(group_idx) != 0;
299 }
300
301 RegisterT *lookupRegister(typename RegisterT::group_num_type group_num,
302 typename RegisterT::group_idx_type group_idx,
303 typename RegisterT::bank_idx_type bank_idx)
304 {
305 const auto &rm = banks_[bank_idx][group_num];
306 const auto idx_count = rm.count(group_idx);
307 if (idx_count==1) {
308 sparta_assert(rm.at(group_idx) != nullptr);
309 }
310 return (idx_count==1 ? rm.at(group_idx) : nullptr);
311 }
312
313 const RegisterT *lookupRegister(typename RegisterT::group_num_type group_num,
314 typename RegisterT::group_idx_type group_idx,
315 typename RegisterT::bank_idx_type bank_idx) const
316 {
317 const auto &rm = banks_[bank_idx][group_num];
318 const auto idx_count = rm.count(group_idx);
319 if (idx_count==1) {
320 sparta_assert(rm.at(group_idx) != nullptr);
321 }
322 return (idx_count==1 ? rm.at(group_idx) : nullptr);
323 }
324
337 RegisterT *getRegister(typename RegisterT::group_num_type group_num,
338 typename RegisterT::group_idx_type group_idx,
339 typename RegisterT::bank_idx_type bank_idx) {
340 if(__builtin_expect(banks_.size() <= bank_idx, 0)){
341 throw SpartaException("Register set ")
342 << stringize() << " has no bank_idx " << bank_idx;
343 }
344 GroupVector& gv = banks_[bank_idx];
345 if(__builtin_expect(gv.size() <= group_num, 0)){
346 throw SpartaException("Register set ")
347 << stringize() << " has no group " << group_num
348 << " in bank " << bank_idx;
349 }
350 RegisterMap& rm = gv[group_num];
351 if(__builtin_expect(rm.size() == 0, 0)){
352 // Empty register map for this group - it does not exist
353 throw SpartaException("Register set ")
354 << stringize() << " has no group " << group_num;
355 }
356
357 if(__builtin_expect(rm.count(group_idx) == 0, 0)){
358 // Null register at this index
359 throw SpartaException("Register set ")
360 << stringize() << " has no register with idx " << group_idx
361 << " in group " << group_num;
362 }
363
364 sparta_assert(rm.at(group_idx) != nullptr);
365 return rm[group_idx];
366 }
367
368 // Overload of TreeNode::stringize
369 virtual std::string stringize(bool pretty=false) const {
370 (void) pretty;
371 std::stringstream ss;
372 //ss << '<' << getLocation() << ' ' << regs_.size() << " regs>";
373 ss << "<RegisterSet bank table: " << banks_.size() << " banks, "
374 << num_regs_ << " phy regs>";
375 return ss.str();
376 }
377
382 void dump(std::ostream& out, bool detailed=false) const {
383 (void) detailed;
384
385 const uint32_t GROUP_NAME_WIDTH = 5;
386 const uint32_t GROUP_NUM_WIDTH = 4;
387 const uint32_t GROUP_IDX_WIDTH = 4;
388 const uint32_t COL_WIDTH = 8;
389
390 typename RegisterT::group_num_type group_num_max = 0;
391
392 // Header of bank columns
393 out << " banks->|";
394 for(size_t bank_idx = 0; bank_idx != banks_.size(); ++bank_idx){
395 out << std::setw(COL_WIDTH) << bank_idx << "|";
396
397 // Determine which bank has the largest group
398 decltype(group_num_max) num_groups = banks_[bank_idx].size();
399 if(num_groups > group_num_max){
400 group_num_max = num_groups - 1;
401 }
402
403 }
404 out << std::endl;
405
406 // Header for groups and cross-bar
407 out << " group |index|";
408 for(size_t bank_idx = 0; bank_idx != banks_.size(); ++bank_idx){
409 out << "--------|"; // Column width
410 }
411
412 out << std::endl;
413
414 // Rows
415 for(typename RegisterT::group_num_type group_num = 0; group_num <= group_num_max; ++group_num) {
416 // Determine the largest group_idx in this group by iterating all
417 // banks and looking for the largest RegisterMap key in that bank
418 typename RegisterT::group_idx_type group_idx_max = 0;
419 for(size_t bank_idx = 0; bank_idx != banks_.size(); ++bank_idx){
420 if(banks_[bank_idx].size() > group_num){
421 // Bank contains this register group
422 const auto & bank = banks_[bank_idx];
423 for (auto &it : bank[group_num]) {
424 group_idx_max = std::max(group_idx_max, it.first);
425 }
426 }
427 }
428
429 // Row for each item in the group
430 bool wrote_group = false; // Has this group number been written yet
431 for(typename RegisterT::group_idx_type group_idx = 0; group_idx <= group_idx_max; ++group_idx) {
432
433 // Check that there at least 1 register in any bank for this group and index
434 bool has_reg = false;
435 std::string group_name; // Name of this group (extracted below)
436 std::vector<std::string> names;
437 for(size_t bank_idx = 0; bank_idx != banks_.size(); ++bank_idx){
438 if(canLookupRegister(group_num, group_idx, bank_idx)){
439 if(has_reg == false){
440 group_name = lookupRegister(group_num, group_idx, bank_idx)->getGroupName();
441 }
442 has_reg = true;
443 const auto r = lookupRegister(group_num, group_idx, bank_idx);
444 names.push_back(r->getName());
445 }else{
446 names.push_back("");
447 }
448 }
449
450 if(has_reg){
451 // Display this group/index row for each bank
452 if(wrote_group){
453 writeNChars(out, GROUP_NAME_WIDTH + 1 + GROUP_NUM_WIDTH);
454 }else{
455 wrote_group = true;
456 out << std::setw(GROUP_NAME_WIDTH) << group_name << ' '
457 << std::setw(GROUP_NUM_WIDTH) << group_num;
458 }
459 out << " |" << std::setw(GROUP_IDX_WIDTH) << group_idx << " |";
460
461 // Column for each bank
462 for(const std::string& val : names){
463 out << std::setw(COL_WIDTH) << val << "|";
464 }
465
466 out << std::endl;
467 }
468
469 }
470 }
471
472 }
473
474protected:
475
483 void extendBanks_(typename RegisterT::bank_idx_type num_banks)
484 {
485 // Add missing banks if required
486 while(num_banks > banks_.size()){
487 banks_.push_back({});
488 GroupVector& gv = banks_.back();
489 for(auto r : unbanked_regs_){
491 }
492 }
493 }
494
505 void insertRegisterInBank_(RegisterT *r, GroupVector& bank)
506 {
507 sparta_assert(r != nullptr);
508 const auto rdef = r->getDefinition();
509 while(rdef.group_num >= bank.size()){
510 bank.push_back(RegisterMap());
511 }
512 RegisterMap& rm = bank[rdef.group_num];
513 sparta_assert(rm.count(rdef.group_idx)==0);
514 rm.insert(std::make_pair(rdef.group_idx, r));
515 return;
516 }
517
518private:
519
531 BankVector banks_;
532
536 std::vector<RegisterT *> unbanked_regs_;
537
542 typename RegisterT::group_num_type num_groups_;
543
547 uint32_t num_regs_;
548};
549
550} // namespace sparta
551
Set of macros for Sparta assertions. Caught by the framework.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
Exception class for all of Sparta.
Basic Node framework in sparta device tree composite pattern.
Container of register banks as a helper for RegisterSet. Instances of this class will be owned by a s...
std::unordered_map< Register::group_idx_type, RegisterT * > RegisterMap
Vector of registers owned externally. Available for lookup within a group by Register::Definition::gr...
std::vector< RegisterMap > GroupVector
Vector of RegisterMap's used for lookup by numeric group number of type Register::Definition::group_n...
void addRegister(RegisterT *r)
Adds a register to this table unless it is not a member of a group (See Register::getGroupNum.
void insertRegisterInBank_(RegisterT *r, GroupVector &bank)
Insert a register in a specific bank.
void dump(std::ostream &out, bool detailed=false) const
Dump this register bank table to an out stream. Banks will be columns and group num/id will be rows.
void setMinimumBankIndex(Register::bank_idx_type min_idx)
Sets the minimum bank index for this register set, overriding the default of BANK_IDX_DEFAULT.
RegisterT::group_idx_type getNumGroups() const
Gets the number of register groups added to this table regardless of banks.
uint32_t getGroupSize(typename RegisterT::group_num_type group_num, typename RegisterT::bank_idx_type bank_num)
Gets the number of registers in a group by its group num and bank.
virtual ~RegisterBankTable()=default
Destructor.
std::vector< GroupVector > BankVector
Vector of GroupVectors used for lookup by numeric bank index of type Register::Definition::bank_idx_t...
void extendBanks_(typename RegisterT::bank_idx_type num_banks)
Extends the banks_ vector to contain num_banks entries.
RegisterT * getRegister(typename RegisterT::group_num_type group_num, typename RegisterT::group_idx_type group_idx, typename RegisterT::bank_idx_type bank_idx)
Gets a Register by group number and group index and throws an exception if it cannot be found.
RegisterT::bank_idx_type getNumBanks() const
Gets the total number of banks instantiated (even if they contain have no actual registers accessible...
uint32_t getNumRegisters() const
Returns number of registers in this table. This excludes any registers added having no group number.
uint32_t bank_idx_type
Numeric bank identifier for bank lookup.
Definition Register.hpp:98
Used to construct and throw a standard C++ exception. Inherits from std::exception.
Macros for handling exponential backoff.
void writeNChars(std::ostream &out, uint32_t num, char chr=' ')
Insert a number of some character into an ostream.
Definition Utils.hpp:332