// ========== Copyright Header Begin ==========================================
// OpenSPARC T2 Processor File: ext_sys_mem.cc
// Copyright (C) 1995-2007 Sun Microsystems, Inc. All Rights Reserved
// 4150 Network Circle, Santa Clara, California 95054, U.S.A.
// * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 2 of the License.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// For the avoidance of doubt, and except that if any non-GPL license
// choice is available it will apply instead, Sun elects to use only
// the General Public License version 2 (GPLv2) at this time for any
// software where a choice of GPL license versions is made
// available with the language indicating that GPLv2 or any later version
// may be used, or where a choice of which version of the GPL is applied is
// otherwise unspecified.
// Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
// CA 95054 USA or visit www.sun.com if you need additional information or
// ========== Copyright Header End ============================================
// Copyright 2001 ADVANCED MICRO DEVICES, INC. All Rights Reserved.
// This software and any related documentation (the "Materials") are the
// confidential proprietary information of AMD. Unless otherwise provided
// in an agreement specifically licensing the Materials, the Materials are
// provided in confidence and may not to be used, distributed, modified, or
// reproduced in whole or in part by any means.
// LIMITATION OF LIABILITY: THE MATERIALS ARE PROVIDED "AS IS" WITHOUT ANY
// EXPRESS OR IMPLIED WARRANTY OF ANY KIND, INCLUDING BUT NOT LIMITED TO
// WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT, TITLE, FITNESS FOR ANY
// PARTICULAR PURPOSE, OR WARRANTIES ARISING FORM CONDUCT, COURSE OF
// DEALING, OR USAGE OF TRADE. IN NO EVENT SHALL AMD OR ITS LICENSORS BE
// LIABLE FOR ANY DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION,
// DAMAGES FOR LOSS OF PROFITS, BUSINESS INTERRUPTION, OR LOSS OF
// INFORMATION) ARISING OUT OF THE USE OF OR INABILITY TO USE THE
// MATERIALS, EVEN IF AMD HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGES. BECAUSE SOME JURISDICTIONS PROHIBIT THE EXCLUSION OR
// LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE
// ABOVE LIMITATION MAY NOT APPLY TO YOU.
// AMD does not assume any responsibility for any errors which may appear
// in the Materials nor any responsibility to support or update the
// Materials. AMD retains the right to modify the Materials at any time,
// without notice, and is not obligated to provide such modified Materials
// NO SUPPORT OBLIGATION: AMD is not obligated to furnish, support, or make
// any further information, software, technical information, know-how, or
// show-how available to you.
// U.S. GOVERNMENT RESTRICTED RIGHTS: The Materials are provided with
// "RESTRICTED RIGHTS." Use, duplication, or disclosure by the Government
// is subject to the restrictions as set forth in FAR 52.227-14 and DFAR
// 252.227-7013, et seq., or its successor. Use of the Materials by the
// Government constitutes acknowledgement of AMD's proprietary rights in
////////////////////////////////////////////////////////////////////////////////
/*****************************************************************************
* $RCSfile: ext_sys_mem.cc,v $
* $Date: 2008/02/21 09:51:16 $
* Copyright (c) 1997 by Advanced Micro Devices, Inc.
* This file is protected by Federal Copyright Law, with all rights
* reserved. No part of this file may be reproduced, stored in a
* retrieval system, translated, transcribed, or transmitted, in any
* form, or by any means manual, electric, electronic, mechanical,
* electro-magnetic, chemical, optical, or otherwise, without prior
* explicit written permission from Advanced Micro Devices, Inc.
****************************************************************************/
////////////////////////////////////////////////////////////////////////////////
// Author: Ron Herzer x52085
// Implements a 64-Gig memory with 64-bit reads, and 64-bit writes that are
// byte-wise granular. Dynamically allocates memory in 512 byte chunks
// Uses a 1024 entry hash table for first lookup step for quick access.
// Uses binary tree for storing multiple hits to the same hash table index.
// Keeps track of last tree node hit for each hash table index for
// quick subsequent accesses to the same chunk.
////////////////////////////////////////////////////////////////////////////////
// Constructor clears all valid bits in hash table.
for (i
=0; i
<SYS_HTBL_SIZE
; i
++) htable
[i
].valid
=0;
void SYS_MEM::SetDefaultMemRandom() {
void SYS_MEM::SetDefaultMemVal(uint val
) {
io_printf("SYS_MEM::Setting default memory to 0x%02x\n", val
);
void SYS_MEM::dump (char *FileName
) {
HTABLE_ENTRY
*K7MemTable
;
HTABLE_ENTRY
*NextTableEntry
;
if (0==strcmp(FileName
, "-")) {
memfile
= fopen(FileName
, "w");
io_printf("SYS_MEM::error - unable to open output file: %s", FileName
);
io_printf("SYS_MEM::dump dumping memory to %s\n", FileName
);
K7MemTable
= (HTABLE_ENTRY
*) ReturnHTable();
for (int idx
= 0; idx
< SYS_HTBL_SIZE
; idx
++) {
NextTableEntry
= (HTABLE_ENTRY
*) ((int)(K7MemTable
) + (idx
*(sizeof(HTABLE_ENTRY
))));
if (NextTableEntry
->valid
) {
CurK7Block
= NextTableEntry
->headPtr
;
dumpMemTree(memfile
, CurK7Block
, idx
);
void SYS_MEM::dumpMemTree(FILE *memfile
, MEMCHUNK
*K7Block
, int Index
) {
dumpMemTree(memfile
, K7Block
->right
, Index
);
dumpMemTree(memfile
, K7Block
->left
, Index
);
AddrLo
= uint(K7Block
->tag
& 0xffffffffllu
) | ((Index
& 0x3ff) << 9);
AddrHi
= uint(K7Block
->tag
>> 32);
fprintf(memfile
, "@%02x_%08x\n", AddrHi
, AddrLo
);
fprintf(memfile
, "@%08x\n", AddrLo
);
for(int idx
= 0; idx
< SYS_CHUNK_SIZE
; idx
++) {
PrintNice
= (idx
+ 1) % 16;
K7Data
= read(AddrHi
, AddrLo
+idx
);
if (PrintNice
&& (idx
< (SYS_CHUNK_SIZE
- 1)))
fprintf(memfile
, "%02x ", (K7Data
& 0xff));
fprintf(memfile
, "%02x\n", (K7Data
& 0xff));
// ***************************************************************************
// Get upto a 64 bit address from the given string (Note: '_' are
// allowed as delimiters within the address field).
// ***************************************************************************
int SYS_MEM::GetAddressFromString(char *InputBuffer
, unsigned int *Addr
) {
static char AddrBufferHi
[128]; // May hold 64bit address + delimiters
static char AddrBufferLo
[8+1]; // Only ever hold 4GB of address
bool FoundNonZero
= false;
char *destptr
= AddrBufferHi
; // strip out '_'s, strip off leading '0's
for (char *srcptr
= InputBuffer
; srcptr
&& *srcptr
; srcptr
++) {
case '0': if (!FoundNonZero
) continue; break;
default: FoundNonZero
= true; break;
if (!isxdigit(*srcptr
)) retVal
= false;
assert(strlen(AddrBufferHi
) <= 16);
if (strlen(AddrBufferHi
) <= 8) { // defined AddrHi, AddrLo
strcpy(AddrBufferLo
, strlen(AddrBufferHi
) ? AddrBufferHi
: "0"); // Were only zeroes there?
strcpy(AddrBufferHi
, "0");
strcpy(AddrBufferLo
, AddrBufferHi
+ (strlen(AddrBufferHi
) - 8));
*(AddrBufferHi
+ (strlen(AddrBufferHi
) - 8)) = '\0';
retVal
&= (sscanf(AddrBufferHi
, "%x", Addr
+ 1)) ? true : false;
retVal
&= (sscanf(AddrBufferLo
, "%x", Addr
)) ? true : false;
// Load memory from .vram or .sim file.
// Should only be called once at the beginning of simulation.
// There is no function to deallocate any existing chunks.
// ***************************************************************************
void SYS_MEM::load (FILE *memfile
, char *Filename
,
long long unsigned int addrLimitLow
, long long unsigned int addrLimitHigh
) {
const uint bufferSize
= 0x100;
char line
[bufferSize
<< 1]; // buffer is 2x since worst case bufferSize chars could be sneaked
char *filename
= const_cast<char *>(Filename
? Filename
: "<RAM FILE>");
uint CommentActive
= 0x0; // Needed for comments that exceed the bufferSize
// 0x1 - Starting this line
// 0x4 - Ending this line
dword address
[2] = {0,0}; // assumes address starts at 0 if first @address not specified.
static uint ErrorCnt
= 0; // Retain the knowledge that were already dead if multiple images are loaded
uint ErrorCntLimit
= 0xffffffff;
uint linechkDone
= 0, linechkErr
;
uint FoundAddr
= ErrorCnt
; // Only flag first instance (if there's another error)...
uint FoundData
= ErrorCnt
; // Only flag first instance (if there's another error)...
rewind (memfile
); // Rewind the file in case this was previously loaded
// load data in vram or sim file. - memfile should always be valid
io_printf("SYS_MEM load error! Could not open memory file, %s, for reading.\n", filename
);
// specifically check for NULL characters since some sim files
// (randomly generated) have been noted to have NULL in file
for (char prechk
= fgetc(memfile
); (prechk
!= EOF
) && !Done
; prechk
= fgetc(memfile
)) {
io_printf("SYS_MEM::error - .sim load error: Found Null character in memory file, %s, at position: %u\n",
filename
, ftell(memfile
));
if (ErrorCnt
>= ErrorCntLimit
)
rewind(memfile
); // Reset the file pointer
while ((fgets(line
, bufferSize
, memfile
)) && !Done
) {
if (SkipOverFlow
&& (CommentActive
& 0x1))
CommentActive
= 0; // This was a comment that started and ended on the same line...
lptr
= line
; // pointer to line
// Initialize here so overFlow is done once per buffer read...
for (chkptr
= line
; chkptr
&& *chkptr
; chkptr
++) {
if (chkptr
&& (overFlow
== '\n'))
CommentActive
= 0; // Continued comment ended last line
CommentActive
= 0x4; // Continuing comment ending this line
else if (CommentActive
& 0x3)
CommentActive
= 0x2; // Comment continuing
if (!(CommentActive
& 0x6)) {
for (chkptr
= line
; chkptr
&& *chkptr
&& !linechkDone
; chkptr
++) {
if (!(*(chkptr
+ 1))) linechkErr
= 1; // not quite a Comment
else if (*(chkptr
+ 1) == '/') {
linechkDone
= 1; // Comment for rest of line...
// In general, overFlowCount should never get larger than one since a byte will typically be
// split -- don't forget about the possibility of a split address.
while (*line
&& (overFlowCount
< ((bufferSize
<< 1)-1)) && (!isspace(line
[overFlowCount
]))) {
overFlow
= fgetc(memfile
);
//io_printf("Found line[%u] '%c', Line %4u sneaking char '%c' from stream",
// overFlowCount, line[overFlowCount], LineNum, overFlow);
else if (overFlow
== '\n') {
ungetc(overFlow
, memfile
);
line
[overFlowCount
] = overFlow
;
if (!overFlow
) // Null terminates string and overflow search...
overFlowCount
= (bufferSize
<< 1);
assert(overFlowCount
< (bufferSize
<< 1));
line
[overFlowCount
+1] = '\0';
|| (*chkptr
== '_') // Allow '_' delimiters in addresses...
io_printf("SYS_MEM::error - .sim load error: found illegal character '%c' in non-commented fields on line %u.\n",*chkptr
, LineNum
);
if (ErrorCnt
>= ErrorCntLimit
)
linechkDone
= 1; // already exceeded error limit
io_printf("Line %4u is a ending comment -%s-\n", LineNum
-1, line
);
else if (CommentActive
& 0x2)
io_printf("Line %4u is a continuing comment -%s-\n", LineNum
, line
);
else if (CommentActive
& 0x1)
io_printf("Line %4u is a new comment -%s-\n", LineNum
, line
);
io_printf("Line %4u is not a comment -%s-\n", LineNum
, line
);
while ((lptr
&& *lptr
&& (*lptr
!= '\n') && (sscanf(lptr
,"%s",word
)==1)) && !Done
)
{ // get the next word on the line
if (ErrorCnt
>= ErrorCntLimit
) {
io_printf("SYS_MEM::Error Limit exceeded: run with --assertions_warnings_only if a complete list is needed\n");
// skip comment lines (or comments at end of line) -- Note: already checked that '/' equates to '//'
if (!(CommentActive
& 0x6) && (*wptr
!= '/')) {
if (*wptr
=='@') { // found new address
if (!GetAddressFromString(wptr
, address
)) {
io_printf("SYS_MEM::Error while reading .sim file, %s:%u, unable to determine address from %s\n",
filename
, LineNum
, wptr
);
if (!sscanf(wptr
,"%x",&dat
)) {
io_printf("SYS_MEM:: .sim load error: At %x%08x, Data '%s' is not a hex number at %s:%u\n",
address
[1], address
[0], wptr
, filename
, LineNum
);
io_printf("SYS_MEM:: .sim load error: Detected byte data '0x%02x' at %s:%u\n",
data1
= dat
<<8*3&0xff000000 | dat
<<8*2&0xff0000 | dat
<<8*1&0xff00 | dat
&0xff;
// decode byte enables & write byte to memory
be
=0x1<<((address
[0])&0x7);
long long unsigned int addressLong
= ((long long unsigned int)(address
[1]) << 32) | (long long unsigned int)address
[0];
if (!(addrLimitLow
|| addrLimitHigh
) || // Limits not enabled
((addrLimitLow
<= addressLong
) && (addressLong
<= addrLimitHigh
))) {
// printf("load %s: Addr: %02x_%08x (%02x) %02x\n", filename, address[1], address[0], (be & 0x000000ff), data0);
write(address
[1],address
[0],be
,data1
,data0
);
if (!address
[0]) address
[1]++; // increment addr[63:32] on wrap around of addr[31:0].
else lptr
=NULL
; // forget everything after comment
while(lptr
&& (*lptr
!= '\0') && isspace(*lptr
)) lptr
++; // skip whitespace within string
// lptr=strstr(lptr," "); // note: returns null if not found
while(lptr
&& (*lptr
!= '\0') && !isspace(*lptr
)) lptr
++; // skip whitespace
io_printf("SYS_MEM::No address specifier in .sim file, %s. The assembler/user must know exactly what's going on...\n", filename
);
io_printf("SYS_MEM:: No data bytes found in .sim file, %s\n", filename
);
//Rc->Finish(); // Stop (this will be at the beginning of time...
// ***************************************************************************
// Specific check for NULL characters since some sim files (randomly generated
// ones) have been noted to have NULL characters in the file.
// ***************************************************************************
int SYS_MEM::PreCheckError(FILE *memfile
, char *filename
) {
rewind (memfile
); // Rewind the file in case this was previously loaded
for (char prechk
= fgetc(memfile
); (prechk
!= EOF
) && !Done
; prechk
= fgetc(memfile
)) {
io_printf("SYS_MEM:: .sim load error: Found Null character in memory file, %s, at position: %u\n",
filename
, ftell(memfile
));
rewind(memfile
); // Reset the file pointer
// ***************************************************************************
// Maximum address size = 64 bits. Aligned to QW boundary. (A[2:0] not used)
// Always returns a QW in {data1,data0}.
void SYS_MEM::read (dword addrh
, dword addrl
, dword
*data1
, dword
*data0
)
long long unsigned int addr
;
long long unsigned int tag
;
addr
= (((long long unsigned int)addrh
)<<32) | addrl
;
tag
= addr
& ~0x7ffffllu
; // tag = addr[63:SYS_CHUNK_BITS]
// extract hash table index addr[18:9], and chunk offset addr[8:3]:
offset
= addrl
>>2 & 0x7e; // note: zero out bit 2
// see if valid entry exists in hash table:
// look for tag match (first check most recent access):
if (htable
[idx
].mraPtr
->tag
==tag
) { // most common case!
*data1
=htable
[idx
].mraPtr
->data
[offset
+1];
*data0
=htable
[idx
].mraPtr
->data
[offset
];
} else { // find chunk in tree
current
= htable
[idx
].headPtr
;
while (current
&& current
->tag
!=tag
) {
if (tag
> current
->tag
) {
current
= current
->right
;
*data1
=current
->data
[offset
+1];
*data0
=current
->data
[offset
];
htable
[idx
].mraPtr
=current
;
} else { // not in chunk tree
_RandomClrMemVal1
= randh
| randl
;
*data1
= _RandomClrMemVal1
;
_RandomClrMemVal0
= randh
| randl
;
*data0
= _RandomClrMemVal0
;
// create memory location with this random data
write(addrh
,addrl
,0xff,_RandomClrMemVal1
,_RandomClrMemVal0
);
} else { // hash table entry not valid
_RandomClrMemVal1
= randh
| randl
;
*data1
= _RandomClrMemVal1
;
_RandomClrMemVal0
= randh
| randl
;
*data0
= _RandomClrMemVal0
;
// create memory location with this random data
write(addrh
,addrl
,0xff,_RandomClrMemVal1
,_RandomClrMemVal0
);
// ***************************************************************************
// Memory Read QW - long long version
// Maximum address size = 64 bits. Aligned to DW boundary. (A[1:0] not used)
// Always returns a QW in {data}.
long long unsigned int SYS_MEM::read (long long unsigned int addr
, long long unsigned int *data
) {
uint addr1
= uint(addr
>> 32);
uint addr0
= uint(addr
& 0xffffffff);
read (addr1
, addr0
, &data1
, &data0
);
*data
= ((long long unsigned int)(data1
) << 32) | (long long unsigned int)data0
;
// ***************************************************************************
// Maximum address size = 64 bits. Aligned to DW boundary. (A[1:0] not used)
// Always returns a DW in {data}.
uint
SYS_MEM::read (dword addrh
, dword addr
, dword
*data
) {
read (addrh
, addr
, &data1
, &data0
);
if (addr
& 0x4) *data
= data1
;
// ***************************************************************************
// Maximum address size = 64 bits.
unsigned char SYS_MEM::read (dword addrh
, dword addr
)
read (addrh
, addr
, &data
);
data
= (data
>> ((addr
& 0x3)*8)) & 0xff;
return ((unsigned char) (data
));
// ***************************************************************************
// Memory Read - arbitrary Size
unsigned char * SYS_MEM::read (dword addrh
, dword addr
, uint Size
) {
unsigned char * retVal
= NULL
;
io_printf("Error: Zero size passed to SYS_MEM::read(...)\n");
retVal
= new unsigned char [Size
];
for (unsigned int idx
= 0; idx
< Size
; idx
++)
for (uint idx
= 0; idx
< Size
; idx
++) {
retVal
[idx
] = read(addrh
, addr
);
if (addr
== ~uint(0x0)) // Generate carry for upper address
// ***************************************************************************
// Maximum address size = 64 bits. Aligned to DW boundary. (A[1:0] not used)
uint
SYS_MEM::readBit (dword addrh
, dword addr
, dword bitField
) {
//if (!bitField) Rc->Assertion(bitField, "bitField must be defined in readBit()\n");
read (addrh
, addr
, &retVal
);
return (retVal
& bitField
);
// ***************************************************************************
void SYS_MEM::write (dword addrh
, dword addr
, byte data
) {
byte be
= 0x1 << (addr
& 0x3);
write(addrh
, (addr
& ~0x3), be
, dword(data
));
// ***************************************************************************
// Memory Write QW - long long version
// Aligned to QW boundary. (A[2:0] not used). Always uses a QW in {data}.
void SYS_MEM::write (long long unsigned int addr
, byte be
, long long unsigned int data
) {
uint addr1
= uint(addr
>> 32);
uint addr0
= uint(addr
& 0xffffffff);
uint data1
= uint(data
>> 32);
uint data0
= uint(data
& 0xffffffff);
write (addr1
, addr0
, be
, data1
, data0
);
// ***************************************************************************
// Aligned to DW boundary. (A[1:0] not used). Always uses a DW in {data}.
void SYS_MEM::write (dword addrh
, dword addr
, byte be
, dword data
) {
printf("SYS_MEM::write(dWord), inappropriate byte enables (%u) sent\n", be
);
write (addrh
, addr
, be
, data
, data
);
// ***************************************************************************
// Maximum address size = 64 bits. Aligned to QW boundary. (A[2:0] not used)
// Uses the QW in {data1,data0} and updates bytes whose byte enable (be[7:0]) is set.
void SYS_MEM::write (dword addrh
, dword addrl
, byte be
, dword data1
, dword data0
)
long long unsigned int addr
;
long long unsigned int tag
;
byte byte7
,byte6
,byte5
,byte4
,byte3
,byte2
,byte1
,byte0
;
addr
= (((long long unsigned int)addrh
)<<32) | addrl
;
tag
= addr
& ~0x7ffffllu
; // tag = addr[63:SYS_CHUNK_BITS]
// extract hash table index addr[18:9], and chunk offset addr[8:3]:
offset
= addrl
>>2 & 0x7e; // note: zero out bit 2
// see if valid entry exists in hash table:
// look for tag match (first check most recent access):
if (htable
[idx
].mraPtr
->tag
==tag
) { // most common case!
current
=htable
[idx
].mraPtr
;
else { // find chunk in tree
current
= htable
[idx
].headPtr
;
while(current
&& current
->tag
!=tag
&& cont
) {
if (tag
>current
->tag
&& current
->right
) { current
= current
->right
; }
else if (tag
<=current
->tag
&& current
->left
) { current
= current
->left
; }
if (current
&& current
->tag
!=tag
) { // not in chunk tree
// add entry to chunk tree
current
->right
= (MEMCHUNK
*) malloc(sizeof(MEMCHUNK
));
if (current
) { current
=current
->right
; added_chunk
=1;}
current
->left
= (MEMCHUNK
*) malloc(sizeof(MEMCHUNK
));
if (current
) { current
=current
->left
; added_chunk
=1;}
else { // hash table entry not valid
// add entry into hash table
htable
[idx
].headPtr
= (MEMCHUNK
*) malloc(sizeof(MEMCHUNK
));
current
=htable
[idx
].headPtr
;
if (current
) { // write data
for (i
=0; i
<SYS_CHUNK_SIZE
/4; i
++) {
_RandomClrMemVal
= randh
| randl
;
current
->data
[i
] = _RandomClrMemVal
;
current
->data
[i
] = _ClrMemVal
;
temp1
=current
->data
[offset
+1];
temp0
=current
->data
[offset
];
byte7
= (be
&0x80) ? (data1
>>3*8) : (temp1
>>3*8);
byte6
= (be
&0x40) ? (data1
>>2*8) : (temp1
>>2*8);
byte5
= (be
&0x20) ? (data1
>>1*8) : (temp1
>>1*8);
byte4
= (be
&0x10) ? (data1
) : (temp1
);
byte3
= (be
&0x08) ? (data0
>>3*8) : (temp0
>>3*8);
byte2
= (be
&0x04) ? (data0
>>2*8) : (temp0
>>2*8);
byte1
= (be
&0x02) ? (data0
>>1*8) : (temp0
>>1*8);
byte0
= (be
&0x01) ? (data0
) : (temp0
);
current
->data
[offset
+1]= byte7
<<3*8&0xff000000 | byte6
<<2*8&0xff0000 | byte5
<<1*8&0xff00 | byte4
&0xff;
current
->data
[offset
] = byte3
<<3*8&0xff000000 | byte2
<<2*8&0xff0000 | byte1
<<1*8&0xff00 | byte0
&0xff;
htable
[idx
].mraPtr
=current
;
else io_printf("SYS_MEM write error! Could not allocate memory...\n");