"""
=========
Testbench
=========
Testbench utility module for TheSyDeKick. Contains attributes and methods to
construct a Verilog or VHDL testbench for a provided DUT module/entity, parse 
its IO and parameter definitions and construct a structured testbench with clock and file IO.
Utilizes logging method from thesdk.
Initially written by Marko Kosunen 20190108, marko.kosunen@aalto.fi
Refactored from 'testbench' by Marko Kosunen 20221119, marko.kosunen@aalto.fi
"""
import os
import sys
import pdb
from rtl.testbench_common import testbench_common
from rtl.sv.verilog_testbench import verilog_testbench
from rtl.vhdl.vhdl_testbench import vhdl_testbench
[docs]
class testbench(testbench_common):
    """ Testbench class. Extends `module` through 'testbench_commom'
    """
    def __init__(self, parent=None, **kwargs):
        """ Executes init of testbench_common, thus having the same attributes and 
        parameters.
        Parameters
        ----------
            **kwargs :
               See module testbench_common
        
        """
        #This should be language specific.
        super().__init__(parent=parent,**kwargs)
    @property
    def langmodule(self):
        """The language specific operation is defined with an instance of 
        language specific class. Properties and methods return values from that class.
        """
        if not hasattr(self,'_langmodule'):
            if self.lang == 'sv':
                self._langmodule=verilog_testbench(
                        parent=self.parent,
                        file=self.file, name=self.name, 
                        instname=self.instname)
            elif self.lang == 'vhdl':  
                self._langmodule=vhdl_testbench(
                        parent=self.parent,
                        file=self.file, name=self.name, 
                        instname=self.instname)
        return self._langmodule
    @property
    def iofiles(self):
        return self.langmodule.iofiles
    @iofiles.setter
    def iofiles(self,val):
        self.langmodule.iofiles = val
    @property
    def connectors(self):
        """Overload to pass values to langmodule.
        """
        if not hasattr(self.langmodule,'_connectors'):
            self.langmodule.connectors=rtl_connector_bundle(lang=self.lang)
        return self.langmodule.connectors
    @connectors.setter
    def connectors(self,val):
        self.langmodule.connectors = val
    @property
    def assignment_matchlist(self):
        if not hasattr(self.langmodule,'_assignment_matchlist'):
            self.langmodule.assignment_matchlist=[]
        return self.langmodule.assignment_matchlist
    @assignment_matchlist.setter
    def assignment_matchlist(self,val):
        self.langmodule.assignment_matchlist = val
    @property
    def content_parameters(self):
        """ Parameters used inside the testbench
            
            Dict of name: (type,value)
        """
        return self.langmodule.content_parameters
    @content_parameters.setter    
    def content_parameters(self,val):
        self.langmodule.content_parameters=val
    @property
    def verilog_instances(self):
        """Verilog instances Bundle to be added to tesbench
        
        Todo 
        Need to handle VHDL instance too.
        """
        if not hasattr(self,'_verilog_instances'):
            self._verilog_instances=Bundle()
        return self._verilog_instances
[docs]
    def verilog_instance_add(self,**kwargs):
        """Add verilog instance to the Bundle fro a file
        Parameters
        ----------
        **kwargs:
           name : str
             name of the module
           file :
               File defining the module
        """
        # TODO: need to handle vhdl instances too
        name=kwargs.get('name')
        file=kwargs.get('file')
        self.verilog_instances.Members[name]=verilog_module(file=file,instname=name)
        # Addc connectors from the imported instance 
        self.connectors.update(bundle=self.verilog_instances.Members[name].io_signals.Members) 
    
    @property
    def parameter_definitions(self):
        """Parameter  and variable definition strings of the testbench
        """
        return self.langmodule.parameter_definitions
    
    @property
    def connector_definitions(self):
        """Verilog register and wire definition, VHDL signal strings.
        """
        return self.langmodule.connector_definitions
[docs]
    def assignments(self,**kwargs):
        """Wire/signal assingment strings
        """
        return self.langmodule.assignments 
     
    @property
    def iofile_definitions(self):
        """IOfile definition strings
        """
        return self.langmodule.iofile_definitions
    @property
    def clock_definition(self):
        """Clock definition string
        Todo
        Create append mechanism to add more clocks.
        """
        return self.langmodule.clock_definitions
    @property
    def iofile_close(self):
        """File close procedure for all IO files.
        """
        return self.langmodule.iofile_close
    @property
    def end_condition(self):
        """ RTL structure that sets the thesdk_simulation_completed to true.
        Default for VHDL: 'thesdk_simulation_completed <= thesdk_file_io_completed;'
        """
        return self.langmodle._end_condition
    @end_condition.setter
    def end_condition(self,value):
        self.langmodule._end_condition = value
    @property
    def misccmd(self):
        """String
        
        Miscellaneous command string corresponding to self.rtlmisc -list in
        the parent entity.
        """
        return self.langmodule.misccmd
    
    @misccmd.setter
    def misccmd(self,value):
        self.langmodule.misccmd=value
    @misccmd.deleter
    def misccmd(self,value):
        self.langmodule.misccmd=None
    # This method 
[docs]
    def define_testbench(self):
        """Defines the tb connectivity, creates reset and clock, and initializes them to zero
        """
        self.langmodule.define_testbench() 
    
    #@property
    #def definition(self):
    #    '''Entity definition part extracted for the file. Contains generics and 
    #    IO definitions.
    #    Overloads the property inherited from 'module', as wish to control whan we generate the headers.
    #    '''
[docs]
    def generate_contents(self):
        """Call language specific contents generator.
        """
        self.langmodule.generate_contents() 
 
if __name__=="__main__":
    pass