Commit | Line | Data |
---|---|---|
920dae64 AT |
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 | } |