Source code for rtl.rtl_iofile

"""
======================
RTL IOfile module 
======================

Provides Verilog- and VHDL file-io related attributes and methods 
for TheSyDeKick RTL intereface.

Restructured from verilog_iofile by Marko Kosunen, marko.kosunen@aalto.fi 2023
"""
import os
import sys
import pdb
from abc import * 
from thesdk import *
from thesdk.iofile import iofile
import numpy as np
import pandas as pd
import sortedcontainers as sc
from rtl.rtl_iofile_common import rtl_iofile_common
from rtl.sv.verilog_iofile import verilog_iofile
from rtl.sv.verilog_iofile_obsoletes import verilog_iofile_obsoletes
from rtl.vhdl.vhdl_iofile import vhdl_iofile
from rtl.connector import indent

[docs] class rtl_iofile(verilog_iofile_obsoletes,rtl_iofile_common): ''' Class to provide file IO for rtl simulations. When created, adds a rtl_iofile object to the parents iofile_bundle attribute. Accessible as self.iofile_bundle.Members['name']. Provides methods and attributes that can be used to construct sections in Verilog testbenches, like file io routines, file open and close routines, file io routines, file io format strings and read/write conditions. Example ------- Initiated in parent as: _=rtl_iofile(self,name='foobar') ''' def __init__(self,parent=None,**kwargs): '''Parameters ----------- parent : object The parent object initializing the rtl_iofile instance. Default None **kwargs : name : str Name of the file. Appended with random string during the simulation. param : str, -g `'g_file_'` The string defining the testbench parameter to be be passed to the simulator at command line. Sets the paramname attribute. ioformat : str, %d sets the ioformat attribute. ''' #This is a redundant check, but does not hurt.to have it here too. if parent==None: self.print_log(type='F', msg="Parent of RTL input file not given") try: super(rtl_iofile_common,self).__init__(parent=parent,**kwargs) self.rtlparent=parent self.paramname=kwargs.get('param','-g g_file_') self._ioformat=kwargs.get('ioformat','%d') #by default, the io values are decimal integer numbers except: self.print_log(type='F', msg="RTL IO file definition failed") self._DictData = None # data structure for event-based IO data @property def langmodule(self): if not hasattr(self,'_langmodule_verilog'): self._langmodule_verilog = verilog_iofile(self) self._langmodule_verilog.file=self.file self._langmodule_verilog.paramname=self.paramname self._langmodule_verilog.name=self.name if not hasattr(self,'_langmodule_vhdl'): self._langmodule_vhdl = vhdl_iofile(self) self._langmodule_vhdl.file=self.file self._langmodule_vhdl.paramname=self.paramname self._langmodule_vhdl.name=self.name if self.parent.lang=='sv': return self._langmodule_verilog elif self.parent.lang=='vhdl': return self._langmodule_vhdl @property def rtl_timescale(self): ''' Timescale for RTL simulations inherited from parent ''' if not hasattr(self,'_rtl_timescale'): self._rtl_timescale = self.parent.rtl_timescale return self._rtl_timescale @property def ioformat(self): '''Formatting string for verilog file reading Default %d, i.e. content of the file is single column of integers. ''' return self.langmodule.ioformat @ioformat.setter def ioformat(self,value): self.langmodule.ioformat=value @property def rtlparam(self): '''Extracts the parameter name and value from simparam attribute. Used to construct the parameter definitions for Verilog testbench. Default {'g_file_<self.name>', ('string',self.file) } ''' #This should be simulators, not lang dependent. return self.langmodule.rtlparam # Status parameter @property def rtl_stat(self): '''Status variable name to be used in verilog testbench. ''' return self.langmodule.rtl_stat @rtl_stat.setter def rtl_stat(self,value): self.langmodule.rtl_stat=value #Timestamp integers for control files @property def rtl_ctstamp(self): '''Current time stamp variable name to be used in verilog testbench. Used in event type file IO. ''' return self.langmodule.rtl_ctstamp @property def rtl_pstamp(self): '''Past time stamp variable for verilog testbench. Used in event type file IO. ''' return self.langmodule.rtl_pstamp @property def rtl_tdiff(self): '''Verilog time differencec variable. Used in event based file IO. ' ''' return self.langmodule.rtl_tdiff # Status integer verilog definitions @property def rtl_statdef(self): '''Verilog file read status integer variable definitions and initializations strings. ''' return self.langmodule.rtl_statdef #Status integer verilog definitions # File pointer @property def rtl_fptr(self): '''Verilog file pointer name. ''' return self.langmodule.rtl_fptr @rtl_fptr.setter def rtl_fptr(self,value): self.langmodule.rtl_fptr=value # File opening, direction dependent @property def rtl_fopen(self): '''Verilog file open routine string. ''' return self.langmodule.rtl_fopen # File close @property def rtl_fclose(self): '''Verilog file close routine sting. ''' return self.langmodule.rtl_fclose @property def rtl_connectors(self): ''' List for verilog connectors. These are the verilog signals/regs associated with this file ''' if not hasattr(self,'_rtl_connectors'): self._rtl_connectors=[] return self._rtl_connectors @rtl_connectors.setter def rtl_connectors(self,value): #Ordered list. self._rtl_connectors=value
[docs] def connector_datamap(self,**kwargs): '''Verilog_connectors is an ordered list. Order defines the assumed order of columns in the file to be read or written. This datamap provides {'name' : index } dictionary to assing data to correct columns. Less use for data files, more for controls ''' name=kwargs.get('name') if not self.rtl_connectors: self.print_log(type='F', msg='Connector undefined, can\'t access.') else: if self.iotype=='sample': self._verilog_connector_datamap=dict() elif self.iotype=='event': self._verilog_connector_datamap={'time':0} index=0 for val in self.rtl_connectors: index+=1 self._verilog_connector_datamap.update({'%s' %(val.name): index}) return self._verilog_connector_datamap[name]
[docs] def set_control_data(self,**kwargs): '''Method to define event based data value with name, time, and value. Uses a python dictionary instead of a numpy array for more efficient insertions. The 'time' column acts as the dictionary key, the remaining columns are stored as the value. Parameters ---------- **kwargs : time: int, 0 name: str val: type undefined init: int, 0 vector of values to initialize the data. lenght should correpond to `self.rtl_connectors+1` ''' time=kwargs.get('time',int(0)) name=kwargs.get('name') val=kwargs.get('val') init=kwargs.get('init',int(0)) # sanity checks assert isinstance(time, int), "Argument 'time' should have the type 'int'" # Init Data and add first element if self.DictData is None: self.DictData = sc.SortedDict() if np.isscalar(init): self.DictData[0] = (np.ones(len(self.rtl_connectors))*init).astype(int) elif init.shape[1] == len(self.rtl_connectors)+1: init_array = init.astype(int) for row in init_array: self.DictData[row[0]] = row[1:] # Add subsequent elements as diffs as follows: # None -- no change # int -- change signal to the given value else: # add a new row if the time is not yet in the dictionary if time not in self.DictData: # init diff as no change self.DictData[time] = [None for _ in range(len(self.rtl_connectors))] # change corresponding value self.DictData[time][self.connector_datamap(name=name)-1] = val
# Overload self.Data accessors to keep them consistent with the assumption of using numpy arrays # To hold IO data. These methods convert to and from the diff-based data structure used in this # module. I.e. the self.Data property will look like an numpy array as seen from external modules # while in reality it's using the more efficient SortedDict implementation internally. # Getter - This takes the difference based format stored in DictData and converts it to a numpy array @property def Data(self): if not hasattr(self, '_Data'): self._Data=None else: if self.iotype=='event' and hasattr(self, '_DictData'): diff_array = np.array([np.insert(signals, 0, time) for (time, signals) in self.DictData.items()]) # populate None values from previous timestamps transposed = np.transpose(diff_array) for i in range(1, transposed.shape[0]): for j in range(1, transposed.shape[1]): if transposed[i,j] is None: transposed[i,j] = transposed[i, j-1] self._Data = np.transpose(transposed).astype(int) return self._Data # Setter - Takes a numpy array and converts it to the diff-based SortedDict @Data.setter def Data(self, value): # convert value to equivalent SortedDict representation if self.iotype=='event': if self.DictData == None: self._DictData = sc.SortedDict() for row in value: self.DictData[row[0]] = row[1:] # build a numpy array from the dict and sort it by time column diff_array = np.array([np.insert(signals, 0, time) for (time, signals) in self.DictData.items()]) # populate None values from previous timestamps transposed = np.transpose(diff_array) for i in range(1, transposed.shape[0]): for j in range(1, transposed.shape[1]): if transposed[i,j] is None: transposed[i,j] = transposed[i, j-1] self._Data = np.transpose(transposed).astype(int) else: self._Data=value @property def DictData(self): if not hasattr(self, '_DictData'): self._DictData=None return self._DictData @DictData.setter def DictData(self, value): self._DictData = value # Condition string for monitoring if the signals are unknown @property def rtl_io_condition(self): '''Condition string that must be true in order to file IO read/write to occur. See langmodules for details. ''' return self.langmodule.rtl_io_condition @rtl_io_condition.setter def rtl_io_condition(self,value): self.langmodule.rtl_io_condition=value def rtl_io_condition_append(self,**kwargs ): '''Append new condition string to `rtl_io_condition` Parameters ---------- **kwargs : cond : str ''' self.langmodule.rtl_io_condition_append(**kwargs) @property def rtl_io_sync(self): '''File io synchronization condition for sample type input. Default: `@(posedge clock)` ''' return self.langmodule.rtl_io_sync @rtl_io_sync.setter def rtl_io_sync(self,value): self.langmodule.rtl_io_sync=value
[docs] def rtl_io_condition_append(self,**kwargs ): '''Append new condition string to `rtl_io_condition` Parameters ---------- **kwargs : cond : str ''' self.langmodule.rtl_io_condition_append(**kwargs)
@property def rtl_io(self,**kwargs): '''Verilog write/read construct for file IO depending on the direction and file type (event/sample). Returns _______ str Verilog code for file IO to read/write the IO file. ''' return self.langmodule.rtl_io