class _SparseMemory( object ): _immutable_fields_ = [ "BlockMemory", "block_size", "addr_mask", "block_mask" ] def __init__( self, BlockMemory, block_size=2**10 ): self.BlockMemory = BlockMemory self.block_size = block_size self.addr_mask = block_size - 1 self.block_mask = 0xffffffff ^ self.addr_mask self.debug = Debug() print "sparse memory size %x addr mask %x block mask %x" \ % ( self.block_size, self.addr_mask, self.block_mask ) #blocks = [] self.block_dict = {} def add_block( self, block_addr ): #print "adding block: %x" % block_addr self.block_dict[ block_addr ] = self.BlockMemory( size=self.block_size ) @elidable def get_block_mem( self, block_addr ): #block_idx = block_dict[ if block_addr not in self.block_dict: self.add_block( block_addr ) block_mem = self.block_dict[ block_addr ] return block_mem @elidable def iread( self, start_addr, num_bytes ): start_addr = hint( start_addr, promote=True ) num_bytes = hint( num_bytes, promote=True ) block_addr = self.block_mask & start_addr block_mem = self.get_block_mem( block_addr ) return block_mem.iread( start_addr & self.addr_mask, num_bytes ) def read( self, start_addr, num_bytes ): if self.debug.enabled( "mem" ): print ':: RD.MEM[%s] = ' % pad_hex( start_addr ), block_addr = self.block_mask & start_addr block_addr = hint( block_addr, promote=True ) block_mem = self.get_block_mem( block_addr ) value = block_mem.read( start_addr & self.addr_mask, num_bytes ) if self.debug.enabled( "mem" ): print '%s' % pad_hex( value ), return value def write( self, start_addr, num_bytes, value ): if self.debug.enabled( "mem" ): print ':: WR.MEM[%s] = %s' % ( pad_hex( start_addr ), pad_hex( value ) ), block_addr = self.block_mask & start_addr block_addr = hint( block_addr, promote=True ) block_mem = self.get_block_mem( block_addr ) block_mem.write( start_addr & self.addr_mask, num_bytes, value )
class _ByteMemory( object ): def __init__( self, data=None, size=2**10 ): self.data = data if data else [' '] * size self.size = len( self.data ) self.debug = Debug() def bounds_check( self, addr ): # check if the accessed data is larger than the memory size if addr > self.size: print "WARNING: accessing larger address than memory size. " + \ "addr=%s size=%s" % ( pad_hex( addr ), pad_hex( self.size ) ) if addr == 0: print "WARNING: writing null pointer!" raise Exception() @unroll_safe def read( self, start_addr, num_bytes ): if self.debug.enabled( "memcheck" ): self.bounds_check( start_addr ) value = 0 if self.debug.enabled( "mem" ): print ':: RD.MEM[%s] = ' % pad_hex( start_addr ), for i in range( num_bytes-1, -1, -1 ): value = value << 8 value = value | ord( self.data[ start_addr + i ] ) if self.debug.enabled( "mem" ): print '%s' % pad_hex( value ), return value # this is instruction read, which is otherwise identical to read. The # only difference is the elidable annotation, which we assume the # instructions are not modified (no side effects, assumes the addresses # correspond to the same instructions) @elidable def iread( self, start_addr, num_bytes ): value = 0 for i in range( num_bytes-1, -1, -1 ): value = value << 8 value = value | ord( self.data[ start_addr + i ] ) return value @unroll_safe def write( self, start_addr, num_bytes, value ): if self.debug.enabled( "memcheck" ): self.bounds_check( start_addr ) if self.debug.enabled( "mem" ): print ':: WR.MEM[%s] = %s' % ( pad_hex( start_addr ), pad_hex( value ) ), for i in range( num_bytes ): self.data[ start_addr + i ] = chr(value & 0xFF) value = value >> 8
class RegisterFile( object ): def __init__( self, constant_zero=True, num_regs=32, nbits=32 ): self.num_regs = num_regs self.regs = [ r_uint(0) ] * self.num_regs self.debug = Debug() self.nbits = nbits self.debug_nchars = nbits / 4 if constant_zero: self._setitemimpl = self._set_item_const_zero else: self._setitemimpl = self._set_item def __getitem__( self, idx ): if self.debug.enabled( "rf" ): print ':: RD.RF[%s] = %s' % ( pad( "%d" % idx, 2 ), pad_hex( self.regs[idx], len=self.debug_nchars ) ), return self.regs[idx] @specialize.argtype(2) def __setitem__( self, idx, value ): value = r_uint( value ) self._setitemimpl( idx, value ) def _set_item( self, idx, value ): self.regs[idx] = value if self.debug.enabled( "rf" ): print ':: WR.RF[%s] = %s' % ( pad( "%d" % idx, 2 ), pad_hex( self.regs[idx], len=self.debug_nchars ) ), def _set_item_const_zero( self, idx, value ): if idx != 0: self.regs[idx] = value if self.debug.enabled( "rf" ): print ':: WR.RF[%s] = %s' % ( pad( "%d" % idx, 2 ), pad_hex( self.regs[idx], len=self.debug_nchars ) ), #----------------------------------------------------------------------- # print_regs #----------------------------------------------------------------------- # prints all registers (register dump) # per_row specifies the number of registers to display per row def print_regs( self, per_row=6 ): for c in xrange( 0, self.num_regs, per_row ): str = "" for r in xrange( c, min( self.num_regs, c+per_row ) ): str += "%s:%s " % ( pad( "%d" % r, 2 ), pad_hex( self.regs[r], len=(self.nbits/4) ) ) print str
class _ByteMemory(object): def __init__(self, data=None, size=2**10, suppress_debug=False): self.data = data if data else [' '] * size self.size = len(self.data) self.debug = Debug() self.suppress_debug = suppress_debug def bounds_check(self, addr): # check if the accessed data is larger than the memory size if addr > self.size: print "WARNING: accessing larger address than memory size. " + \ "addr=%s size=%s" % ( pad_hex( addr ), pad_hex( self.size ) ) if addr == 0: print "WARNING: writing null pointer!" raise Exception() @unroll_safe def read(self, start_addr, num_bytes): if self.debug.enabled("memcheck") and not self.suppress_debug: self.bounds_check(start_addr) value = 0 if self.debug.enabled("mem") and not self.suppress_debug: print ':: RD.MEM[%s] = ' % pad_hex(start_addr), for i in range(num_bytes - 1, -1, -1): value = value << 8 value = value | ord(self.data[start_addr + i]) if self.debug.enabled("mem") and not self.suppress_debug: print '%s' % pad_hex(value), return value # this is instruction read, which is otherwise identical to read. The # only difference is the elidable annotation, which we assume the # instructions are not modified (no side effects, assumes the addresses # correspond to the same instructions) @elidable def iread(self, start_addr, num_bytes): value = 0 for i in range(num_bytes - 1, -1, -1): value = value << 8 value = value | ord(self.data[start_addr + i]) return value @unroll_safe def write(self, start_addr, num_bytes, value): if self.debug.enabled("memcheck") and not self.suppress_debug: self.bounds_check(start_addr) if self.debug.enabled("mem") and not self.suppress_debug: print ':: WR.MEM[%s] = %s' % (pad_hex(start_addr), pad_hex(value)), for i in range(num_bytes): self.data[start_addr + i] = chr(value & 0xFF) value = value >> 8
class RegisterFile(object): def __init__(self, constant_zero=True, num_regs=32, nbits=32): self.num_regs = num_regs self.regs = [r_uint(0)] * self.num_regs self.debug = Debug() self.nbits = nbits self.debug_nchars = nbits / 4 if constant_zero: self._setitemimpl = self._set_item_const_zero else: self._setitemimpl = self._set_item def __getitem__(self, idx): if self.debug.enabled("rf"): print ':: RD.RF[%s] = %s' % (pad( "%d" % idx, 2), pad_hex(self.regs[idx], len=self.debug_nchars)), return self.regs[idx] @specialize.argtype(2) def __setitem__(self, idx, value): value = r_uint(value) self._setitemimpl(idx, value) def _set_item(self, idx, value): self.regs[idx] = value if self.debug.enabled("rf"): print ':: WR.RF[%s] = %s' % (pad( "%d" % idx, 2), pad_hex(self.regs[idx], len=self.debug_nchars)), def _set_item_const_zero(self, idx, value): if idx != 0: self.regs[idx] = value if self.debug.enabled("rf"): print ':: WR.RF[%s] = %s' % (pad("%d" % idx, 2), pad_hex(self.regs[idx], len=self.debug_nchars)), #----------------------------------------------------------------------- # print_regs #----------------------------------------------------------------------- # prints all registers (register dump) # per_row specifies the number of registers to display per row def print_regs(self, per_row=6): for c in xrange(0, self.num_regs, per_row): str = "" for r in xrange(c, min(self.num_regs, c + per_row)): str += "%s:%s " % (pad( "%d" % r, 2), pad_hex(self.regs[r], len=(self.nbits / 4))) print str
class _WordMemory( object ): def __init__( self, data=None, size=2**10 ): self.data = data if data else [ r_uint32(0) ] * (size >> 2) self.size = (len( self.data ) << 2) self.debug = Debug() # TODO: pass data_section to memory for bounds checking self.data_section = 0x00000000 def bounds_check( self, addr, x ): # check if the accessed data is larger than the memory size if addr > self.size: print ("WARNING: %s accessing larger address than memory size. " "addr=%s size=%s") % ( x, pad_hex( addr ), pad_hex( self.size ) ) raise Exception() if addr == 0: print "WARNING: accessing null pointer!" raise Exception() # Special write checks if x == 'WR' and addr < self.data_section: print ("WARNING: %s writing address below .data section!!!. " "addr=%s size=%s") % ( x, pad_hex( addr ), pad_hex( self.data_section ) ) raise Exception() @unroll_safe def read( self, start_addr, num_bytes ): assert 0 < num_bytes <= 4 word = start_addr >> 2 byte = start_addr & 0b11 if self.debug.enabled( "mem" ): print ':: RD.MEM[%s] = ' % pad_hex( start_addr ), if self.debug.enabled( "memcheck" ): self.bounds_check( start_addr, 'RD' ) value = 0 if num_bytes == 4: # TODO: byte should only be 0 (only aligned) value = widen( self.data[ word ] ) elif num_bytes == 2: # TODO: byte should only be 0, 1, 2, not 3 mask = 0xFFFF << (byte * 8) value = ( widen( self.data[ word ] ) & mask) >> (byte * 8) elif num_bytes == 1: mask = 0xFF << (byte * 8) value = ( widen( self.data[ word ] ) & mask) >> (byte * 8) else: raise Exception('Invalid num_bytes: %d!' % num_bytes) if self.debug.enabled( "mem" ): print '%s' % pad_hex( value ), return value # this is instruction read, which is otherwise identical to read. The # only difference is the elidable annotation, which we assume the # instructions are not modified (no side effects, assumes the addresses # correspond to the same instructions) @elidable def iread( self, start_addr, num_bytes ): assert start_addr & 0b11 == 0 # only aligned accesses allowed return widen( self.data[ start_addr >> 2 ] ) @unroll_safe def write( self, start_addr, num_bytes, value ): assert 0 < num_bytes <= 4 word = start_addr >> 2 byte = start_addr & 0b11 if self.debug.enabled( "memcheck" ): self.bounds_check( start_addr, 'WR' ) if num_bytes == 4: # TODO: byte should only be 0 (only aligned) pass # no masking needed elif num_bytes == 2: # TODO: byte should only be 0, 1, 2, not 3 mask = ~(0xFFFF << (byte * 8)) & 0xFFFFFFFF value = ( widen( self.data[ word ] ) & mask ) \ | ( (value & 0xFFFF) << (byte * 8) ) elif num_bytes == 1: mask = ~(0xFF << (byte * 8)) & 0xFFFFFFFF value = ( widen( self.data[ word ] ) & mask ) \ | ( (value & 0xFF ) << (byte * 8) ) else: raise Exception('Invalid num_bytes: %d!' % num_bytes) if self.debug.enabled( "mem" ): print ':: WR.MEM[%s] = %s' % ( pad_hex( start_addr ), pad_hex( value ) ), self.data[ word ] = r_uint32( value )
val = layer.getValue(x, y) val[0] = int(val[0]) val[1] = int(val[1]) val[2] = int(val[2]) #myDebug.dprint("layer val: " + str(val)) pix[x, y] = tuple(val) # time it took to generate 1 row of pixels if not eta: newTime = time.time() timeRemaining = (newTime - oldTime) * float(width) * float(frames) hours = int(timeRemaining / 3600) mins = int(timeRemaining / 60) - hours * 3600 seconds = int(timeRemaining) - mins * 60 print "Render Time Estimate:", hours, "hours", mins, "mins", seconds, "seconds" eta = True myDebug.dprint("Frame " + str(nImage + 1) + " of " + str(frames)) draw = ImageDraw.Draw(image) if (myDebug.enabled()): draw.text((0, 0),"Frame: " + str(nImage + 1),(255,255,255)) draw.text((0, height - 15),"PysiCrystals v" + VERSION,(255,255,255)) images.append(image) layer.update() print ("Writing Gif...") # duration parameter seems a bit off... writeGif(outputFile, images, duration / 20, 0, 0, 0)
class _SparseMemory( object ): _immutable_fields_ = [ "BlockMemory", "block_size", "addr_mask", "block_mask" ] def __init__( self, BlockMemory, block_size=2**10 ): self.BlockMemory = BlockMemory self.block_size = block_size self.addr_mask = block_size - 1 self.block_mask = 0xffffffff ^ self.addr_mask self.debug = Debug() print "sparse memory size %x addr mask %x block mask %x" \ % ( self.block_size, self.addr_mask, self.block_mask ) #blocks = [] self.block_dict = {} self.debug = Debug() def add_block( self, block_addr ): #print "adding block: %x" % block_addr self.block_dict[ block_addr ] = self.BlockMemory( size=self.block_size ) @elidable def get_block_mem( self, block_addr ): #block_idx = block_dict[ if block_addr not in self.block_dict: self.add_block( block_addr ) block_mem = self.block_dict[ block_addr ] return block_mem @elidable def iread( self, start_addr, num_bytes ): start_addr = hint( start_addr, promote=True ) num_bytes = hint( num_bytes, promote=True ) end_addr = start_addr + num_bytes - 1 block_addr = self.block_mask & start_addr block_mem = self.get_block_mem( block_addr ) # For mixed-width ISAs, the start_addr is not necessarily # word-aligned, and can cross block memory boundaries. If there is # such a case, we have two instruction reads and then form the word # for it block_end_addr = self.block_mask & end_addr if block_addr == block_end_addr: return block_mem.iread( start_addr & self.addr_mask, num_bytes ) else: num_bytes1 = min( self.block_size - (start_addr & self.addr_mask), num_bytes ) num_bytes2 = num_bytes - num_bytes1 block_mem1 = block_mem block_mem2 = self.get_block_mem( block_end_addr ) value1 = block_mem1.iread( start_addr & self.addr_mask, num_bytes1 ) value2 = block_mem2.iread( 0, num_bytes2 ) value = value1 | ( value2 << (num_bytes1*8) ) #print "nb1", num_bytes1, "nb2", num_bytes2, \ # "ba1", hex(block_addr), "ba2", hex(block_end_addr), \ # "v1", hex(value1), "v2", hex(value2), "v", hex(value) return value def read( self, start_addr, num_bytes ): if self.debug.enabled( "mem" ): print ':: RD.MEM[%s] = ' % pad_hex( start_addr ), block_addr = self.block_mask & start_addr block_addr = hint( block_addr, promote=True ) block_mem = self.get_block_mem( block_addr ) value = block_mem.read( start_addr & self.addr_mask, num_bytes ) if self.debug.enabled( "mem" ): print '%s' % pad_hex( value ), return value def write( self, start_addr, num_bytes, value ): if self.debug.enabled( "mem" ): print ':: WR.MEM[%s] = %s' % ( pad_hex( start_addr ), pad_hex( value ) ), block_addr = self.block_mask & start_addr block_addr = hint( block_addr, promote=True ) block_mem = self.get_block_mem( block_addr ) block_mem.write( start_addr & self.addr_mask, num_bytes, value )
class _WordMemory(object): def __init__(self, data=None, size=2**10): self.data = data if data else [0] * (size >> 2) self.size = (len(self.data) << 2) self.debug = Debug() def bounds_check(self, addr, x): # check if the accessed data is larger than the memory size if addr > self.size: print( "WARNING: %s accessing larger address than memory size. " "addr=%s size=%s") % (x, pad_hex(addr), pad_hex(self.size)) raise Exception() if addr == 0: print "WARNING: accessing null pointer!" raise Exception() # Special write checks if x == 'WR' and addr < self.data_section: print( "WARNING: %s writing address below .data section!!!. " "addr=%s size=%s") % (x, pad_hex(addr), pad_hex(self.data_section)) raise Exception() @unroll_safe def read(self, start_addr, num_bytes): assert 0 < num_bytes <= 4 word = start_addr >> 2 byte = start_addr & 0b11 if self.debug.enabled("mem"): print ':: RD.MEM[%s] = ' % pad_hex(start_addr), if self.debug.enabled("memcheck"): self.bounds_check(start_addr, 'RD') value = 0 if num_bytes == 4: # TODO: byte should only be 0 (only aligned) value = self.data[word] elif num_bytes == 2: # TODO: byte should only be 0, 1, 2, not 3 mask = 0xFFFF << (byte * 8) value = (self.data[word] & mask) >> (byte * 8) elif num_bytes == 1: mask = 0xFF << (byte * 8) value = (self.data[word] & mask) >> (byte * 8) else: raise Exception('Invalid num_bytes: %d!' % num_bytes) if self.debug.enabled("mem"): print '%s' % pad_hex(value), return value # this is instruction read, which is otherwise identical to read. The # only difference is the elidable annotation, which we assume the # instructions are not modified (no side effects, assumes the addresses # correspond to the same instructions) @elidable @unroll_safe def iread(self, start_addr, num_bytes): assert start_addr & 0b11 == 0 # only aligned accesses allowed return self.data[start_addr >> 2] @unroll_safe def write(self, start_addr, num_bytes, value): assert 0 < num_bytes <= 4 word = start_addr >> 2 byte = start_addr & 0b11 if self.debug.enabled("memcheck"): self.bounds_check(start_addr, 'WR') if num_bytes == 4: # TODO: byte should only be 0 (only aligned) self.data[word] = value elif num_bytes == 2: # TODO: byte should only be 0, 1, 2, not 3 mask = ~(0xFFFF << (byte * 8)) & 0xFFFFFFFF value = (self.data[word] & mask) | ((value & 0xFFFF) << (byte * 8)) elif num_bytes == 1: mask = ~(0xFF << (byte * 8)) & 0xFFFFFFFF value = (self.data[word] & mask) | ((value & 0xFF) << (byte * 8)) else: raise Exception('Invalid num_bytes: %d!' % num_bytes) if self.debug.enabled("mem"): print ':: WR.MEM[%s] = %s' % (pad_hex(start_addr), pad_hex(value)), self.data[word] = value
class _SparseMemory(object): _immutable_fields_ = [ "BlockMemory", "block_size", "addr_mask", "block_mask" ] def __init__(self, BlockMemory, block_size=2**10): self.BlockMemory = BlockMemory self.block_size = block_size self.addr_mask = block_size - 1 self.block_mask = 0xffffffff ^ self.addr_mask self.debug = Debug() print "sparse memory size %x addr mask %x block mask %x" \ % ( self.block_size, self.addr_mask, self.block_mask ) #blocks = [] self.block_dict = {} self.debug = Debug() def add_block(self, block_addr): #print "adding block: %x" % block_addr self.block_dict[block_addr] = self.BlockMemory(size=self.block_size, suppress_debug=True) @elidable def get_block_mem(self, block_addr): #block_idx = block_dict[ if block_addr not in self.block_dict: self.add_block(block_addr) block_mem = self.block_dict[block_addr] return block_mem @elidable def iread(self, start_addr, num_bytes): start_addr = hint(start_addr, promote=True) num_bytes = hint(num_bytes, promote=True) end_addr = start_addr + num_bytes - 1 block_addr = self.block_mask & start_addr block_mem = self.get_block_mem(block_addr) # For mixed-width ISAs, the start_addr is not necessarily # word-aligned, and can cross block memory boundaries. If there is # such a case, we have two instruction reads and then form the word # for it block_end_addr = self.block_mask & end_addr if block_addr == block_end_addr: return block_mem.iread(start_addr & self.addr_mask, num_bytes) else: num_bytes1 = min(self.block_size - (start_addr & self.addr_mask), num_bytes) num_bytes2 = num_bytes - num_bytes1 block_mem1 = block_mem block_mem2 = self.get_block_mem(block_end_addr) value1 = block_mem1.iread(start_addr & self.addr_mask, num_bytes1) value2 = block_mem2.iread(0, num_bytes2) value = value1 | (value2 << (num_bytes1 * 8)) #print "nb1", num_bytes1, "nb2", num_bytes2, \ # "ba1", hex(block_addr), "ba2", hex(block_end_addr), \ # "v1", hex(value1), "v2", hex(value2), "v", hex(value) return value def read(self, start_addr, num_bytes): if self.debug.enabled("mem"): print ':: RD.MEM[%s] = ' % pad_hex(start_addr), block_addr = self.block_mask & start_addr block_addr = hint(block_addr, promote=True) block_mem = self.get_block_mem(block_addr) value = block_mem.read(start_addr & self.addr_mask, num_bytes) if self.debug.enabled("mem"): print '%s' % pad_hex(value), return value def write(self, start_addr, num_bytes, value): if self.debug.enabled("mem"): print ':: WR.MEM[%s] = %s' % (pad_hex(start_addr), pad_hex(value)), block_addr = self.block_mask & start_addr block_addr = hint(block_addr, promote=True) block_mem = self.get_block_mem(block_addr) block_mem.write(start_addr & self.addr_mask, num_bytes, value)