Source code for pylab_ml.common.file_io

"""
file IO functions

"""
from pathlib import Path
import os
import pathlib
import inspect
from pylab_ml.common.data import str2num
from pylab_ml.common.common import choice
from pylab_ml.common.data import complement
# from pylab_ml.base_instrument import logger


[docs] def help(): print("Lists all functions available in file_io module:") print("********************************************************") print(readVlogMemFile.__doc__) print("********************************************************") print(writeVlogMemFile.__doc__)
NETWORK = '//samba'
[docs] def filedialog(initialdir, title, filetypes): from tkinter import Tk from tkinter import filedialog as fd root = Tk() root.withdraw() filename = fd.askopenfilename(initialdir=initialdir, title=title, filetypes=filetypes) root.destroy() return filename
[docs] def openFile(fileName, *argv, **kwargs): '''open a file with name= fileName - fileName could contain $Variable, this will be interpret with os.environ.get('$Variable') - if os = windows and filename start with '/' than add //samba ''' fileName = replaceFilename(fileName) file = None try: file = open(fileName, *argv, **kwargs) except Exception: # logger.error(f'{inspect.stack()[1][3]}: Cannot open {fileName}') print(f'ERROR: {inspect.stack()[1][3]}: Cannot open {fileName}') return file
[docs] def replaceFilename(fileName): if fileName.find('$') > -1: # find environment variables inside the value? tmp = fileName.split('/') index = 0 for s in tmp: if s.find('$') == 0: env = os.environ.get(s[1:]) tmp[index] = env if env is not None else '' index += 1 fileName = '/'.join(tmp) if len(fileName) > 0 and fileName[0] == '/' and os.name == "nt" and fileName.find(NETWORK) != 0: fileName = NETWORK + fileName return fileName
[docs] def get_latestfile(filename, logerror=None): """ Get the file with the latest date. Parameters ---------- logger : filename : string prefix from the file Returns ------- return with the latest file with the name 'filename'. """ directory = os.path.split(filename)[0] prefix = os.path.basename('.'.join(filename.split('.')[:-1])) suffix = Path(filename).suffix files = os.listdir(directory) filesfound = [] for f in files: if f.find(prefix) == 0 and Path(f).suffix == suffix: filesfound.append(f) if len(filesfound) == 0: msg = f"no files '{prefix}*{suffix}' found" if logerror is None: print(f"Error: {msg}") else: logerror(msg) else: return os.path.join(directory, filesfound[-1])
[docs] def loadIHexFile(fileName, bytemem=None, size=0x10000): """ loadIHexFile(fileName, bytemem=None, size=0x10000) reads the ihex file(s) and builds a byte image ihexfileName....path to IntelHex file bytemem.........byte image, default=None size............size of byte memory, default 64kB return bytemem or None if fileName not exist """ try: fop = open(fileName, 'rt') print('loadIHexFile: open %s' % fileName) except Exception: print('loadIHexFile: Cannot open %s' % fileName) return None if bytemem is None: bytemem = [0] * size firstaddr = 0xffffffff lastaddr = 0x0 addr_offset = 0 # read const file for line in fop: if line[0:3] != ':00' and line[7:9] == '00': # Data Record (Typ 00) byte_count = int(line[1:3], 16) addr = int(line[3:7], 16) + addr_offset if addr < firstaddr: firstaddr = addr if addr < size: if addr+byte_count-1 > lastaddr: lastaddr = addr+byte_count-1 for i in range(byte_count): bytemem[addr + i] = int(line[9 + (i * 2):11 + (i * 2)], 16) elif line[7:9] == '01': # Enf of File Record (Typ 01) break elif line[7:9] == '02': # Extended Segment Address Record (Typ 02) addr_offset = line[10:12] << 4 fop.close() print(' Read IHex image in address range 0x%0X to 0x%0X' % (firstaddr, lastaddr)) return bytemem
[docs] def readMemFile(filename, typ=None, interpret=None): """ read a memory-file and alocate data. Parameters ---------- filename : TYPE DESCRIPTION. typ : TYPE DESCRIPTION. Returns ------- data : memory data integer list, None entries for not used/initialized values """ extensions = {'v': 'verilog', 'txt': 'txt'} start = None if typ is None: file_extension = pathlib.Path(filename).suffix[1:] if file_extension in extensions.keys(): typ = extensions[file_extension] else: typ = f'.{file_extension}' if not choice(typ, ['verilog', 'txt']): return False if typ == 'verilog': start, data = readVlogMemFile(filename, 0) # TODO!: change readVlogMemFile to get automatically the size elif typ == 'txt': start, data = readtxtMemFile(filename, interpret) return start, data
[docs] def readtxtMemFile(fileName, memSize=None, bitsize_source=None, bitsize_target=None, numerative=None, raw_data=False): """ Read a file with memory data in simple hex or integer format. Parameters ---------- fileName : string DESCRIPTION. memSize : int size from the reserved memory array. bitsize_source: int size from one datum, e.q. 8-bit, 16-bit, must be multiple times of 8 this has an effect on the adress counting if None each datum will assign to an adress, otherwise each 8-bit datum has an adress only little endian is supported yet bitsize_target: int size from one datum in the array result. needed if negative values in the sourcefile numerative : {adr : base dat : base} # base could be 10(decimal) or 16(hex) raw_data: bool if True than return with a list of adr + data if False than return with a coherent memory, beginning with startadr Returns ------- startadress : integer data : memory data integer list, None entries for not used/initialized values """ file = openFile(fileName) if file is None: return None base_adr = numerative['adr'] if (numerative is not None and 'adr' in numerative.keys()) else 10 base_dat = numerative['dat'] if (numerative is not None and 'dat' in numerative.keys()) else 10 data = [] for line in file: parts = line.split() for index in range(0, len(parts)): value = parts[index] if value == '//': break value = str2num(value, base_adr if index == 0 else base_dat) if type(value) is str: continue if index == 0: adr = value continue if bitsize_source is None: data.append((adr, value)) else: for i in range(bitsize_source // 8): data.append((adr, value % 256)) value = value // 256 adr += 1 continue adr += 1 file.close() if raw_data: return data minadr = min(data)[0] maxadr = max(data)[0]+1 bitsize_source = 8 if bitsize_source is None else bitsize_source array = [None]*((maxadr-minadr) // (bitsize_source//8)) if memSize is None else [None]*memSize adr = minadr for index in range(0, len(data)): adr = data[index][0] dat = data[index][1] realadr = (adr-minadr) // (bitsize_source//8) oldat = 0 if array[realadr] is None else array[realadr] shift = adr % (bitsize_source//8) array[realadr] = (dat << (shift*8)) + oldat if bitsize_target is not None: for index in range(0, len(array)): if array[index] < 0: array[index] = complement(abs(array[index]), bitsize_target) + 1 # calculated 2 complement return minadr, array
[docs] def readQEMemFile(fileName, base=0x20): """Read a file with memory data in the DUMP_QE Software format. base address 0 address 0 data hex 0000 data dec 0 address 1 data hex 0000 data dec 0 Parameters ---------- fileName : string DESCRIPTION. Returns ------- startadress : integer data : memory data integer list, None entries for not used/initialized values """ file = openFile(fileName) if file is None: return None data = [] for line in file: parts = line.split() if parts[0] == 'base': baseadr = int(parts[2]) continue elif parts[0] == 'address': adr = baseadr * base + int(parts[1]) elif parts[0] == 'data': value = str2num(parts[2], 16) data.append((adr, value)) file.close() minadr = min(data)[0] maxadr = max(data)[0]+1 array = [None]*(maxadr-minadr) for index in range(0, len(data)): array[data[index][0]-minadr] = data[index][1] return minadr, array
[docs] def readVlogMemFile(fileName, memSize=0, defaultvalues=None, bitwidth=None, debug=False): """ readVlogMemFile(fileName, debug=False) reads a file with memory data in verilog hex format fileName........path to verilog file memSize.........number of memory addresses, if 0 then calculate from the data of the file defaultvalues...default values for the memory array if no data was read bitwidth........bitwidht from the data debug...........if True it prints out the data loaded return data data............memory data integer list, None entries for not used/initialized values """ fi = openFile(fileName, 'rt') if fi is None: return fi startadr = 0 calc_memSize = 0 calc_bitwidht = bitwidth for line in fi: # get the memsize from the file parts = line.split() try: if len(parts) == 0 or (len(parts) > 0 and parts[0] == '//'): continue elif parts[0][0] == "@": adr = int(parts[0][1:], 16) calc_memSize = adr if adr >= calc_memSize else calc_memSize startadr = adr if adr <= startadr else startadr parts.pop(0) if len(parts) > 0: for value in parts: int(value, 16) calc_memSize += len(value) * 4 // bitwidth calc_bitwidht = len(value) * 4 except Exception: continue fi.seek(0) memSize = calc_memSize if memSize == 0 else memSize data = [defaultvalues]*(memSize) adr = 0 for line in fi: parts = line.split() try: if len(parts) == 0 or (len(parts) > 0 and parts[0] == '//'): continue elif parts[0][0] == "@": adr = int(parts[0][1:], 16) if len(parts[1] * 4) > bitwidth: v = [] for i in range(0, len(parts[1]), 2): v.append(int(parts[1][i:i+2], 16)) else: v = int(parts[1], 16) else: v = int(parts[0], 16) if adr < len(data): if type(v) is not list: data[adr] = v else: i = len(v) - 1 for dat in v: data[adr+i] = dat i -= 1 else: # logger.error('VlogMemFile: %s > %d value(s)' % (fileName, memSize)) print('ERROR: VlogMemFile: %s > %d value(s)' % (fileName, memSize)) fi.close() return adr += 1 except Exception: continue # logger.info('VlogMemFile: %s read with %d value(s)' % (fileName, len(data))) print('VlogMemFile: %s read with %d value(s)' % (fileName, len(data))) if debug: for i, v in enumerate(data): # logger.debug('0x%X: 0x%X' % (i, v)) print('0x%X: 0x%X' % (i, v)) fi.close() return startadr, data
[docs] def writeVlogMemFile(fileName, mem, a_dig=4, d_dig=8, adroffset=0, adrinc=1, header=["", "verilog memory data file", ""]): """ Write memory data to a file in verilog hex format. fileName........path to verilog file mem.............list with integer memory data a_dig...........min address digits d_dig...........min data digits adroffset.......adress offset adrinc..........increment adr by header..........string list placed as comments at top of file return True/False """ fi = openFile(fileName, 'wt', newline='\n') if fi is None: return False for comment in header: fi.write(f'// {comment}\n') for i, v in enumerate(mem): if v is not None: fi.write('@{:0{}x} {:0{}x}\n'.format(i*adrinc+adroffset, a_dig, v, d_dig)) fi.write(' ') fi.close() return True
[docs] def readVerilogMemFile(filename): """ Parses an EEPROM file with format '@Address Data' in hex and returns a dictionary with decimal keys and values. Parameters @filename : string Path to the EEPROM file. Returns eeprom_dict : dict Dictionary with address (decimal) as keys and data (decimal) as values. """ eeprom_dict = {} try: with open(filename, 'r') as file: for line in file: line = line.strip() # Ensure the line starts with '@' and has both address and data if line.startswith('@'): parts = line.split() if len(parts) >= 2: # Extract and clean hex strings hex_address = parts[0].replace('@', '') hex_data = parts[1] # Convert hex to decimal address_decimal = int(hex_address, 16) data_decimal = int(hex_data, 16) # Store in dictionary eeprom_dict[address_decimal] = data_decimal return eeprom_dict except FileNotFoundError: print(f"Error: The file '{filename}' was not found.") return {} except ValueError: print(f"Error: Could not convert a value in the file. Check the hex format.") return {} except Exception as e: print(f"An unexpected error occurred: {e}") return {}
if __name__ == '__main__': data = [1, 2, 3, 4, 5] writeVlogMemFile('schrott.v', data)