| 1 | // ========== Copyright Header Begin ========================================== |
| 2 | // |
| 3 | // OpenSPARC T2 Processor File: SS_BaseCsr.cc |
| 4 | // Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. |
| 5 | // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. |
| 6 | // |
| 7 | // The above named program is free software; you can redistribute it and/or |
| 8 | // modify it under the terms of the GNU General Public |
| 9 | // License version 2 as published by the Free Software Foundation. |
| 10 | // |
| 11 | // The above named program is distributed in the hope that it will be |
| 12 | // useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | // General Public License for more details. |
| 15 | // |
| 16 | // You should have received a copy of the GNU General Public |
| 17 | // License along with this work; if not, write to the Free Software |
| 18 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. |
| 19 | // |
| 20 | // ========== Copyright Header End ============================================ |
| 21 | /************************************************************************ |
| 22 | ** |
| 23 | ** Copyright (C) 2006, Sun Microsystems, Inc. |
| 24 | ** |
| 25 | ** Sun considers its source code as an unpublished, proprietary |
| 26 | ** trade secret and it is available only under strict license provisions. |
| 27 | ** This copyright notice is placed here only to protect Sun in the event |
| 28 | ** the source is deemed a published work. Disassembly, decompilation, |
| 29 | ** or other means of reducing the object code to human readable form |
| 30 | ** is prohibited by the license agreement under which this code is |
| 31 | ** provided to the user or company in possession of this copy. |
| 32 | ** |
| 33 | *************************************************************************/ |
| 34 | #include <sstream> |
| 35 | #include <list> |
| 36 | #include "SS_BaseCsr.h" |
| 37 | #include "SS_Memory.h" |
| 38 | #include "SS_AddressMap.h" |
| 39 | |
| 40 | using namespace std; |
| 41 | |
| 42 | //====================================================================== |
| 43 | //====================================================================== |
| 44 | void |
| 45 | SS_BaseCsr::access( void* obj, |
| 46 | uint_t sid, |
| 47 | SS_Access::Type type, |
| 48 | SS_Paddr addr, |
| 49 | uint_t size, |
| 50 | uint64_t* data) |
| 51 | { |
| 52 | SS_BaseCsr* self = (SS_BaseCsr*)obj; |
| 53 | switch (type) |
| 54 | { |
| 55 | case SS_Access::LOAD: |
| 56 | self->read64(addr, data, MemoryTransaction::READ, sid); |
| 57 | break; |
| 58 | case SS_Access::STORE: |
| 59 | self->write64(addr, *data, MemoryTransaction::WRITE, sid); |
| 60 | break; |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | //====================================================================== |
| 65 | //====================================================================== |
| 66 | int64_t |
| 67 | SS_BaseCsr::findAttributeEntryNdx(SS_Paddr paddr, |
| 68 | const RegisterAttribute* attributeTable, |
| 69 | int entries) |
| 70 | { |
| 71 | for (int i = 0; i < entries; i++) { |
| 72 | if (attributeTable[i].startAddr <= paddr && paddr <= attributeTable[i].endAddr) { |
| 73 | if (((paddr - attributeTable[i].startAddr) & (attributeTable[i].stride - 1)) == 0) { |
| 74 | return i; |
| 75 | } |
| 76 | } |
| 77 | } |
| 78 | return ENTRY_NOT_FOUND; |
| 79 | } |
| 80 | |
| 81 | //====================================================================== |
| 82 | //====================================================================== |
| 83 | const RegisterAttribute* const |
| 84 | SS_BaseCsr::findAttributeEntry(SS_Paddr paddr, |
| 85 | const RegisterAttribute* attributeTable, |
| 86 | int entries) |
| 87 | { |
| 88 | uint64_t ndx = findAttributeEntryNdx(paddr, attributeTable, entries); |
| 89 | return ndx == ENTRY_NOT_FOUND ? NULL: &attributeTable[ndx]; |
| 90 | } |
| 91 | |
| 92 | //====================================================================== |
| 93 | //====================================================================== |
| 94 | void |
| 95 | SS_BaseCsr::warmReset(vector<vector<RegisterValue>*>* values, |
| 96 | int nrAttributeEntries) |
| 97 | { |
| 98 | //TODO if a register has different por and warm-reset values, and |
| 99 | // the register is never referenced up to this point, such that it |
| 100 | // is not in the table yet, then we won't create a register with |
| 101 | // warm-reset value for it, the next access to the register will |
| 102 | // return its por value, which is wrong. 11/29/05 |
| 103 | for (int i = 0; i < nrAttributeEntries; i++) |
| 104 | { |
| 105 | // CSR entries, has 1-to-1 mapping with RegisterAttribute table |
| 106 | vector<vector<RegisterValue>*> nodes = values[i]; |
| 107 | for (int j = 0; j < nodes.size(); j++) |
| 108 | { |
| 109 | // each entry points to a vector of registers belong to a particulat node |
| 110 | if (nodes[j] != NULL) |
| 111 | { |
| 112 | vector<RegisterValue>& regs = *nodes[j]; |
| 113 | for (int k = 0; k < regs.size(); k++) |
| 114 | { |
| 115 | // registers, of a particular node and a particular CSR entry |
| 116 | if (regs[k].valid()) |
| 117 | { |
| 118 | if (regs[k].attribute->maskWarm == 0x0) |
| 119 | { |
| 120 | // if no warm-reset mask, just use warm-reset value |
| 121 | regs[k].set_data(regs[k].attribute->warmReset); |
| 122 | } |
| 123 | else |
| 124 | { |
| 125 | // otherwise, use warm-reset mask on current data to produce |
| 126 | // the new data. |
| 127 | regs[k].set_data(regs[k].data() & regs[k].attribute->maskWarm); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | } |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | //============================================================================= |
| 137 | // this function assumes value is always 8-byte |
| 138 | //============================================================================= |
| 139 | uint64_t |
| 140 | SS_BaseCsr::reverseByteOrder(uint64_t value, bool littleEndian) |
| 141 | { |
| 142 | if (littleEndian) |
| 143 | { |
| 144 | int size = 8; |
| 145 | uint8_t *byteArr = (uint8_t*)&value; |
| 146 | uint64_t revValue = 0; |
| 147 | uint8_t *revByteArr = (uint8_t*)&revValue; |
| 148 | |
| 149 | int revIdx = sizeof( value ) - size; |
| 150 | int topByte = sizeof( value ) - 1; |
| 151 | for ( int byte = topByte; byte > topByte - size; --byte ) |
| 152 | { |
| 153 | revByteArr[revIdx] = byteArr[byte]; |
| 154 | ++revIdx; |
| 155 | } |
| 156 | return revValue; |
| 157 | } |
| 158 | else |
| 159 | { |
| 160 | return value; |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | //============================================================================= |
| 165 | // mainly used by follow-me, allow writing into any bits except reserved ones, |
| 166 | // ignore RW1C and RW1S masking. If the data is in littleEndian, then the |
| 167 | // reserved-mask must be reversed before doing the masking. |
| 168 | //============================================================================= |
| 169 | uint64_t |
| 170 | SS_BaseCsr::forceWrite(uint64_t inputData, |
| 171 | const RegisterAttribute* attribute, |
| 172 | int access) |
| 173 | { |
| 174 | bool littleEndian = (access & MemoryTransaction::LITTLE_ENDIAN) ? true : false; |
| 175 | return (inputData & ~(reverseByteOrder(attribute->maskRSVD, littleEndian))); |
| 176 | } |
| 177 | |
| 178 | //============================================================================= |
| 179 | // normal write operation, take all masking into account. |
| 180 | // |
| 181 | // exclude_maskRW = maskRW & ~(maskRW1C | maskRW1S) |
| 182 | // new_data = (input_data & exclude_maskRW) | |
| 183 | // (input_data & maskRW1S) | |
| 184 | // (old_data & ~(exclde_maskRW | (input_data & maskRW1C))) |
| 185 | // newData = normalWrite(inputData, oldData, attribute, littleEndian); |
| 186 | //============================================================================= |
| 187 | uint64_t |
| 188 | SS_BaseCsr::normalWrite(uint64_t inputData, |
| 189 | uint64_t oldData, |
| 190 | const RegisterAttribute* attribute, |
| 191 | int access) |
| 192 | { |
| 193 | bool littleEndian = (access & MemoryTransaction::LITTLE_ENDIAN) ? true : false; |
| 194 | uint64_t maskRW_exc = attribute->maskRW & ~(attribute->maskRW1C | attribute->maskRW1S); |
| 195 | uint64_t newData = (inputData & reverseByteOrder(maskRW_exc, littleEndian)) | |
| 196 | (inputData & reverseByteOrder(attribute->maskRW1S, littleEndian)) | |
| 197 | (oldData & ~(reverseByteOrder(maskRW_exc, littleEndian) | (inputData & reverseByteOrder(attribute->maskRW1C, littleEndian)))); |
| 198 | |
| 199 | return newData; |
| 200 | } |
| 201 | |
| 202 | //============================================================================= |
| 203 | //============================================================================= |
| 204 | void |
| 205 | SS_BaseCsr::registerAddressSpace(RegisterAttribute *attributeTable, |
| 206 | uint_t tableSize, |
| 207 | const string &description) |
| 208 | { |
| 209 | //TODO this routine does not take localvs global I/O address into account |
| 210 | |
| 211 | uint64_t start = ULLONG_MAX; |
| 212 | uint64_t end = 0; |
| 213 | |
| 214 | for (uint_t tableNdx = 0; tableNdx < tableSize; ++tableNdx) { |
| 215 | if (attributeTable[tableNdx].startAddr < start) { |
| 216 | start = attributeTable[tableNdx].startAddr; |
| 217 | } |
| 218 | if (attributeTable[tableNdx].endAddr > end) { |
| 219 | // Be sure to cover the entire address range |
| 220 | end = attributeTable[tableNdx].endAddr + |
| 221 | attributeTable[tableNdx].stride - 1; |
| 222 | |
| 223 | // Die if the stride and the count don't match the |
| 224 | // endAddr |
| 225 | if (end + 1 < attributeTable[tableNdx].startAddr + |
| 226 | attributeTable[tableNdx].stride * |
| 227 | attributeTable[tableNdx].count) { |
| 228 | ostringstream os; |
| 229 | os << "SS_BaseCsr::registerAddressSpace: " << |
| 230 | "bad RegisterAttribute ranges: endAddr 0x" << |
| 231 | hex << end << |
| 232 | " startAddr 0x" << attributeTable[tableNdx].startAddr << |
| 233 | " stride " << dec << attributeTable[tableNdx].stride << |
| 234 | " count\n" << dec << attributeTable[tableNdx].count << |
| 235 | "\n"; |
| 236 | |
| 237 | fprintf(stderr, "ERROR: %s\n", os.str().c_str()); |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | #ifndef COMPILE_FOR_COSIM |
| 242 | //TODO we don't have strand pointer here, cannot access sim_state.cosim() |
| 243 | //if (!s->sim_state.cosim()) |
| 244 | SS_Io::io.add(start, end, this, SS_AddressMap::ABS, SS_BaseCsr::access); |
| 245 | #endif |
| 246 | } |
| 247 | |
| 248 | //============================================================================= |
| 249 | // when mem==NULL, a paddr that does not match any supported CSR will |
| 250 | // return INVALID value, this is mainly used by callback corresponding |
| 251 | // to asi_read. when access.getInternal()==true, it means the |
| 252 | // read64() is invoked by callback// corresponding to asi_read, the |
| 253 | // read64() will return INVALID for non-matched paddr, and it will |
| 254 | // remove a matched entry after the value is returned. |
| 255 | //============================================================================= |
| 256 | //====================================================================== |
| 257 | // THIS IS READ64 |
| 258 | //====================================================================== |
| 259 | int |
| 260 | SS_BaseCsr::read64( SS_Paddr paddr, uint64_t* data, int access, int sid ) |
| 261 | { |
| 262 | // for N2, there is only one cpu (i.e., node), so local I/O address is |
| 263 | // equal to global I/O address. |
| 264 | |
| 265 | // In general strands in the same cpu use the same global I/O address, but |
| 266 | // for CSR follow-me, each strand can have its own follow-me value, so we |
| 267 | // need strand-id in follow-me matching up. |
| 268 | |
| 269 | SS_Paddr laddr; |
| 270 | SS_Paddr gaddr; |
| 271 | if (is_global_addr(paddr)) |
| 272 | { |
| 273 | laddr = global2local(paddr); |
| 274 | gaddr = paddr; |
| 275 | } |
| 276 | else if (sid < 0) |
| 277 | { |
| 278 | // if it is not a global address, then we need a real strand-id to |
| 279 | // convert the address to global address, otherwise it is an error. |
| 280 | fprintf(stderr, "ERROR: SS_BaseCsr::read64( paddr=%#llx, sid=%d ): paddr not a global addr\n", paddr, sid); |
| 281 | *data = 0; |
| 282 | return SS_Io::NOP; |
| 283 | } |
| 284 | else |
| 285 | { |
| 286 | laddr = paddr; |
| 287 | gaddr = local2global(paddr, sid); |
| 288 | } |
| 289 | SS_Paddr baddr = addr_node0(paddr); |
| 290 | int nid = get_node_id(gaddr); |
| 291 | |
| 292 | // for any read, look at follow-me list first, if a match can be found, |
| 293 | // use it (and then discard the entry), otherwise go to real register area. |
| 294 | if ((access & SS_BaseCsr::NO_FOLLOW_ME) == 0) |
| 295 | { |
| 296 | list<FollowMeData>::iterator iter = followme_.begin(); |
| 297 | for (; iter != followme_.end(); iter++) |
| 298 | { |
| 299 | // if either side of sid is -1, then pick the first entry that has a |
| 300 | // matching addr |
| 301 | if (((*iter).addr == gaddr) && |
| 302 | (((*iter).sid == sid) || ((*iter).sid < 0) || (sid < 0))) |
| 303 | { |
| 304 | *data = (*iter).data; |
| 305 | followme_.erase(iter); |
| 306 | return SS_Io::FOLLOWME; |
| 307 | } |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | // no matching follow-me, search real CSR |
| 312 | uint64_t attributeNdx = |
| 313 | findAttributeEntryNdx(baddr, attributeTable_, nrAttributeEntries_); |
| 314 | if (attributeNdx != ENTRY_NOT_FOUND) |
| 315 | { |
| 316 | // attribute table uses node0 addr as keyword, 'value' vector has an |
| 317 | // 1-on-1 mapping with attribute table, so node0 addr has to be used. |
| 318 | const RegisterAttribute* attribute = &attributeTable_[attributeNdx]; |
| 319 | RegisterValue *value = attribute->find(baddr, values_[attributeNdx], nid); |
| 320 | if (value == NULL) |
| 321 | { |
| 322 | // this is an error |
| 323 | fprintf(stderr, "ERROR: SS_BaseCsr::read64( paddr=%#llx, sid=%d ): null RegisterValue pointer\n", paddr, sid); |
| 324 | *data = 0; |
| 325 | return SS_Io::NOP; |
| 326 | } |
| 327 | else |
| 328 | { |
| 329 | if (!value->valid()) |
| 330 | { |
| 331 | // no valid data for this entry yet, generate it |
| 332 | uint64_t init_data; |
| 333 | // first, see if the addr has a valid data in mem.image (sometimes |
| 334 | // uses this approach to provide a csr's init value other then its |
| 335 | // pre-defined por value). |
| 336 | int state = read_memimage(gaddr, laddr, &init_data); |
| 337 | init_data &= attribute->maskRSVD; |
| 338 | if (init_data == 0x0) |
| 339 | { |
| 340 | // when read_memimage() returns 0, we consider that means no data |
| 341 | // for this addr is available in mem.image, so we use the por value |
| 342 | // in CSR attribute table. |
| 343 | //TODO this can be troublesome, if a real 0x0 value is set for the |
| 344 | // addr in mem.image, then we will mistake it as not available, |
| 345 | // and use por value instead (wrongly), do we have a way of |
| 346 | // knowing whether a valid data is available in mem.image? |
| 347 | init_data = attribute->por; |
| 348 | } |
| 349 | RegisterValue newValue(baddr, init_data, attribute, sid); |
| 350 | *value = newValue; |
| 351 | } |
| 352 | *data = value->data(); |
| 353 | } |
| 354 | } |
| 355 | else |
| 356 | { |
| 357 | // not in csr attribute list, use global addr for non-csr |
| 358 | return read64_notCsr( gaddr, laddr, data ); |
| 359 | } |
| 360 | return SS_Io::OK; |
| 361 | } |
| 362 | |
| 363 | //============================================================================= |
| 364 | // read64_notCsr() reads any non-CSR address |
| 365 | //============================================================================= |
| 366 | int |
| 367 | SS_BaseCsr::read64_notCsr( SS_Paddr gaddr, SS_Paddr laddr, uint64_t* data ) |
| 368 | { |
| 369 | // for addr not in csr list, just read it from memory |
| 370 | return read_memimage(gaddr, laddr, data); |
| 371 | } |
| 372 | |
| 373 | //============================================================================= |
| 374 | // when mem==NULL, a paddr that does not match any supported CSR will |
| 375 | // be silently dropped, this is mainly used by asi_read function. |
| 376 | // when access.getInternal()==true, it means allwoing write to RO |
| 377 | // non-reserved field(s) of a register, this is to mimic hardware |
| 378 | // writing to RO non-reserved field(s). |
| 379 | //============================================================================= |
| 380 | int |
| 381 | SS_BaseCsr::write64( SS_Paddr paddr, uint64_t data, int access, int sid ) |
| 382 | { |
| 383 | SS_Paddr laddr; |
| 384 | SS_Paddr gaddr; |
| 385 | if (is_global_addr(paddr)) |
| 386 | { |
| 387 | laddr = global2local(paddr); |
| 388 | gaddr = paddr; |
| 389 | } |
| 390 | else if (sid < 0) |
| 391 | { |
| 392 | // if it is not a global address, then we need a real strand-id to |
| 393 | // convert the address to global address, otherwise it is an error. |
| 394 | fprintf(stderr, "ERROR: SS_BaseCsr::write64( paddr=%#llx, sid=%d ): paddr not a global addr\n", paddr, sid); |
| 395 | return SS_Io::NOP; |
| 396 | } |
| 397 | else |
| 398 | { |
| 399 | laddr = paddr; |
| 400 | gaddr = local2global(paddr, sid); |
| 401 | } |
| 402 | SS_Paddr baddr = addr_node0(paddr); |
| 403 | int nid = get_node_id(gaddr); |
| 404 | |
| 405 | if (access & MemoryTransaction::FOLLOW_ME) |
| 406 | { |
| 407 | // this is a follow-me write, keep the value in followme_ list |
| 408 | FollowMeData fm(gaddr, data, sid); |
| 409 | followme_.push_back(fm); |
| 410 | return SS_Io::OK; |
| 411 | } |
| 412 | else |
| 413 | { |
| 414 | uint64_t attributeNdx = |
| 415 | findAttributeEntryNdx(baddr, attributeTable_, nrAttributeEntries_); |
| 416 | if (attributeNdx != ENTRY_NOT_FOUND) |
| 417 | { |
| 418 | const RegisterAttribute* attribute = &attributeTable_[attributeNdx]; |
| 419 | RegisterValue *value = attribute->find(baddr, values_[attributeNdx], nid); |
| 420 | if (value == NULL) |
| 421 | { |
| 422 | // this is an error |
| 423 | fprintf(stderr, "ERROR: SS_BaseCsr::write64( paddr=%#llx, sid=%d ): null RegisterValue pointer\n", paddr, sid); |
| 424 | return SS_Io::NOP; |
| 425 | } |
| 426 | else |
| 427 | { |
| 428 | uint64_t new_data; |
| 429 | uint64_t old_data = attribute->por; |
| 430 | if (value->valid()) |
| 431 | { |
| 432 | old_data = value->data(); |
| 433 | } |
| 434 | if ((access & MemoryTransaction::INTERNAL) || |
| 435 | (access & MemoryTransaction::MEM_SLAM)) |
| 436 | { |
| 437 | // allow writing into any non-reserved bits |
| 438 | new_data = forceWrite(data, attribute, access); |
| 439 | } |
| 440 | else |
| 441 | { |
| 442 | if (attribute->protect != RegisterAttribute::RO) |
| 443 | { |
| 444 | // can only write into W bits (e.g., WO, RW, RW1C, RW1S, etc) |
| 445 | new_data = normalWrite(data, old_data, attribute, access); |
| 446 | } |
| 447 | else |
| 448 | { |
| 449 | fprintf(stderr, "WARNING: %s::write64(): paddr=%#llx (gaddr=%#llx) is read-only, data=%#llx is NOT committed\n", className_.c_str(), paddr, gaddr, data); |
| 450 | return SS_Io::NOP; |
| 451 | } |
| 452 | } |
| 453 | if (!value->valid()) |
| 454 | { |
| 455 | // the entry is not available yet, create it |
| 456 | RegisterValue newValue(baddr, new_data, attribute, sid); |
| 457 | *value = newValue; |
| 458 | } |
| 459 | else |
| 460 | { |
| 461 | value->set_data(new_data); |
| 462 | } |
| 463 | } |
| 464 | } |
| 465 | else |
| 466 | { |
| 467 | // paddr not in csr list |
| 468 | return write64_notCsr( gaddr, data ); |
| 469 | } |
| 470 | } |
| 471 | return SS_Io::OK; |
| 472 | } |
| 473 | |
| 474 | //============================================================================= |
| 475 | // write64_notCsr() reads any non-CSR address |
| 476 | //============================================================================= |
| 477 | int |
| 478 | SS_BaseCsr::write64_notCsr( SS_Paddr gaddr, uint64_t data ) |
| 479 | { |
| 480 | // for addr not in csr list, just write it to memory using global-addr |
| 481 | #ifndef COMPILE_FOR_SAM |
| 482 | SS_Memory::memory.poke64(gaddr, data); |
| 483 | return SS_Io::OK; |
| 484 | #else |
| 485 | extern SS_Memory *mm1; |
| 486 | mm1->st64(gaddr, data); |
| 487 | return SS_Io::OK; |
| 488 | #endif |
| 489 | } |
| 490 | |
| 491 | //============================================================================= |
| 492 | // even if an addr in question is in CSR list, a diag may have the addr's |
| 493 | // initial value in mem.image, it can use either local or global addr, so |
| 494 | // look for those first. Always start with global addr, as that is specific |
| 495 | // to each node, local addr is shared among nodes. If none is found, then use |
| 496 | // the pre-defined por value (if one is available). (yes, it is messy, but |
| 497 | // verification diag wants the flexibility). |
| 498 | //============================================================================= |
| 499 | int |
| 500 | SS_BaseCsr::read_memimage( SS_Paddr gaddr, SS_Paddr laddr, uint64_t* data ) |
| 501 | { |
| 502 | #ifndef COMPILE_FOR_SAM |
| 503 | *data = SS_Memory::memory.peek64(gaddr); |
| 504 | #else |
| 505 | extern SS_Memory *mm1; |
| 506 | *data = mm1->ld64(gaddr); |
| 507 | #endif |
| 508 | if ((*data == 0x0) && (gaddr != laddr)) |
| 509 | { |
| 510 | // if not match with global addr, try local addr |
| 511 | //TODO this is not perfect, returned value of 0x0 can be |
| 512 | // (1) the addr is not in mem.image, (2) the addr is in |
| 513 | // mem.image with value 0x0. In here we assume 0x0 means |
| 514 | // not in mem.image. |
| 515 | #ifndef COMPILE_FOR_SAM |
| 516 | *data = SS_Memory::memory.peek64(laddr); |
| 517 | #else |
| 518 | extern SS_Memory *mm1; |
| 519 | *data = mm1->ld64(laddr); |
| 520 | #endif |
| 521 | } |
| 522 | return SS_Io::OK; |
| 523 | } |