Repository: riscv-software-src/riscof (language: python)

Issue:
Missing dependency: `device-tree-compiler`
I'm following the [installation guide](https://riscof.readthedocs.io/en/stable/installation.html), but there seems to be dependencies missing form the instructions.

The step `apt-get install device-tree-compiler` as mentioned at https://github.com/riscv/riscv-isa-sim#build-steps is missing.


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index f99e905..ab39633 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,3 +28,5 @@ docs/build
 sail_cSim
 
 spike
+
+riscof.egg-info/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1de01d5..698b32a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
 
 This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [1.21.2] - 2021-09-03
+- Ported CLI to use click package
+- Improved documentation for using plugins.
+- Added separate paths for dut and ref artifacts.
+
 ## [1.21.1] - 2021-07-24
 - Added support for intermediate run start points #1
 - --dbfile is added to generate the test-list and then run tests on the target and reference
diff --git a/MANIFEST.in b/MANIFEST.in
index 851e3a4..d5b4c96 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,13 +1,5 @@
-recursive-include riscof/suite *.S *.h
-
-recursive-include riscof/coverage *
-
 recursive-include riscof/Templates *
 
-recursive-include riscof/framework *.yaml
-
-recursive-exclude riscof/suite/wip *.S
-
 recursive-exclude riscof/__pycache__ *.pyc
 
 include LICENSE
diff --git a/docs/source/installation.rst b/docs/source/installation.rst
index 7e7b7f6..2fc5af7 100644
--- a/docs/source/installation.rst
+++ b/docs/source/installation.rst
@@ -183,120 +183,22 @@ Once you have installed RISCOF you can execute ``riscof --help`` to print the he
 
 .. code-block:: bash
 
-    usage: riscof [-h] [--version] [--verbose]
-                  {arch-tests,coverage,gendb,setup,validateyaml,run,testlist} ...
-    
-    RISCOF is a framework used to run the Architectural Tests on a DUT and check
-    compatibility with the RISC-V ISA
-    
-    optional arguments:
-      --verbose             [Default=info]
-      --version, -v         Print version of RISCOF being used
-      -h, --help            show this help message and exit
-    
-    Action:
-      The action to be performed by riscof.
-    
-      {arch-tests,coverage,gendb,setup,validateyaml,run,testlist}
-                            List of actions supported by riscof.
-        arch-tests          Setup and maintenance for Architectural Test Suite.
-        coverage            Generate Coverage Report for the given YAML spec.
-        gendb               Generate Database for the standard suite.
-        setup               Initiate setup for riscof.
-        validateyaml        Validate the Input YAMLs using riscv-config.
-        run                 Run the tests on DUT and reference and compare
-                            signatures.
-        testlist            Generate the test list for the given DUT and suite.
-    Action 'arch-tests'
-    
-    	usage: riscof arch-tests [-h] [--dir PATH] [--get-version] [--clone]
-    	                         [--update] [--show-version]
-    	
-    	optional arguments:
-    	  --clone         Clone and setup the architectural tests from the remote
-    	                  repository.
-    	  --dir PATH      The Path to the directory to initialise/containing the
-    	                  tests.[Default = ./riscv-arch-test]
-    	  --get-version   Version of the repository to get. To be used with the
-    	                  update/clone argument. [Default = latest]
-    	  --show-version  Print the version of the architectural tests being used.
-    	  --update        Update the architectural tests to the latest release.
-    	  -h, --help      show this help message and exit
-    	
-    Action 'coverage'
-    
-    	usage: riscof coverage [-h] [--config PATH] --cgf PATH --suite PATH --env PATH
-    	                       [--work-dir PATH] [--no-browser]
-    	
-    	optional arguments:
-    	  --cgf PATH       The Path to the cgf file(s). Multiple allowed
-    	  --config PATH    The Path to the config file. [Default=./config.ini]
-    	  --env PATH       The Path to the custom env directory.
-    	  --no-browser     Do not open the browser for showing the test report.
-    	  --suite PATH     The Path to the custom suite directory.
-    	  --work-dir PATH  The Path to the work-dir.
-    	  -h, --help       show this help message and exit
-    	
-    Action 'gendb'
-    
-    	usage: riscof gendb [-h] --suite PATH --env PATH [--work-dir PATH]
-    	
-    	optional arguments:
-    	  --env PATH       The Path to the custom env directory.
-    	  --suite PATH     The Path to the custom suite directory.
-    	  --work-dir PATH  The Path to the work-dir.
-    	  -h, --help       show this help message and exit
-    	
-    Action 'setup'
-    
-    	usage: riscof setup [-h] [--dutname NAME] [--refname NAME] [--work-dir PATH]
-    	
-    	optional arguments:
-    	  --dutname NAME   Name of DUT plugin. [Default=spike]
-    	  --refname NAME   Name of Reference plugin. [Default=sail_cSim]
-    	  --work-dir PATH  The Path to the work-dir.
-    	  -h, --help       show this help message and exit
-    	
-    Action 'validateyaml'
-    
-    	usage: riscof validateyaml [-h] [--config PATH] [--work-dir PATH]
-    	
-    	optional arguments:
-    	  --config PATH    The Path to the config file. [Default=./config.ini]
-    	  --work-dir PATH  The Path to the work-dir.
-    	  -h, --help       show this help message and exit
-    	
-    Action 'run'
-
-	usage: riscof run [-h] [--config PATH] --suite PATH --env PATH [--no-browser] [--work-dir PATH] [--dbfile PATH | --testfile PATH]
-	                  [--no-ref-run] [--no-dut-run]
-	
-	optional arguments:
-	  --config PATH    The Path to the config file. [Default=./config.ini]
-	  --dbfile PATH    The Path to the database file.
-	  --env PATH       The Path to the custom env directory.
-	  --no-browser     Do not open the browser for showing the test report.
-	  --no-dut-run     Do not run tests on DUT
-	  --no-ref-run     Do not run tests on Reference
-	  --suite PATH     The Path to the custom suite directory.
-	  --testfile PATH  The Path to the testlist file.
-	  --work-dir PATH  The Path to the work-dir.
-	  -h, --help       show this help message and exit
-
-    	
-    Action 'testlist'
-    
-    	usage: riscof testlist [-h] [--work-dir PATH] [--config PATH] --suite PATH
-    	                       --env PATH
-    	
-    	optional arguments:
-    	  --config PATH    The Path to the config file. [Default=./config.ini]
-    	  --env PATH       The Path to the custom env directory.
-    	  --suite PATH     The Path to the custom suite directory.
-    	  --work-dir PATH  The Path to the work-dir.
-    	  -h, --help       show this help message and exit
-    
-	
+    Usage: riscof [OPTIONS] COMMAND [ARGS]...
+        
+        Options:
+          --version                       Show the version and exit.
+          -v, --verbose [info|error|debug]
+                                          Set verbose level
+          --help                          Show this message and exit.
+        
+        Commands:
+          arch-test     Setup and maintenance for Architectural TestSuite.
+          coverage      Run the tests on DUT and reference and compare signatures
+          gendb         Generate Database for the Suite.
+          run           Run the tests on DUT and reference and compare signatures
+          setup         Initiate Setup for riscof.
+          testlist      Generate the test list for the given DUT and suite.
+          validateyaml  Validate the Input YAMLs using riscv-config.
 
 Install RISCV-GNU Toolchain
 ===========================
@@ -366,7 +268,8 @@ These are often used as reference models in RISCOF.
   .. tab:: Spike (riscv-isa-sim)
 
     .. code-block:: bash
-    
+   
+      $ sudo apt-get install device-tree-compiler
       $ git clone https://github.com/riscv/riscv-isa-sim.git
       $ cd riscv-isa-sim
       $ mkdir build
@@ -511,13 +414,6 @@ any generic DUT. Components of this folder will need to be modified by the user
 By default the ``model_test.h`` files and the ``link.ld`` file will work out of the box for
 ``spike`` and ``sail`` models.
 
-Since our DUT model in this guide is spike, you will only have to change the execute command at line 100 of
-spike/riscof_spike.py to the following:
-
-.. code-block:: python
-
-  execute += self.dut_exe + ' --log-commits --log dump --isa={0} +signature={1} +signature-granularity=4 {2};'.format(self.isa, sig_file, elf)
-
 .. note:: Custom DUTs can go through the various ``#TODO`` comments to figure out what changes need to be
   made in the respective python file.
 
@@ -539,6 +435,19 @@ We are now ready to run the architectural tests on the DUT via RISCOF.
 
 .. _csim_docs: https://github.com/rems-project/sail-riscv/riscof-plugin/README.md 
 
+Cloning the Architectural Tests
+===============================
+
+We will be running the tests from the official riscv-arch-test repository on the DUT and Reference
+models. To create a copy of the latest tests from the riscv-arch-test repository do the following:
+
+.. code-block:: console
+    
+    $ riscof --verbose info arch-tests --clone
+
+This will create a riscv-arch-test in the current working directory.
+   
+
 Running RISCOF
 ==============
 
@@ -573,7 +482,7 @@ The next step is to generate the list of tests that need to be run on the models
 
 .. code-block:: bash
 
-  riscof testlist --config=config.ini
+  riscof testlist --config=config.ini --suite=riscv-arch-test/riscv-test-suite/ --env=riscv-arch-test/riscv-test-suite/env
 
 This step calls the validate-step and thus the output adds one more line to the above dump:
 
@@ -609,7 +518,7 @@ guarantee correctness.
 
 .. code-block:: bash
 
-  riscof run --config=config.ini
+  riscof run --config=config.ini --suite=riscv-arch-test/riscv-test-suite/ --env=riscv-arch-test/riscv-test-suite/env
 
 This should compile and execute the tests on each of the models and end up with the following log.
 The run will also open an HTML page with all the information.
diff --git a/docs/source/plugins.rst b/docs/source/plugins.rst
index 47f06f6..8645a6b 100644
--- a/docs/source/plugins.rst
+++ b/docs/source/plugins.rst
@@ -26,9 +26,12 @@ The ``env`` directory in must contain:
 
   - ``model_test.h`` header file which provides the model specific macros as described in the
     `TestFormat Spec
-    <https://github.com/riscv/riscv-arch-test/blob/master/spec/TestFormatSpec.adoc>`_.
+    <https://github.com/riscv/riscv-arch-test/blob/master/spec/TestFormatSpec.adoc>`_. 
   - ``link.ld`` linker script which can be used by the plugin during test-compilation.
 
+  .. note:: If you have already ported your target to the old architectural test framework, the
+     above files can be re-used from that port/target itself.
+
 The ``env`` folder can also contain other necessary plugin specific files for pre/post processing of
 logs, signatures, elfs, etc.
 
@@ -41,30 +44,8 @@ The python plugin files capture the behavior of model for compiling tests, execu
 and finally extracting the signature for each test. The following sections provide a detailed
 explanation on how to build the python files for your model.
 
+.. hint:: All paths provided by riscof are absolute and it is advised to always use absolute paths while executing/generating commands to avoid errors. 
 
-Why Python Based Plugins ?
-==========================
-
-- Since the entire RISCOF framework is in python it did not make sense to have the 
-  user-DUT in a separate environment. It would then cause issues in transferring data across 
-  these environments/domains. 
-  
-- While many prefer the conventional *Makefile/autoconf* approach, transferring the *test-list* in YAML 
-  to be used by another Makefile-environment seemed like a bad and an unscalable idea.
-  
-- Expecting initial hesitation, we have tried to ensure that the python plugins can be made extremely 
-  simple (as crude as writing out bash instructions using shellCommand libraries). 
-  
-- Considering there would be a few backlashes in these choices, we have given enough pit-stops in the 
-  flow: ``validation, test-list, coverage, etc`` so one can stop at any point in the flow and move 
-  to their custom domain. 
-
-- Having a python plugin **does not change your test-bench** in anyway. The plugins only act as a common
-  interface between your environment and RISCOF. All you need to do is call the respective sim
-  commands from within the python plugin.
-  
-If you do feel the flow can be further improved or changed please do drop in an issue on the
-official repository.
 
 Start with Templates
 ====================
@@ -79,6 +60,7 @@ using the following command::
 This above command should generate a spike folder with the following contents:
 
 .. code-block:: bash
+  :linenos:
 
   env                          # contains sample header file and linker file   
   riscof_spike.py              # sample spike plugin for RISCOF
@@ -88,6 +70,7 @@ This above command should generate a spike folder with the following contents:
 The command will also generate a sample ``config.ini`` file with the following contents:
 
 .. code-block:: bash
+  :linenos:
 
   [RISCOF]
   ReferencePlugin=cSail                                                                               
@@ -99,6 +82,7 @@ The command will also generate a sample ``config.ini`` file with the following c
   pluginpath=/scratch/git-repo/incoresemi/riscof/spike
   ispec=/scratch/git-repo/incoresemi/riscof/spike/spike_isa.yaml                                 
   pspec=/scratch/git-repo/incoresemi/riscof/spike/spike_platform.yaml
+  target_run=1
   
   [sail_cSim]
   pluginpath=/scratch/git-repo/incoresemi/riscof/sail_cSim
@@ -106,24 +90,49 @@ The command will also generate a sample ``config.ini`` file with the following c
 The following changes need to be made:
 
 1. Fix the paths in the ``config.ini`` to point to the folder containing the respective riscof_*.py files.
-2. The macros in the ``spike/env/model_test.h`` can be updated based on the model. Definitions of
+2. The macros in the ``spike/env/model_test.h`` can be updated/replaced based on the model. Definitions of
    the macros and their use is available in the :ref:`test_format_spec`.
 3. Update the ``riscof_<target-name>.py`` with respective functions as described in the following 
    paragraphs.
 
 The plugin file in the ``spike`` folder: riscof_spike.py is the one that needs to be
-changed and updated for each model as described in the next section.
+changed and updated for each model as described in the following sections
 
 
 Please note the user is free to add more custom functions in this file which are called within the
 three base functions (as mentioned above).
 
+Why Python Based Plugins ?
+==========================
+
+- Since the entire RISCOF framework is in python it did not make sense to have the 
+  user-DUT in a separate environment. It would then cause issues in transferring data across 
+  these environments/domains. 
+  
+- While many prefer the conventional *Makefile/autoconf* approach, transferring the *test-list* in YAML 
+  to be used by another Makefile-environment seemed like a bad and an unscalable idea.
+  
+- Expecting initial hesitation, we have tried to ensure that the python plugins can be made extremely 
+  simple (as crude as writing out bash instructions using shellCommand libraries). 
+  
+- Considering there would be a few backlashes in these choices, we have given enough pit-stops in the 
+  flow: ``validation, test-list, coverage, etc`` so one can stop at any point in the flow and move 
+  to their custom domain. 
+
+- Having a python plugin **does not change your test-bench** in anyway. The plugins only act as a common
+  interface between your environment and RISCOF. All you need to do is call the respective sim
+  commands from within the python plugin.
+  
+If you do feel the flow can be further improved or changed please do drop in an issue on the
+official repository.
+
+
 .. _plugin_def:
 
-Plugin Function Definitions
-===========================
+Python Plugin file
+==================
 
-As can be seen from the template python file, it creates a Metaclass for the plugins 
+As can be seen from the above generated template python file, it creates a Metaclass for the plugins 
 supported by the :ref:`abstract_class`. This class basically offers the users three basic 
 functions: ``initialize`` , ``build`` and ``runTests``. 
 For each model RISCOF calls these functions in the following order:
@@ -132,27 +141,84 @@ For each model RISCOF calls these functions in the following order:
 
   initialize --> build --> runTests
 
-We now define the various arguments and expected functionality of each of the above
+These functions have been conceptualized keeping in mind what a typical DUT execution may require.
+Instead of having a single complex function, we have split it across 3 functions.
+
+We now define the various arguments and possible functionality of each of the above
 mentioned functions. Please note, this is not a strict guide and the users can choose to perform
-different actions in different functions as opposed to what is outlined in this guide as long as
+different actions in different functions as long as
 they comply with the order of the functions being called and the signatures are generated in their 
-respective directories at the end of the `runTest` function.
+respective directories at the end of the `runTests` function.
+
+.. note:: The contents of the signature file must conform to specification mentioned in the 
+  TestFormat Spec `here <https://github.com/riscv/riscv-arch-test/blob/master/spec/TestFormatSpec.adoc#36-the-test-signature>`_
 
 __init__ (self, *args, **kwargs)
 --------------------------------
 
-This is the constructor function for the pluginTemplate class. The configuration dictionary of the
-plugin, as specified in the ``config.ini``, is passed to the plugin via the kwargs argument.
+.. hint:: **PYTHON-HINT**: The self variable is used to represent the instance of the class which 
+   is often used in object-oriented programming. It works as a reference to the object. Python 
+   uses the self parameter to refer to instance attributes and methods of the class. In this 
+   guide we use the self parameter to create and access methods declared across the functions 
+   within the same class.
 
-In this function you will also need to define the path of the model executable in the `dut_exe`
-variable as shown in line-6 below. Note, this variable can be dierctly set in the ``config.ini`` by
-setting the `PATH` variable under the model plugin's header.
 
-The `num_jobs` variable, in line-7,  is used to indicate the number of parallel jobs that can be
-spawned for simulation.
-
-Finally, thise constructor will capture the paths to the plugin, the isa yaml and the platform yaml
-for further usage (as seen in lines 13-14).
+This is the constructor function for the pluginTemplate class. The configuration dictionary of the
+dut plugin, as specified in the ``config.ini``, is passed to the plugin via the ``**kwargs`` argument.
+The typical action in this function would be to capture as much information about the DUT from the
+`config.ini` as possible, since the config will not be available as arguments to the remaining
+functions.
+
+.. hint:: **PYTHON-HINT**: In Python we use *args and **kwargs as an argument when we are unsure about the number 
+   of arguments to pass in the functions. *args allow us to pass the variable number of non 
+   keyword arguments to a function. The arguments are passed as a tuple and these passed arguments 
+   make tuple inside the function with same name as the parameter excluding asterisk ``*``.
+
+   **kwargs allows us to pass the variable length of keyword 
+   arguments to the function. The double asterisk is used to indicate a variable length keyword
+   argument. The arguments are passed as a dictionary and these arguments make a dictionary inside 
+   function with name same as the parameter excluding double asterisk ``**``.
+
+   As is seen below, we access the config node as ``kwargs.get('config')``
+
+   Refer to this `blog
+   <https://www.programiz.com/python-programming/args-and-kwargs#:~:text=*args%20passes%20variable%20number%20of,a%20dictionary%20can%20be%20performed.>`_ for more information
+
+As mentioned, in the :ref:`config_syntax` section, the config.ini file can be used to pass some
+common or specific parameters to the python plugin. This makes it easy for users to modify the
+parameters in the config.ini file itself, instead of having to change it in the python file.
+
+At minimum, the DUT node of the ``config.ini`` must contain paths to the ISA and Platform yaml specs.
+If the DUT node is missing or is empty in the ``config.ini`` this function should throw an error and
+exit. This is done in lines 8-10 in the snippet below.
+
+One of the parameters we should capture here would be the path to the simulation executable of
+the DUT. In case of an RTL based DUT, this would be point to the final binary executable of your
+test-bench produced by a simulator (like verilator, vcs, incisive, etc). In case of an ISS or
+Emulator, this variable could point to where the ISS binary is located. This is shown in line-16 in
+the below snippet.
+
+Another variable of interest would be the number of parallel jobs that can be spawned off by RISCOF
+for various actions performed in later functions, specifically to run the tests in parallel on the
+DUT executable. This variable is captured in as the variable ``num_jobs`` in line-21 below. If the
+`config.ini` does not have the ``jobs`` variable specified then we default to the value of 1.
+
+The ``target_run`` parameter is used to control if the user would like to stop
+after compilation of the tests or continue running the tests on the target and
+go on to signature comparison. When set to '0' the plugin must only compile the
+tests and exit (using ``raise SystemExit`` in python). When set to ``1`` the
+plugin will compile and run the tests on the target. This parameter is captured
+in lines 34-37. 
+
+Finally, the mandatory parameters that must be present in the ``config.ini`` for the DUT are the
+paths to the riscv-config based ISA and Platform YAML files. These paths are collected in lines
+28-29. Remember these are paths to the unchecked version of the yaml and are only captured here to
+send them across to the RISCOF framework, where RISCOF will validate them with riscv-config , send
+it to the reference model for configuration and also use it filter the tests.
+The verified/checked versions of the YAMLs will be provided to the build function.
+
+The above yaml file paths and other arguments are captured in the class methods and returned back to
+the RISCOF framework in line 40.
 
 .. code-block:: python
     :linenos:
@@ -162,155 +228,426 @@ for further usage (as seen in lines 13-14).
 
         config = kwargs.get('config')
 
-        self.dut_exe = os.path.join(config['PATH'] if 'PATH' in config else "","spike")
-        self.num_jobs = str(config['jobs'] if 'jobs' in config else 1)
+        # If the config node for this DUT is missing or empty. Raise an error. At minimum we need
+        # the paths to the ispec and pspec files
         if config is None:
             print("Please enter input file paths in configuration.")
             raise SystemExit
+
+        # In case of an RTL based DUT, this would be point to the final binary executable of your
+        # test-bench produced by a simulator (like verilator, vcs, incisive, etc). In case of an iss or
+        # emulator, this variable could point to where the iss binary is located. If 'PATH variable
+        # is missing in the config.ini we can hardcode the alternate here (spike in this case)
+        self.dut_exe = os.path.join(config['PATH'] if 'PATH' in config else "","spike")
+
+        # Number of parallel jobs that can be spawned off by RISCOF
+        # for various actions performed in later functions, specifically to run the tests in 
+        # parallel on the DUT executable. Can also be used in the build function if required.
+        self.num_jobs = str(config['jobs'] if 'jobs' in config else 1)
+
+        # Path to the directory where this python file is located. Collect it from the config.ini
+        self.pluginpath=os.path.abspath(config['pluginpath'])
+
+        # Collect the paths to the  riscv-config absed ISA and platform yaml files. One can choose
+        # to hardcode these here itself instead of picking it from the config.ini file.
+        self.isa_spec = os.path.abspath(config['ispec'])
+        self.platform_spec = os.path.abspath(config['pspec'])
+
+        #We capture if the user would like the run the tests on the target or
+        #not. If you are interested in just compiling the tests and not running
+        #them on the target, then following variable should be set to False
+        if 'target_run' in config and config['target_run']=='0':
+            self.target_run = False
         else:
-            self.isa_spec = os.path.abspath(config['ispec'])
-            self.platform_spec = os.path.abspath(config['pspec'])
-            self.pluginpath=os.path.abspath(config['pluginpath'])
+            self.target_run = True
 
+        # Return the parameters set above back to RISCOF for further processing.
         return sclass
 
 .. warning:: if the config is empty or if the isa and platform yamls are not available in the
    specified paths, the above function shall generate an error and exit.
 
+.. note:: It is not necessary for your config.ini to pass any of these parameters. And one could
+   instead hardwire the paths in this function itself. For eg.
 
-initialize (suite, workdir, env)
---------------------------------
+   .. code-block:: python
+
+      self.dut_exe = '/scratch/mydut/sim/tb.exe'
+      self.num_jobs = 7
+
+Between lines 38-40 one can still add and capture many more DUT specific parameters which could be
+useful later. For example,
+
+.. code-block:: python
+
+        # some system may use 'pmake' instead of 'make' for parallel jobs. The following line
+        # captures the make command set in the config.ini and defaults to using make otherwise.
+        self.make = config['make'] if 'make' in config else 'make'
+
+        # setting the build path for any artifacts generated in the build function
+        self.build_path = '/scratch/mybuild/'
 
-This function is typically meant to create and initialize all necessary variables such as :
-compilation commands, elf2hex utility command, objdump command, include directories, etc.
-This function provides the following arguments which can be used:
+Compared to a conventional Makefile flow, this phase would be similar to capturing and setting some
+of the DUT specific parameters in a Makefile.include. Many of those variables can be set here and
+used later in different contexts.
+
+initialize (self, suite, workdir, archtest_env)
+-----------------------------------------------
+
+The primary action here would be to create the templates for the compile and any other pre/post
+processing commands that will be required later here. This function provides the following 
+arguments which can be used in this function:
 
 1. `suite`: This argument holds the absolute path of the directory where the architectural test suite
-   exists.This can be used to replace the name of the file to create directories in proper order.
+   exists.
 2. `workdir`: This argument holds the absolute path of the work directory where all the execution
-   and meta files/states will be dumped as part of running RISCOF.
+   and meta files/states should be dumped as part of running RISCOF.
 3. `archtest_env`: This argument holds the absolute path of the directory where all the
-   architectural test header files are located. This should be used to initialize the include arguments to the
-   compiler/assembler.
+   architectural test header files (``arch_test.h``) are located. This should be used to initialize 
+   the include arguments to the compiler/assembler.
 
-An example of this function is shown below:
+Since we have access to the test environment directory here, it would make sense to build a generic
+template of the command that we will be using to compile the tests. For example consider the
+following python code which sets the compile command to use the riscv-gcc compiler.
+
+.. code-block:: python
+
+   self.compile_cmd = 'riscv{1}-unknown-elf-gcc -march={0} \
+         -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -g\
+         -T '+self.pluginpath+'/env/link.ld\
+         -I '+self.pluginpath+'/env/\
+         -I ' + archtest_env + '{2} -o {3} {4}'
+
+.. hint:: **PYTHON-HINT**: Python's new style of string formatting makes it quite regular to use.
+   One can place curly braces within the string to indicate the point at which a replacement needs
+   to be peformed and then use the ``.format(var)`` syntax to assign those values. Curly braces with
+   integers in them indicate the argument number which should be used for replacement.
+
+   For example,
+
+   .. code-block:: python
+
+      'My name is {0} and age is {1}'.format('John','20')
+
+   In python one can also use the ``+`` symbol to concatenate strings as is shown in the above
+   snippet code, where the include directories are appended at the end
+
+
+Some folks might build a `riscv32-` toolchain or a `riscv64-` toolchain depending on
+their DUT. To be agnostic of this choice, in the above snippet we have left the integer following
+`riscv` string to be a variable (defined by ``{1}``. see below hint for python syntax details) 
+which will be fixed in the later functions. Based on the DUT one can even hard-code it here and
+remove the variable dependence. 
+
+Also, the ``march`` string that a test should be compiled with should not be hardwired here as it
+changes from test to test. Hence, we leave it as a variable in the above snippet (defined as
+``{0}``). 
+
+The variable ``{2}`` indicates the assembly file of the test that needs to be compiled. 
+The variables ``{3}`` and ``{4}`` are used to indicate the output elf name and any compile macros
+that need to be assigned respectively. Both of which will be set in the runTests function later.
+Remember here, we are assigning this string template to a method in the `self` instance of the class
+which can be accessed in other functions as well.
+
+
+Similar to the compile command above, one can choose to build template for many other commands that
+may be required to be executed for each test. For example, some common utilities would be:
+
+.. code-block:: python
+
+   # set the objdump template here. Note we continue with the variable toolchain below. Also the
+   # name of the elf is kept a variable to be fixed in the runTests function.
+   self.objdump = 'riscv{0}-unknown-elf-objdump -D {1} > test.disass'
+
+   # set the elf2hex command here. This is mostly used by rtl test-benches which use the readmemb or
+   # readmemh like utilities to load the test. Note again here, we have kept the name of the elf as
+   # variable which will be set in runTests function
+   self.elf2hex = elf2hex 8 33554432 {0} 2147483648 > code.mem
+
+The following snippet shows the entire function for reference based on the above discussion. One can
+add the above utility snippets  after line 20 below.
 
 .. code-block:: python
    :linenos:
 
    def initialise(self, suite, work_dir, archtest_env):
-       if shutil.which(self.dut_exe) is None:
-           logger.error(self.dut_exe+' Not Found')
-           logger.error('Please install Executable for spike to proceed further')
-           sys.exit(0)
+      
+       # capture the working directory. Any artifacts that the DUT creates should be placed in this
+       # directory. Other artifacts from the framework and the Reference plugin will also be placed
+       # here itself.
        self.work_dir = work_dir
+       
+       # capture the architectural test-suite directory. 
+       self.suite_dir = suite
 
-       #TODO: The following assumes you are using the riscv-gcc toolchain. If
-       #      not please change appropriately
+       # Note the march is not hardwired here, because it will change for each
+       # test. Similarly the output elf name and compile macros will be assigned later in the
+       # runTests function
        self.compile_cmd = 'riscv{1}-unknown-elf-gcc -march={0} \
-        -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles\
-        -T '+self.pluginpath+'/env/link.ld\
-        -I '+self.pluginpath+'/env/\
-        -I ' + archtest_env
+         -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -g\
+         -T '+self.pluginpath+'/env/link.ld\
+         -I '+self.pluginpath+'/env/\
+         -I ' + archtest_env + ' {2} -o {3} {4}'
 
-       # set all the necessary variables like compile command, elf2hex
-       # commands, objdump cmds. etc whichever you feel necessary and required
-       # for your plugin. 
+       # add more utility snippets here
 
-The `dut_exe` variable in line-2 above, is derived and set in the `__init__` function described
-earlier. This function checks if the `dut_exe` is indeed available and throws an error if not. The
-above template is used for the riscv-gnu-toolchain. If you are using an alternate or custom
-toolchain the `compile_cmd`, in line-10 above, will have  to be changed appropriately.
 
-One can also choose to add moer commands like objdump, elf2hex, etc from line 202 onwards which will
-be used further during the build and run phases.
+This phase is much similar to the setting up command variables in a Makefile. These commands are
+generic and parameterized and can be applied to any test.
 
-build(isa_yaml, platform_yaml)
-------------------------------
+An example of a more complex compile command is provided below,
 
-RISCOF is not limited to validating only a RTL targets, but can also be used to validate
-instruction set simulators (ISS) or modern day core-generators like rocket or chromite. These ISS
-and core generators have the ability to tune themselves to a specific set of configurations as defined in
-the standardized RISCV-CONFIG YAML. Thus, the `build` phase can be used as an intermediate stage to
-build or configure not only these models/targets but also be used to build respective custom tool-chains that may be required.
+.. code-block:: python
+   :linenos:
+
+   self.compile_cmd = 'riscv32-uknown-elf-gcc -march={0} \
+      -static -mcmodel=medany -g -fvisibility=hidden -nostdlib -nostartfiles \
+      -I {1} -I{2} -T{3} -o {4} {5};\
+      riscv32-unknown-elf-objcopy -O binary {4} {4}.bin;\
+      riscv32-unknown-elf-objdump {4} -D > {4}.objdump;\
+      riscv32-unknown-elf-objdump {4} --source > {4}.debug;\
+      riscv32-unknown-elf-readelf -a {4} > {4}.readelf;'
 
-The `build` function provides the following arguments:
+In the above snippet the compile command has 6 variables ( indicated by ``{0}`` to ``{5}``). To
+assign values to these variables in the later stages, one can use the following syntax. Remember the
+order of the arguments in the ``format()`` function below must match the order of variables used
+above. Here the arguments of the format function are strings or variable holding the specified
+information.
+
+.. code-block:: python
+   :linenos:
 
-1. `isa_spec`: This argument holds the path to the validated ISA config YAML. This can be used to extract
+   self.compile_cmd.format(march_str, testsuite_env, dut_env, dut_link.ld, output_elf, input_asm)
+
+If the integer numbering feels uncomfortable, python also allows name-based substitution which would
+like the following:
+
+.. code-block:: python
+   :linenos:
+
+   self.compile_cmd = 'riscv32-uknown-elf-gcc -march={testmarch} \
+      -static -mcmodel=medany -g -fvisibility=hidden -nostdlib -nostartfiles \
+      -I {testenv} -I{dutenv} -T{dutlink} -o {outputelf} {inputasm};\
+      riscv32-unknown-elf-objcopy -O binary {outputelf} {outputelf}.bin;\
+      riscv32-unknown-elf-objdump {outputelf} -D > {outputelf}.objdump; \
+      riscv32-unknown-elf-objdump {outputelf} --source > {outputelf}.debug; \
+      riscv32-unknown-elf-readelf -a {outputelf} > {outputelf}.readelf;'
+
+   self.compile_cmd.format(testmarch=march_str, testenv=testsuite_env, dutenv=dut_env,
+   dutlink=dut_link.ld, outputelf=output_elf, inputasm=input_asm)
+
+build(self, isa_yaml, platform_yaml)
+------------------------------------
+
+This function is primarily meant for building or configuring the DUT (or its runtime arguments) if 
+required. This is particularly useful when working with core-generators. This stage can be used to 
+generate a specific configuration of the DUT leveraging the specs available in the checked 
+ISA and Platform yamls. For example in the case of spike, we can use the ISA yaml to create the
+appropriate value of the ``--isa`` argument used by spike.
+
+Apart, from configuring the DUT this stage can also be used to check if all the commands required by
+the DUT for successful execution are available or not. For example checking if the compiler is
+installed, the dut_exe executable is available, etc.
+
+To enable the above actions the `build` function provides the following arguments to the user:
+
+1. `isa_spec`: This argument holds the absolute path to the validated ISA config YAML. This can be used to extract
    various fields from the YAML (e.g. ISA) and configure the DUT accordingly.
-2. `platform_spec`: This argument holds the path to the validated PLATFORM config YAML and can be used
+2. `platform_spec`: This argument holds the absolute path to the validated PLATFORM config YAML and can be used
    similarly as above.
 
+Some of the parameters of interest that can be captured in this stage using the isa yaml are:
+
+- the xlen value: this can be obtained from the max value in the ``supported_xlen`` field of the 
+  yaml. This is particularly useful in setting the compiler integer number we discussed before and
+  also for setting other DUT specific parameters (like the ``--isa`` argument of spike). Shown in
+  line 9 below.
+- the isa string: for simulators like spike, we can parse this to generate the string for the
+  ``--isa`` argument. Shown in lines 13-19 below.
+
+.. hint:: **PYTHON-HINT**: one can access dictionary elements using the square braces ``[]``.
+
+
+.. note:: For pre-compiled/configured RTL targets this phase is typically empty and no actions are 
+   required. Though, one could choose to compile the RTL in this phase if required using simulators
+   like verilator, vcs, etc.
+
 An example of this function for an ISS like spike is show below:
 
 .. code-block:: python
    :linenos:
 
-   def build(self, isa_spec, platform_spec):
+   def build(self, isa_yaml, platform_yaml):
+
+      # load the isa yaml as a dictionary in python. 
       ispec = utils.load_yaml(isa_yaml)['hart0']
+
+      # capture the XLEN value by picking the max value in 'supported_xlen' field of isa yaml. This
+      # will be useful in setting integer value in the compiler string (if not already hardcoded);
+      # also for setting the '--isa' argument of spike.
       self.xlen = ('64' if 64 in ispec['supported_xlen'] else '32')
+
+      # for spike start building the '--isa' argument. the self.isa is spike specific and may not be
+      # useful for all DUTs
       self.isa = 'rv' + self.xlen
-      #TODO: The following assumes you are using the riscv-gcc toolchain. If
-      #      not please change appropriately
-      self.compile_cmd = self.compile_cmd+' -mabi='+('lp64 ' if 64 in ispec['supported_xlen'] else 'ilp32 ')
       if "I" in ispec["ISA"]:
           self.isa += 'i'
       if "M" in ispec["ISA"]:
           self.isa += 'm'
       if "C" in ispec["ISA"]:
           self.isa += 'c'
+      
+      #TODO: The following assumes you are using the riscv-gcc toolchain. If
+      #      not please change appropriately
+      self.compile_cmd = self.compile_cmd+' -mabi='+('lp64 ' if 64 in ispec['supported_xlen'] else 'ilp32 ')
 
-      # based on the validated isa and platform configure your simulator or
-      # build your RTL here
+runTests(self, testlist)
+------------------------
 
-.. note:: For RTL targets this phase is typically empty and no actions are required. Though, one
-   could choose to compile the RTL in this phase if required.
+This function is responsible for compiling and executing each test on the DUT and produce individual
+signature files, which can later be used for comparison. The function provides a single argument
+which is the ``testList``. This argument is available as a python based dictionary and follows the
+syntax presented in the :ref:`testlist` section.
 
-runTests(testlist)
-------------------
+The only outcome of this function should be a signature file generated for each test. These
+signature files must be located in directory pointed by the ``test_dir`` field of each test in the
+testList. The signature files generated by the DUT must conform to the TestFormatSpec and must be
+named ``DUT-<dut-name>.signature``. In RISCOF, this can be achieved using the ``self.name`` syntax
+and then appending the string ``.signature`` to it.
 
-This function is responsible for executing/running each test on the mode and produce individual
-signature files. A common approach is to create a simple Makefile with each test as a target using
-the commands and initializations done during the build and initialization phase. RISCOF also
-provides a simple `makeUtil` utility function which can be used directly, however, users are free to
-define their own execution environments. After generating the Makefile, the users should also
-call the ``make`` or suitable command to execute the run.
+Also note, the contents of the signature file must conform to specification mentioned in the 
+TestFormat Spec `here
+<https://github.com/riscv/riscv-arch-test/blob/master/spec/TestFormatSpec.adoc#36-the-test-signature>`_
 
-The function takes a single argument: `testlist` which is a dictionary of tests and respective meta
-informations. The format of the testlist is available here: :ref:`testlist`.
+There are multiple ways of defining this function. We will start with the most simplest version and
+move on to more involved variants.
 
-At the end of execution of this function it is expected that each test has a signature file available 
-in the respective work_dir. The signature file generated should be named : ``self.name[:-1].+"signature"``
+Using Shell Commands
+^^^^^^^^^^^^^^^^^^^^
 
-A sample of this function which uses the ``shellCommand`` utility for compiling, executing and
-renaming the signature file. The function essentially iterates over all the tests in a sequence
-performing the same commands.
+In this variant we will build a simple function which will spawn off individual shell commands to
+compile the test, run the test and collect/post-process the signature of each test. An example of
+this script is provided below.
 
+.. hint:: **PYTHON-HINT**: To display progress on the terminal it is often good to have some print
+   statements in the code. In this plugin we use the logger library from python to achieve this.
+   Syntax for usage is::
+
+     logger.debug('My Progress here')
+
+   The keyword 'debug' above indicates that the above statement will be displayed on the terminal
+   only when the ``--verbose`` cli argument is set to "debug". Similarly one can create warning and
+   error statements (which will be printed in different colors and enabled via the cli)::
+
+     logger.warning('This is enabled when verbose is debug or warning')
+     logger.error('This is enabled when verbose is debug, warning or error')
 
 .. code-block:: python
   :linenos:
 
   def runTests(self, testList):
-      for file in testList:
-          testentry = testList[file]
+      
+      # we will iterate over each entry in the testList. Each entry node will be referred to by the
+      # variable testname.
+      for testname in testList:
+        
+          # for each testname we get all its fields (as described by the testList format)
+          testentry = testList[testname]
+
+          # we capture the path to the assembly file of this test
           test = testentry['test_path']
+
+          # capture the directory where the artifacts of this test will be dumped/created.
           test_dir = testentry['work_dir']
 
+          # name of the elf file after compilation of the test
           elf = 'my.elf'
+
+          # name of the signature file as per requirement of RISCOF. RISCOF expects the signature to
+          # be named as DUT-<dut-name>.signature. The below variable creates an absolute path of
+          # signature file.
           sig_file = os.path.join(test_dir, self.name[:-1] + ".signature")
+          
+          # for each test there are specific compile macros that need to be enabled. The macros in
+          # the testList node only contain the macros/values. For the gcc toolchain we need to
+          # prefix with "-D". The following does precisely that.
+          compile_macros= ' -D' + " -D".join(testentry['macros'])
 
-          cmd = self.compile_cmd.format(testentry['isa'].lower(), self.xlen) + ' ' + test + ' -o ' + elf
-          compile_cmd = cmd + ' -D' + " -D".join(testentry['macros'])
+          # collect the march string required for the compiler
+          marchstr = testentry['isa'].lower()
+
+          # substitute all variables in the compile command that we created in the initialize
+          # function
+          cmd = self.compile_cmd.format(marchstr, self.xlen, test, elf, compile_macros)
+
+          # just a simple logger statement that shows up on the terminal
           logger.debug('Compiling test: ' + test)
-          utils.shellCommand(compile_cmd).run(cwd=test_dir)
 
-          execute = spike_path + 'spike --isa={0} +signature={1} +signature-granularity=4 {2}'.format(self.isa, sig_file, elf)
-          logger.debug('Executing on Spike ' + execute)
-          utils.shellCommand(execute).run(cwd=test_dir)
+          # the following command spawns a process to run the compile command. Note here, we are
+          # changing the directory for this command to that pointed by test_dir. If you would like
+          # the artifacts to be dumped else where change the test_dir variable to the path of your
+          # choice.
+          utils.shellCommand(cmd).run(cwd=test_dir)
+
+	        # if the user wants to disable running the tests and only compile the tests, then
+	        # the if condition is skipped
+          if self.target_run:
+            # build the command for running the elf on the DUT. In this case we use spike and indicate
+            # the isa arg that we parsed in the build stage, elf filename and signature filename.
+            # Template is for spike. Please change for your DUT
+            execute = self.dut_exe + ' --isa={0} +signature={1} +signature-granularity=4 {2}'.format(self.isa, sig_file, elf)
+            logger.debug('Executing on Spike ' + execute)
+
+            # launch the execute command. Change the test_dir if required.
+            utils.shellCommand(execute).run(cwd=test_dir)
+
+          # post-processing steps can be added here in the template below
+          #postprocess = 'mv {0} temp.sig'.format(sig_file)'
+          #utils.shellCommand(postprocess).run(cwd=test_dir)
+
+      # if target runs are not required then we simply exit as this point after running all 
+      # the makefile targets. 
+      if not self.target_run:
+        raise SystemExit
 
-An example which uses the ``makeUtil`` utility is show below. Here a Makefile is first generated
-where every test is a make target. the utility automatically creates the relevant targets and only
-requires the user to define what should occur under each target.
+As mentioned earlier, the `-march` string is test-specific and needs to be collected from the
+testList fields. Line-30 above, shows that ``testentry['isa']`` provides this information. 
+
+.. hint:: **PYTHON-HINT**: the lower() function in line-30 above is used to reduce all the
+   characters of a string to lowercase
+
+Note, that as the toolchain and tests evolves, one might need to manipulate this string 
+before assigning it to the march argument of the compiler. 
+
+At times, for debug purposes or initial bring up purposes one might want to just compile the tests
+and not run them on the DUT. In order to achieve this, one can set the
+``target_run`` parameter in the ``config.ini`` file to 0. This will cause lines
+47-55 to be skipped and thereby skip from running tests on the target.
+
+
+.. hint:: **PYTHON-HINT**: Note in python we use ``#`` for comments. Also note, that python uses
+   indentation to indicate a block of code (hence the indentation of lines 7 through 58). 
+
+Makefile Flow (Recommended)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+While the previous solution is small and precise, it offers very less debug artifacts. In this
+variant we will be generating a single Makefile which can be used outside RISCOF as well to run a
+particular or a collection of tests.
+
+The Makefile generated here will have as many targets as there are tests, and each make-target will
+correspond to having commands which will compile the test, run on the dut and collect the signature.
+To provide ease in creating such a Makefile, RISCOF provides a makeUtility which can be used in this
+function.
+
+.. tip:: if one is more well-versed with python, you can choose to create the Makefile differently
+   with more custom targets. However, note that the make utility provided from RISCOF might not 
+   work for custom Makefiles.
+
+An example of the runTests function which uses the ``makeUtil`` utility is shown below. 
+Here a Makefile is first generated where every test is a make target. The utility 
+automatically creates the relevant targets and only requires the user to define what should 
+occur under each target.
 
 The user can choose to use a different make command by setting
 the ``make.makeCommand``. More details of this utility are available at: :ref:`utils`
@@ -319,34 +656,477 @@ the ``make.makeCommand``. More details of this utility are available at: :ref:`u
   :linenos:
 
   def runTests(self, testList):
+      
+      # create an instance the makeUtil class that we will use to create targets.
       make = utils.makeUtil(makefilePath=os.path.join(self.work_dir, "Makefile." + self.name[:-1]))
+
+      # set the make command that will be used. The num_jobs parameter was set in the __init__
+      # function earlier
       make.makeCommand = 'make -j' + self.num_jobs
-      for file in testList:
-          testentry = testList[file]
+
+      # we will iterate over each entry in the testList. Each entry node will be refered to by the
+      # variable testname.
+      for testname in testList:
+        
+          # for each testname we get all its fields (as described by the testList format)
+          testentry = testList[testname]
+
+          # we capture the path to the assembly file of this test
           test = testentry['test_path']
+
+          # capture the directory where the artifacts of this test will be dumped/created. RISCOF is
+          # going to look into this directory for the signature files
           test_dir = testentry['work_dir']
 
-          elf = 'dut.elf'
+          # name of the elf file after compilation of the test
+          elf = 'my.elf'
+
+          # name of the signature file as per requirement of RISCOF. RISCOF expects the signature to
+          # be named as DUT-<dut-name>.signature. The below variable creates an absolute path of
+          # signature file.
+          sig_file = os.path.join(test_dir, self.name[:-1] + ".signature")
+          
+          # for each test there are specific compile macros that need to be enabled. The macros in
+          # the testList node only contain the macros/values. For the gcc toolchain we need to
+          # prefix with "-D". The following does precisely that.
+          compile_macros= ' -D' + " -D".join(testentry['macros'])
           
-          execute = "@cd "+testentry['work_dir']+";"
+          # substitute all variables in the compile command that we created in the initialize
+          # function
+          cmd = self.compile_cmd.format(testentry['isa'].lower(), self.xlen, test, elf, compile_macros)
+
+	        # if the user wants to disable running the tests and only compile the tests, then
+	        # the "else" clause is executed below assigning the sim command to simple no action
+	        # echo statement. 
+          if self.target_run:
+            # set up the simulation command. Template is for spike. Please change.
+            simcmd = self.dut_exe + ' --isa={0} +signature={1} +signature-granularity=4 {2}'.format(self.isa, sig_file, elf)
+          else:
+            simcmd = 'echo "NO RUN"'
+
+          # concatenate all commands that need to be executed within a make-target.
+          execute = '@cd {0}; {1}; {2};'.format(testentry['work_dir'], cmd, simcmd)
+
+          # create a target. The makeutil will create a target with the name "TARGET<num>" where num
+          # starts from 0 and increments automatically for each new target that is added
+          make.add_target(execute)
+      
+      # if you would like to exit the framework once the makefile generation is complete uncomment the
+      # following line. Note this will prevent any signature checking or report generation.
+      #raise SystemExit
 
-          cmd = self.compile_cmd.format(testentry['isa'].lower(), self.xlen) + ' ' + test + ' -o ' + elf
+      # once the make-targets are done and the makefile has been created, run all the targets in
+      # parallel using the make command set above.
+      make.execute_all(self.work_dir)
+  
+      # if target runs are not required then we simply exit as this point after running all 
+      # the makefile targets. 
+      if not self.target_run:
+          raise SystemExit
 
-          #TODO: we are using -D to enable compile time macros. If your
-          #      toolchain is not riscv-gcc you may want to change the below code
-          compile_cmd = cmd + ' -D' + " -D".join(testentry['macros'])
-          execute+=compile_cmd+";"
 
-          sig_file = os.path.join(test_dir, self.name[:-1] + ".signature")
+.. include:: ../../PLUGINS.rst
 
-          #TODO: You will need to add any other arguments to your DUT
-          #      executable if any in the quotes below
-          execute += self.dut_exe + ' --log-commits --log dump --isa={0} +signature={1} +signature-granularity=4 {2};'.format(self.isa, sig_file, elf)
+Using the Target files from existing framework with riscof
+==========================================================
+To ease transition from the old framework, the ``makeplugin`` is provided in the IncorePlugins
+repository.
+
+Setup
+-----
+
+1. Clone the repository using the following command.
+
+    .. code-block:: shell
+
+        git clone https://gitlab.com/incoresemi/riscof-plugins.git
+
+2. Modify the following values in the ``config.ini``
+
+    .. code-block:: ini
+
+        DUTPlugin=makeplugin
+        DUTPluginPath=<path-to-riscof-plugins>/makeplugin 
+
+3. Add the following node to the ``config.ini``.
+
+   .. code-block:: ini
+
+        [makeplugiun]
+        # To specify multiple files use comma separated paths
+        makefiles=<path-to-makefile.includes>
+        ispec=<path-to-isa-yaml-file>
+        pspec=<path-to-platform-yaml-file>
+
+Modifying the makefile
+----------------------
+The commands in the makefile need to be modified such that the variables from the following tables
+are used in the commands. These variables shall be replaced with the appropriate values in the
+``RUN_TARGET`` and ``COMPILE_TARGET`` commands.
+
+.. list-table:: 
+    :header-rows: 1
+
+    * - Variable Name
+      - Description
+    * - ``${target_dir}``
+      - The directory where the plugin file resides. (riscof_makeplugin.py))
+    * - ``${asm}``
+      - Absolute path to the assemble test file i.e the .S file for the test.
+    * - ``${work_dir}``
+      - The absolute path to the work directory for the test.
+    * - ``${test_name}``
+      - The name of the test, for example add-01 etc. Can be used for naming any intermediate files generated.
+    * - ``${include}``
+      - The path to the directory which containts the test header files. This needs to be specified as an include path in the compile command.
+    * - ``${march}``
+      - The ISA to be used for compiling the test. This is in the format expected by march argument of gcc.
+    * - ``${mabi}``
+      - The abi to be used for compiling the test. This is in the format expected by mabi argument of gcc.
+    * - ${target_isa}
+      - This is the ISA specified in the input ISA yaml. The idea is that it can be used to configure the model at run time via cli arguments if necessary.
+    * - ``${test_bin}``
+      - The name of the binary file to be created after compilation. Can be ignored. Custom names can be used as long as the ``RUN_TARGET`` command picks up the correct binary to execute on the target.
+    * - ``${signature_file}``
+      - The absolute path to the signature file. This path cannot be changed and the signature file should be present at this path for riscof to verify at the end of testing.
+    * - ``${macros}``
+      - The macros to be defined while compilation. Currently they are in the format expected by gcc i.e. ``-D <macro-name>=<macro-value>``
+
+**Example**:
+
+The Makefile.include for the SAIL C Simulator from 
+`here <https://github.com/riscv/riscv-arch-test/blob/2.4.4/riscv-target/sail-riscv-c/device/rv32i_m/I/Makefile.include>`_ 
+is used as a reference for this example.
+
+.. code-block:: Makefile
+    :linenos:    
+
+    TARGET_SIM   ?= riscv_sim_RV32 -V
+    TARGET_FLAGS ?= $(RISCV_TARGET_FLAGS)
+    ifeq ($(shell command -v $(TARGET_SIM) 2> /dev/null),)
+        $(error Target simulator executable '$(TARGET_SIM)` not found)
+    endif
+    
+    RUN_CMD=\
+        $(TARGET_SIM) $(TARGET_FLAGS) \
+            --test-signature=$(*).signature.output \
+            $(<) 
+    
+    RISCV_PREFIX   ?= riscv32-unknown-elf-
+    RISCV_GCC      ?= $(RISCV_PREFIX)gcc
+    RISCV_OBJDUMP  ?= $(RISCV_PREFIX)objdump
+    RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles $(RVTEST_DEFINES)
+    
+    COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
+    							-I$(ROOTDIR)/riscv-test-suite/env/ \
+    							-I$(TARGETDIR)/$(RISCV_TARGET)/ \
+    							-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
+    							$$(<) -o $$@ 
+    OBJ_CMD = $$(RISCV_OBJDUMP) $$@ -D > $$@.objdump; \
+    					$$(RISCV_OBJDUMP) $$@ --source > $$@.debug
+    
+    
+    COMPILE_TARGET=\
+    				$(COMPILE_CMD); \
+            if [ $$$$? -ne 0 ] ; \
+                    then \
+                    echo "\e[31m$$(RISCV_GCC) failed for target $$(@) \e[39m" ; \
+                    exit 1 ; \
+                    fi ; \
+    				$(OBJ_CMD); \
+            if [ $$$$? -ne 0 ] ; \
+                    then \
+                    echo "\e[31m $$(RISCV_OBJDUMP) failed for target $$(@) \e[39m" ; \
+                    exit 1 ; \
+                    fi ;
+    
+    RUN_TARGET=\
+    	$(RUN_CMD) 
+
+The first order of business is to move the ``COMPILE_CMD`` and ``RUN_CMD`` and define the contents
+in the ``COMPILE_TARGET`` and ``RUN_TARGET`` respectively as these are the only commands where the
+values will be substituted by the python function. Hence the respective variables look like this:
+
+.. code-block:: Makefile
+    :linenos:    
+
+    COMPILE_CMD = $$(RISCV_GCC) $(1) $$(RISCV_GCC_OPTS) \
+    							-I$(ROOTDIR)/riscv-test-suite/env/ \
+    							-I$(TARGETDIR)/$(RISCV_TARGET)/ \
+    							-T$(TARGETDIR)/$(RISCV_TARGET)/link.ld \
+    							$$(<) -o $$@ 
+
+    RUN_CMD=\
+        $(TARGET_SIM) $(TARGET_FLAGS) \
+            --test-signature=$(*).signature.output \
+            $(<) 
+
+Then these commands are rewritten to work with the python substitution variables. Hence variables
+such as ``$$(<)`` are replaced with ``${asm}`` in compile and ``$test_bin`` in the run commands. The
+``$$@`` in compile is replaced with ``${test_bin}``. This ensures that the binary file is
+appropriately created. The values for ``march`` and ``mabi`` was defied in the old framework in the
+makefiles for the suite. These values are provided per target in riscof. Hence the ``$(1)`` is
+replaced with ``-march=${march} -mabi=${mabi}``. 
+
+The directory with the header files for the tests is also provided by riscof. Hence line 2 is
+replaced with ``-I${include} \``. The paths in lines 3 and 4 are fixed to the appropriate ones by
+using the directory where the plugin file is present as an anchor. Riscof also provides macro
+definitions for the tests too and the plugin generates these macros in the format required by gcc.
+Hence ``${macro}`` is added to the end of the compile command.
+
+Similarly the path to the signature file in line 9 is also replaced with ``${signature_file}`` to
+ensure correct path. Since ``$(RVTEST_DEFINES)`` is no longer available, it is removed from
+``RISCV_GCC_OPTS`` on line 15 in the first snippet. Finally, all conditional code is cleanned up and
+the final makefile looks like this:
+
+.. code-block:: Makefile
+    :linenos:
 
-          make.add_target(execute)
-      make.execute_all(self.work_dir)
+    TARGET_SIM   ?= riscv_sim_RV32 -V
+    TARGET_FLAGS =
+    
+    RISCV_PREFIX   ?= riscv32-unknown-elf-
+    RISCV_GCC      ?= $(RISCV_PREFIX)gcc
+    RISCV_OBJDUMP  ?= $(RISCV_PREFIX)objdump
+    RISCV_GCC_OPTS ?= -g -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles
+    
+    COMPILE_TARGET=\
+                $$(RISCV_GCC) -march=${march} -mabi=${mabi} $$(RISCV_GCC_OPTS) \
+    							-I${include} \
+    							-I${target_dir}/env/ \
+    							-T${target_dir}/env/link.ld \
+    						    ${asm} -o ${test_bin} ${macros}
+    
+    RUN_TARGET=\
+        $(TARGET_SIM) $(TARGET_FLAGS)\
+            --test-signature=${signature_file} \
+            ${test_bin} 
+
+To add the disassembly of the test as an artifact of the run, the ``COMPILE_TARGET`` can be modified
+to the following:
+
+.. code-block:: Makefile
+    :linenos:
 
-.. include:: ../../PLUGINS.rst
+    COMPILE_TARGET=\
+                $$(RISCV_GCC) -march=${march} -mabi=${mabi} $$(RISCV_GCC_OPTS) \
+    							-I${include} \
+    							-I${target_dir}/env/ \
+    							-T${target_dir}/env/link.ld \
+    						    ${asm} -o ${test_bin} ${macros};\
+            $$(RISCV_OBJDUMP) ${test_bin} -D > ${test_name}.disass; \
+    					$$(RISCV_OBJDUMP) ${test_bin} --source > ${test_name}.debug
+
+.. note::
+    To ensure that a ``$`` is printed in the output Makefile (like ``$(RISCV_GCC)``) ensure that a
+    ``$$`` is present in the input makefile.
+
+Plugin Function Explanation
+---------------------------
+
+\_\_init\_\_(self, *args, **kwargs)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: python
+    :linenos:
+
+    def __init__(self, *args, **kwargs):
+        sclass = super().__init__(*args, **kwargs)
+
+        # Get the node for the plugin in the config.ini file. This is extracted by riscof and only
+        # the node relevant to the plugin is passed.
+        config = kwargs.get('config')
+
+        # Extract all information from the nodes. If any required values are missing, an error and a
+        # system exit is raised.
+        if 'makefiles' not in config:
+            logger.error("Path to the Makefiles not specified for "+self.__model__)
+            raise SystemExit
+        if 'ispec' not in config or 'pspec' not in config:
+            logger.error("Path to the input YAML files not specified for "+self.__model__)
+            raise SystemExit
+        # Paths to the Makefile.include files. Mandatory to be provided
+        self.makefiles = [os.path.abspath(path) for path in config['makefiles'].split(",")]
+        # Number of jobs to launch in parallel
+        self.num_jobs = str(config['jobs'] if 'jobs' in config else 1)
+        # Path to the directory in which this file is present
+        self.pluginpath = os.path.dirname(__file__)
+        # Path to the input ISA yaml as per riscv-config format.
+        self.isa_spec = os.path.abspath(config['ispec']) if 'ispec' in config else ''
+        # Path to the input platform yaml as per riscv-config format.
+        self.platform_spec = os.path.abspath(config['pspec']) if 'ispec' in config else ''
+        self.make = config['make'] if 'make' in config else 'make'
+        return sclass
+
+This function extracts the necessary fields from the node for the plugin in the config file given to
+riscof. The plugin supports the following arguments.
+    - **makefiles** (*required*)- Comma separated paths to the makefiles. If multiple are specified, all will be
+      merged in the final output makefile. Note that only the varaibles in the makefiles are written
+      out into the final makefiles. Any targets or includes will be left out. Such cases can be
+      handled by editing the plugin to output the relevant lines as a part of the ``build`` function.
+    - **ispec** (*required*)- The path to the input ISA yaml specification of the target.
+    - **pspec** (*required*)- The path to the input platform yaml specification of the target.
+    - **make** - The make utility to use like make,bmake,pmake etc. (Default is ``make``)
+    - **jobs** - The number of threads to launch parallely. (Default is ``1``)
+
+initialise(self, suite, work_dir, archtest_env)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: python
+    :linenos:
+
+    def initialise(self, suite, work_dir, archtest_env):
+        # Store the path to the suite.
+        self.suite = suite
+        # Store the path to the root level work directory.
+        self.work_dir = work_dir
+        # Store the path to the folder which contains the header files for the tests.
+        self.archtest_env = archtest_env
+
+This function stores the necessary values as variables local to the instance.
+
+
+build(self, isa_yaml, platform_yaml)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: python
+    :linenos:
+
+    def build(self, isa_yaml, platform_yaml):
+        # Extract the configuration of hart0 from isa yaml to figure out the configuration of the
+        # hart being tested.
+        ispec = utils.load_yaml(isa_yaml)['hart0']
+        # Resolve xlen value from the isa yaml
+        self.xlen = ('64' if 64 in ispec['supported_xlen'] else '32')
+        # Store the ISA of the target.
+        self.isa = ispec["ISA"]
+        self.var_dict = {}
+        # Extract all variables from the makefiles
+        for fpath in self.makefiles:
+            self.var_dict.update(getmakevars(fpath))
+        # The path where the Makefile is created
+        self.makefilepath = os.path.join(self.work_dir, "Makefile." + self.name[:-1])
+        with open(self.makefilepath,"w") as fp:
+            # The path to the target directory, i.e the directory where this python file is
+            # present. This variable is written out to the makefile so that other variables can use
+            # this value.
+            fp.write("TARGET_DIR = "+self.pluginpath+"\n")
+            # Write out all values except the COMPILE_TARGET and RUN_TARGET commands
+            for entry in self.var_dict.keys():
+                if not entry.endswith("_TARGET"):
+                    fp.write(entry+" = "+self.var_dict[entry]+"\n")
+                else:
+                    self.var_dict[entry] = Template(self.var_dict[entry])
+
+This function extracts and resolves the values of different fields needed while generating compile
+commands. Line 8, the ISA of the model is extracted from the input ISA yaml. Lines 11 and 12
+extract all variables from the input makefiles. Line 14 generates the absolute path for the
+makefile. The rest of the lines write out all the variables except the ones named ``*_TARGET`` to 
+the output makefile. Line 19 writes out an extra variable ``TARGET_DIR`` which points to the
+directory where the plugin files exist. This variable can be used as an anchor to resolve paths to
+other necessary files (like linker scripts) in the commands.
+
+
+runTests(self, testList,cgf_file=None)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: python
+    :linenos:
+
+    def runTests(self, testList,cgf_file=None):
+        # Initialise the Make Utility from riscof with the output path for the Makefile
+        make = utils.makeUtil(makefilePath=self.makefilepath)
+        # Modify the make command based on the input values in the config file.
+        make.makeCommand = self.make + ' -j' + self.num_jobs
+        # Iterate over all the entries in the test list
+        for entry in testList:
+            # Extract the entry from the testlist
+            testentry = testList[entry]
+            # Extract the path to the assembly test file from the test list entry
+            test = testentry['test_path']
+            # Extract the path to the work directory for the test.
+            test_dir = testentry['work_dir']
+            # Macros to be defined are added in the GCC command format
+            # -D <macro_name>=<macro_value>
+            # Change this if the toolchain uses a different format
+            macros = ' -D' + " -D".join(testentry['macros'])
+            # Variables accessible in the *_TARGET commands
+            # Add more variables below if you wish to use these variables in the *_TARGET commands.
+            substitute = {
+                # The path to the target directory, i.e the directory where this python file is
+                # present
+                'target_dir': self.pluginpath,
+                # Path to the test assembly file
+                'asm': test,
+                # Path to the work directory
+                'work_dir': testentry['work_dir'],
+                # Name of the Test. Can be used to name files in the work directory.
+                'test_name': test.rsplit('/',1)[1][:-2],
+                # The path to the env folder containing the header files for the suite. This path
+                # should be passed as an include path in the compile commands.
+                'include': self.archtest_env,
+                # The isa string to be passed to the compiler. The format adheres to the march
+                # argument of GCC
+                'march': testentry['isa'].lower(),
+                # The abi string to be passed to the compiler. The format adheres to the mabi
+                # argument of GCC. To change how this string is derived, change function on line 21.
+                'mabi': mabi(testentry['isa']),
+                # The ISA string present in the input yaml. Can be used to set the ISA of the target
+                'target_isa': self.isa,
+                # Name of the generated binary file. Can be custom.
+                'test_bin': 'ref.elf',
+                # Name of the signature file. Note the name of the file should be in the same
+                # particular format and inside the test work directory.
+                'signature_file': os.path.join(test_dir, self.name[:-1] + ".signature"),
+                # The string which specifies all the macros to be defined for the test. As computed
+                # in line 108.
+                'macros': macros
+            }
+
+            # Construct the command for the test and add a target in the makefile. The format of the
+            # command is as follows:
+            # cd <work_directory>;substitute(COMPILE_TARGET);substitute(RUN_TARGET);
+            # The RUN_TARGET is optional and can be skipped if the same is not defined in the input
+            # makefile
+            execute = "@cd "+testentry['work_dir']+";"
+
+            compile_cmd = self.var_dict['COMPILE_TARGET'].safe_substitute(substitute)
+            execute+=compile_cmd+";"
+            if 'RUN_TARGET' in self.var_dict:
+                run_cmd = self.var_dict['RUN_TARGET'].safe_substitute(substitute)
+                execute+=run_cmd+";"
+            # Add target in the makefile for the test
+            make.add_target(execute,)
+        # Execute all targets.
+        make.execute_all(self.work_dir)
+
+This function uses the ``makeUtil`` provided by ``riscof.utils`` to write out a Makefile with the
+commands for each entry in the testlist. The format of the command for each target is 
+``cd <work_directory>;substitute(COMPILE_TARGET);substitute(RUN_TARGET);``. Lines 9 to 49 extract 
+and setup the values of the necessary variables for substitution. This function uses the `template 
+substitution <https://docs.python.org/3/library/string.html#template-strings>`_ provided by the 
+``string`` class of python. The values of the variables in the template strings are defined in a
+dictionary(``substitute``) and the substitution is performed for the ``COMPILE_TARGET`` on line 58.
+Similarly if ``RUN_TARGET`` is defined in the input makefile, the substitution for the same is done
+on line 61. Finally the target is added to the makefile and all targets are executed.
+
+
+Tips
+====
+
+1. Avoid writing out multiple ``;`` simultaneously in the Makefiles.
+2. Use the template substitution provided by the ``string`` class in python instead of string
+   operations to ease command generation and avoid formatting errors. `This <https://askpython.com/python/string/python-template-strings>`_ article provides a good
+   overview on the same.
+3. It is advisable to use the ``logger`` provided by ``riscof.utils`` for logging/printing
+   information to the console.
+4. Ensure to add space between multiple arguments in a command to avoid execution errors.
+
+   .. code-block:: python
+
+        ## Avoid this. The generated command is wrong and will cause an execution error due to a
+        ## missing space before -T
+        execute += 'riscv64-unknown-elf-gcc'+'-T bin/link.ld'
 
 
 Other Utilities available
diff --git a/riscof/Templates/setup/model/riscof_model.py b/riscof/Templates/setup/model/riscof_model.py
index 8967352..fddb267 100644
--- a/riscof/Templates/setup/model/riscof_model.py
+++ b/riscof/Templates/setup/model/riscof_model.py
@@ -26,80 +26,224 @@ def __init__(self, *args, **kwargs):
 
         config = kwargs.get('config')
 
-        self.dut_exe = os.path.join(config['PATH'] if 'PATH' in config else "","dutname")
-        self.num_jobs = str(config['jobs'] if 'jobs' in config else 1)
+        # If the config node for this DUT is missing or empty. Raise an error. At minimum we need
+        # the paths to the ispec and pspec files
         if config is None:
             print("Please enter input file paths in configuration.")
             raise SystemExit
+
+        # In case of an RTL based DUT, this would be point to the final binary executable of your
+        # test-bench produced by a simulator (like verilator, vcs, incisive, etc). In case of an iss or
+        # emulator, this variable could point to where the iss binary is located. If 'PATH variable
+        # is missing in the config.ini we can hardcode the alternate here.
+        self.dut_exe = os.path.join(config['PATH'] if 'PATH' in config else "","dutname")
+
+        # Number of parallel jobs that can be spawned off by RISCOF
+        # for various actions performed in later functions, specifically to run the tests in
+        # parallel on the DUT executable. Can also be used in the build function if required.
+        self.num_jobs = str(config['jobs'] if 'jobs' in config else 1)
+
+        # Path to the directory where this python file is located. Collect it from the config.ini
+        self.pluginpath=os.path.abspath(config['pluginpath'])
+
+        # Collect the paths to the  riscv-config absed ISA and platform yaml files. One can choose
+        # to hardcode these here itself instead of picking it from the config.ini file.
+        self.isa_spec = os.path.abspath(config['ispec'])
+        self.platform_spec = os.path.abspath(config['pspec'])
+
+        #We capture if the user would like the run the tests on the target or
+        #not. If you are interested in just compiling the tests and not running
+        #them on the target, then following variable should be set to False
+        if 'target_run' in config and config['target_run']=='0':
+            self.target_run = False
         else:
-            self.isa_spec = os.path.abspath(config['ispec'])
-            self.platform_spec = os.path.abspath(config['pspec'])
-            self.pluginpath=os.path.abspath(config['pluginpath'])
+            self.target_run = True
 
+        # Return the parameters set above back to RISCOF for further processing.
         return sclass
 
     def initialise(self, suite, work_dir, archtest_env):
-        if shutil.which(self.dut_exe) is None:
-            logger.error(self.dut_exe+' Not Found')
-            logger.error('Please install Executable for dutname to proceed further')
-            sys.exit(0)
-        self.work_dir = work_dir
-
-        #TODO: The following assumes you are using the riscv-gcc toolchain. If
-        #      not please change appropriately
-        self.compile_cmd = 'riscv{1}-unknown-elf-gcc -march={0} \
-         -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles\
+
+       # capture the working directory. Any artifacts that the DUT creates should be placed in this
+       # directory. Other artifacts from the framework and the Reference plugin will also be placed
+       # here itself.
+       self.work_dir = work_dir
+
+       # capture the architectural test-suite directory.
+       self.suite_dir = suite
+
+       # Note the march is not hardwired here, because it will change for each
+       # test. Similarly the output elf name and compile macros will be assigned later in the
+       # runTests function
+       self.compile_cmd = 'riscv{1}-unknown-elf-gcc -march={0} \
+         -static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -g\
          -T '+self.pluginpath+'/env/link.ld\
          -I '+self.pluginpath+'/env/\
-         -I ' + archtest_env
+         -I ' + archtest_env + ' {2} -o {3} {4}'
 
-        # set all the necessary variables like compile command, elf2hex
-        # commands, objdump cmds. etc whichever you feel necessary and required
-        # for your plugin. 
+       # add more utility snippets here
 
     def build(self, isa_yaml, platform_yaml):
-        ispec = utils.load_yaml(isa_yaml)['hart0']
-        self.xlen = ('64' if 64 in ispec['supported_xlen'] else '32')
-        self.isa = 'rv' + self.xlen
-        #TODO: The following assumes you are using the riscv-gcc toolchain. If
-        #      not please change appropriately
-        self.compile_cmd = self.compile_cmd+' -mabi='+('lp64 ' if 64 in ispec['supported_xlen'] else 'ilp32 ')
-        if "I" in ispec["ISA"]:
-            self.isa += 'i'
-        if "M" in ispec["ISA"]:
-            self.isa += 'm'
-        if "C" in ispec["ISA"]:
-            self.isa += 'c'
-
-        # based on the validated isa and platform configure your simulator or
-        # build your RTL here
-
-    def runTests(self, testList, cgf_file=None):
-        if os.path.exists(self.work_dir+ "/Makefile." + self.name[:-1]):
-            os.remove(self.work_dir+ "/Makefile." + self.name[:-1])
-        make = utils.makeUtil(makefilePath=os.path.join(self.work_dir, "Makefile." + self.name[:-1]))
-        make.makeCommand = 'make -j' + self.num_jobs
-        for file in testList:
-            testentry = testList[file]
-            test = testentry['test_path']
-            test_dir = testentry['work_dir']
 
-            elf = 'dut.elf'
-            
-            execute = "@cd "+testentry['work_dir']+";"
+      # load the isa yaml as a dictionary in python.
+      ispec = utils.load_yaml(isa_yaml)['hart0']
 
-            cmd = self.compile_cmd.format(testentry['isa'].lower(), self.xlen) + ' ' + test + ' -o ' + elf
+      # capture the XLEN value by picking the max value in 'supported_xlen' field of isa yaml. This
+      # will be useful in setting integer value in the compiler string (if not already hardcoded);
+      self.xlen = ('64' if 64 in ispec['supported_xlen'] else '32')
 
-            #TODO: we are using -D to enable compile time macros. If your
-            #      toolchain is not riscv-gcc you may want to change the below code
-            compile_cmd = cmd + ' -D' + " -D".join(testentry['macros'])
-            execute+=compile_cmd+";"
+      # for dutname start building the '--isa' argument. the self.isa is dutnmae specific and may not be
+      # useful for all DUTs
+      self.isa = 'rv' + self.xlen
+      if "I" in ispec["ISA"]:
+          self.isa += 'i'
+      if "M" in ispec["ISA"]:
+          self.isa += 'm'
+      if "C" in ispec["ISA"]:
+          self.isa += 'c'
 
-            sig_file = os.path.join(test_dir, self.name[:-1] + ".signature")
+      #TODO: The following assumes you are using the riscv-gcc toolchain. If
+      #      not please change appropriately
+      self.compile_cmd = self.compile_cmd+' -mabi='+('lp64 ' if 64 in ispec['supported_xlen'] else 'ilp32 ')
 
-            #TODO: You will need to add any other arguments to your DUT
-            #      executable if any in the quotes below
-            execute += self.dut_exe + '' 
+    def runTests(self, testList):
 
-            make.add_target(execute)
-        make.execute_all(self.work_dir)
+      # Delete Makefile if it already exists.
+      if os.path.exists(self.work_dir+ "/Makefile." + self.name[:-1]):
+            os.remove(self.work_dir+ "/Makefile." + self.name[:-1])
+      # create an instance the makeUtil class that we will use to create targets.
+      make = utils.makeUtil(makefilePath=os.path.join(self.work_dir, "Makefile." + self.name[:-1]))
+
+      # set the make command that will be used. The num_jobs parameter was set in the __init__
+      # function earlier
+      make.makeCommand = 'make -j' + self.num_jobs
+
+      # we will iterate over each entry in the testList. Each entry node will be refered to by the
+      # variable testname.
+      for testname in testList:
+
+          # for each testname we get all its fields (as described by the testList format)
+          testentry = testList[testname]
+
+          # we capture the path to the assembly file of this test
+          test = testentry['test_path']
+
+          # capture the directory where the artifacts of this test will be dumped/created. RISCOF is
+          # going to look into this directory for the signature files
+          test_dir = testentry['work_dir']
+
+          # name of the elf file after compilation of the test
+          elf = 'my.elf'
+
+          # name of the signature file as per requirement of RISCOF. RISCOF expects the signature to
+          # be named as DUT-<dut-name>.signature. The below variable creates an absolute path of
+          # signature file.
+          sig_file = os.path.join(test_dir, self.name[:-1] + ".signature")
+
+          # for each test there are specific compile macros that need to be enabled. The macros in
+          # the testList node only contain the macros/values. For the gcc toolchain we need to
+          # prefix with "-D". The following does precisely that.
+          compile_macros= ' -D' + " -D".join(testentry['macros'])
+
+          # substitute all variables in the compile command that we created in the initialize
+          # function
+          cmd = self.compile_cmd.format(testentry['isa'].lower(), self.xlen, test, elf, compile_macros)
+
+	  # if the user wants to disable running the tests and only compile the tests, then
+	  # the "else" clause is executed below assigning the sim command to simple no action
+	  # echo statement.
+          if self.target_run:
+            # set up the simulation command. Template is for spike. Please change.
+            simcmd = self.dut_exe + ' --isa={0} +signature={1} +signature-granularity=4 {2}'.format(self.isa, sig_file, elf)
+          else:
+            simcmd = 'echo "NO RUN"'
+
+          # concatenate all commands that need to be executed within a make-target.
+          execute = '@cd {0}; {1}; {2};'.format(testentry['work_dir'], cmd, simcmd)
+
+          # create a target. The makeutil will create a target with the name "TARGET<num>" where num
+          # starts from 0 and increments automatically for each new target that is added
+          make.add_target(execute)
+
+      # if you would like to exit the framework once the makefile generation is complete uncomment the
+      # following line. Note this will prevent any signature checking or report generation.
+      #raise SystemExit
+
+      # once the make-targets are done and the makefile has been created, run all the targets in
+      # parallel using the make command set above.
+      make.execute_all(self.work_dir)
+
+      # if target runs are not required then we simply exit as this point after running all
+      # the makefile targets.
+      if not self.target_run:
+          raise SystemExit
+
+#The following is an alternate template that can be used instead of the above.
+#The following template only uses shell commands to compile and run the tests.
+
+#    def runTests(self, testList):
+#
+#      # we will iterate over each entry in the testList. Each entry node will be referred to by the
+#      # variable testname.
+#      for testname in testList:
+#
+#          logger.debug('Running Test: {0} on DUT'.format(testname))
+#          # for each testname we get all its fields (as described by the testList format)
+#          testentry = testList[testname]
+#
+#          # we capture the path to the assembly file of this test
+#          test = testentry['test_path']
+#
+#          # capture the directory where the artifacts of this test will be dumped/created.
+#          test_dir = testentry['work_dir']
+#
+#          # name of the elf file after compilation of the test
+#          elf = 'my.elf'
+#
+#          # name of the signature file as per requirement of RISCOF. RISCOF expects the signature to
+#          # be named as DUT-<dut-name>.signature. The below variable creates an absolute path of
+#          # signature file.
+#          sig_file = os.path.join(test_dir, self.name[:-1] + ".signature")
+#
+#          # for each test there are specific compile macros that need to be enabled. The macros in
+#          # the testList node only contain the macros/values. For the gcc toolchain we need to
+#          # prefix with "-D". The following does precisely that.
+#          compile_macros= ' -D' + " -D".join(testentry['macros'])
+#
+#          # collect the march string required for the compiler
+#          marchstr = testentry['isa'].lower()
+#
+#          # substitute all variables in the compile command that we created in the initialize
+#          # function
+#          cmd = self.compile_cmd.format(marchstr, self.xlen, test, elf, compile_macros)
+#
+#          # just a simple logger statement that shows up on the terminal
+#          logger.debug('Compiling test: ' + test)
+#
+#          # the following command spawns a process to run the compile command. Note here, we are
+#          # changing the directory for this command to that pointed by test_dir. If you would like
+#          # the artifacts to be dumped else where change the test_dir variable to the path of your
+#          # choice.
+#          utils.shellCommand(cmd).run(cwd=test_dir)
+#
+#          # for debug purposes if you would like stop the DUT plugin after compilation, you can
+#          # comment out the lines below and raise a SystemExit
+#
+#          if self.target_run:
+#            # build the command for running the elf on the DUT. In this case we use spike and indicate
+#            # the isa arg that we parsed in the build stage, elf filename and signature filename.
+#            # Template is for spike. Please change for your DUT
+#            execute = self.dut_exe + ' --isa={0} +signature={1} +signature-granularity=4 {2}'.format(self.isa, sig_file, elf)
+#            logger.debug('Executing on Spike ' + execute)
+#
+#          # launch the execute command. Change the test_dir if required.
+#          utils.shellCommand(execute).run(cwd=test_dir)
+#
+#          # post-processing steps can be added here in the template below
+#          #postprocess = 'mv {0} temp.sig'.format(sig_file)'
+#          #utils.shellCommand(postprocess).run(cwd=test_dir)
+#
+#      # if target runs are not required then we simply exit as this point after running all
+#      # the makefile targets.
+#      if not self.target_run:
+#          raise SystemExit
diff --git a/riscof/__init__.py b/riscof/__init__.py
index 6874d34..c924aaa 100644
--- a/riscof/__init__.py
+++ b/riscof/__init__.py
@@ -4,4 +4,4 @@
 
 __author__ = """InCore Semiconductors Pvt Ltd"""
 __email__ = 'info@incoresemi.com'
-__version__ = '1.21.1'
+__version__ = '1.21.2'
diff --git a/riscof/cli.py b/riscof/cli.py
new file mode 100644
index 0000000..ac3806e
--- /dev/null
+++ b/riscof/cli.py
@@ -0,0 +1,547 @@
+import click
+
+import logging
+import importlib
+from datetime import datetime
+import os
+import sys
+import pytz
+import shutil
+import configparser
+import distutils.dir_util
+
+from jinja2 import Template
+
+#from riscof.log import *
+from riscof.__init__ import __version__
+import riscv_config.checker as checker
+import riscof.framework.main as framework
+import riscof.framework.test as test_routines
+import riscof.arch_test as archtest
+import riscof.dbgen as dbgen
+import riscof.utils as utils
+import riscof.constants as constants
+from riscof.log import logger
+from riscv_config.errors import ValidationError
+import riscv_isac.coverage as isac
+import riscv_isac
+import riscv_config
+
+class Context:
+    mkdir = True
+    config = None
+    dut = None
+    base = None
+    config_dir = None
+    isa_file = None
+    platform_file = None
+
+def setup_directories(work_dir,skip_rm=False):
+    #Creating work directory
+    if not os.path.exists(work_dir):
+        logger.debug('Creating new work directory: ' + work_dir)
+        os.mkdir(work_dir)
+    elif not skip_rm:
+        logger.debug('Removing old work directory: ' + work_dir)
+        shutil.rmtree(work_dir)
+        logger.debug('Creating new work directory: ' + work_dir)
+        os.mkdir(work_dir)
+
+def read_config(configfile):
+    config = configparser.ConfigParser()
+    logger.info("Reading configuration from: "+configfile)
+    try:
+        config.read(configfile)
+    except FileNotFoundError as err:
+        logger.error(err)
+        raise SystemExit
+    return config,os.path.dirname(os.path.abspath(configfile))
+
+def prepare_models(config_dir,config):
+    riscof_config = config['RISCOF']
+    logger.info("Preparing Models")
+    try:
+        dut_model = riscof_config['DUTPlugin']
+        dut_model_path = utils.absolute_path(config_dir, riscof_config['DUTPluginPath'])
+
+        base_model = riscof_config['ReferencePlugin']
+        base_model_path = utils.absolute_path(config_dir, riscof_config['ReferencePluginPath'])
+    except KeyError as key:
+        logger.error("Error in config file. Possible missing keys.")
+        logger.error(key)
+        raise SystemExit
+
+    logger.debug("Importing " + dut_model + " plugin from: "+str(dut_model_path))
+    sys.path.append(dut_model_path)
+    try:
+        dut_plugin = importlib.import_module("riscof_" + dut_model)
+    except ImportError as msg:
+        logger.error("Error while importing "+dut_model+".\n"+str(msg))
+        raise SystemExit
+    dut_class = getattr(dut_plugin, dut_model)
+    if dut_model in config:
+        dut = dut_class(name="DUT", config=config[dut_model], config_dir=config_dir)
+    else:
+        dut = dut_class(name="DUT")
+
+    logger.debug("Importing " + base_model + " plugin from: "+str(base_model_path))
+    sys.path.append(base_model_path)
+    try:
+        base_plugin = importlib.import_module("riscof_" + base_model)
+    except ImportError as msg:
+        logger.error("Error while importing "+base_model+".\n"+str(msg))
+        raise SystemExit
+    base_class = getattr(base_plugin, base_model)
+    if base_model in config:
+        base = base_class(name="Reference", config=config[base_model], config_dir=config_dir)
+    else:
+        base = base_class(name="Reference")
+    return dut,base
+
+def opt_to_name(opt):
+    return (opt.replace("--","",1)).replace("-","_")
+
+class CustomOption(click.Option):
+    def __init__(self, *args, **kwargs):
+        self.mutually_exclusive = set([opt_to_name(a) for a in kwargs.pop('mutually_exclusive', [])])
+        self.requires = [set([opt_to_name(x) for x in a]) for a in kwargs.pop('requires', []) if a]
+        help = kwargs.get('help', '')
+        if self.mutually_exclusive:
+            ex_str = ', '.join(self.mutually_exclusive)
+            kwargs['help'] = help + (
+                ' NOTE: This argument is mutually exclusive with '
+                ' arguments: [' + ex_str + '].'
+            )
+        if self.requires:
+            ex_str = '], ['.join([", ".join(a) for a in self.requires])
+            kwargs['help'] = help + (
+                ' NOTE: This argument requires one of the following sets of '
+                ' arguments: [' + ex_str + '].'
+            )
+        super(CustomOption, self).__init__(*args, **kwargs)
+
+    def handle_parse_result(self, ctx, opts, args):
+        if self.mutually_exclusive.intersection(opts) and self.name in opts:
+            raise click.UsageError(
+                "Illegal usage: `{}` is mutually exclusive with "
+                "arguments `{}`.".format(
+                    self.name,
+                    ', '.join(self.mutually_exclusive)
+                )
+            )
+        if self.requires:
+            if (not any([a.intersection(opts) == a for a in self.requires])) and self.name in opts:
+                raise click.UsageError(
+                    "Illegal usage: `{}` requires one of the following sets of arguments too "
+                    "arguments [{}].".format(
+                        self.name,
+                        '], ['.join([", ".join(a) for a in self.requires])
+                    )
+                )
+
+        return super(CustomOption, self).handle_parse_result(
+            ctx,
+            opts,
+            args
+        )
+
+
+@click.group()
+@click.version_option(prog_name="RISC-V Architectural Test Framework.",version=__version__)
+@click.option('--verbose', '-v', default='info', help='Set verbose level', type=click.Choice(['info','error','debug'],case_sensitive=False))
+@click.pass_context
+def cli(ctx,verbose):
+    logger.level(verbose)
+    logger.info('****** RISCOF: RISC-V Architectural Test Framework {0} *******'.format(__version__ ))
+    logger.info('using riscv_isac version : ' + str(riscv_isac.__version__))
+    logger.info('using riscv_config version : ' + str(riscv_config.__version__))
+    ctx.obj = Context()
+
+
+
+@cli.command('validateyaml',help= 'Validate the Input YAMLs using riscv-config.')
+@click.option('--config',type= click.Path(resolve_path=True,exists=True),
+                        help='The Path to the config file. [Default=./config.ini]',
+                        metavar= 'PATH',
+                        default='./config.ini')
+@click.option('--work-dir',default="./riscof_work",metavar='PATH',
+        type=click.Path(resolve_path=True,writable=True),
+        help='Path to the work directory.')
+@click.pass_context
+def validate(ctx,config,work_dir):
+    if ctx.obj.mkdir:
+        setup_directories(work_dir)
+        ctx.obj.mkdir = False
+    if ctx.obj.config is None:
+        ctx.obj.config, ctx.obj.config_dir = read_config(config)
+    if ctx.obj.dut is None:
+        ctx.obj.dut,ctx.obj.base = prepare_models(ctx.obj.config_dir,ctx.obj.config)
+    isa_file = ctx.obj.dut.isa_spec
+    platform_file = ctx.obj.dut.platform_spec
+    try:
+        isa_file = checker.check_isa_specs( isa_file, work_dir, True)
+        platform_file = checker.check_platform_specs( platform_file, work_dir, True)
+    except ValidationError as msg:
+        logger.error(msg)
+        raise SystemExit
+    ctx.obj.isa_file = isa_file
+    ctx.obj.platform_file = platform_file
+
+@cli.command('gendb',help='Generate Database for the Suite.')
+@click.option('--suite',required=True,type=click.Path(resolve_path=True,exists=True),metavar="PATH",
+    help="Path to the custom Suite Directory.")
+@click.option('--env',required=True,type=click.Path(resolve_path=True,exists=True),metavar="PATH",
+    help='Path to the env directory for the suite.')
+@click.option('--work-dir',default="./riscof_work",metavar='PATH',
+    type=click.Path(resolve_path=True,writable=True),
+    help='Path to the work directory. [Default = ./riscof_work]')
+@click.pass_context
+def generate(ctx,suite,env,work_dir):
+    if ctx.obj.mkdir:
+        setup_directories(work_dir)
+        ctx.obj.mkdir = False
+
+    logger.info("Generating database for suite: "+suite)
+    constants.suite = suite
+    constants.framework_db = os.path.join(work_dir,"database.yaml")
+    logger.debug('Suite used: '+constants.suite)
+    logger.debug('ENV used: '+ env)
+    dbgen.generate()
+    logger.info('Database File Generated: '+constants.framework_db)
+    constants.env = env
+    logger.info('Env path set to'+constants.env)
+
+@cli.command('testlist',help='Generate the test list for the given DUT and suite.')
+@click.option('--suite',required=True,type=click.Path(resolve_path=True,exists=True),metavar="PATH",
+    help="Path to the custom Suite Directory.")
+@click.option('--env',required=True,type=click.Path(resolve_path=True,exists=True),metavar="PATH",
+    help='Path to the env directory for the suite.')
+@click.option('--config',type= click.Path(resolve_path=True,exists=True),
+                        help='The Path to the config file. [Default=./config.ini]',
+                        metavar= 'PATH',
+                        default='./config.ini')
+@click.option('--work-dir',default="./riscof_work",metavar='PATH',
+        type=click.Path(resolve_path=True,writable=True),
+        help='Path to the work directory. [Default = ./riscof_work]')
+@click.pass_context
+def testlist(ctx,config,work_dir,suite,env):
+    setup_directories(work_dir)
+    ctx.obj.mkdir = False
+    ctx.invoke(generate,suite=suite,env=env,work_dir=work_dir)
+    ctx.obj.gendb = False
+    ctx.obj.validate = False
+    ctx.invoke(validate,config=config,work_dir=work_dir)
+    isa_specs = utils.load_yaml(ctx.obj.isa_file)['hart0']
+    platform_specs = utils.load_yaml(ctx.obj.platform_file)
+    test_routines.generate_test_pool(isa_specs, platform_specs, work_dir)
+
+
+@cli.command('run',help='Run the tests on DUT and reference and compare signatures')
+@click.option('--suite',required=True,type=click.Path(resolve_path=True,exists=True),metavar="PATH",
+    help="Path to the custom Suite Directory.")
+@click.option('--env',required=True,type=click.Path(resolve_path=True,exists=True),metavar="PATH",
+    help='Path to the env directory for the suite.')
+@click.option('--config',type= click.Path(resolve_path=True,exists=True),
+                        help='The Path to the config file. [Default=./config.ini]',
+                        metavar= 'PATH',
+                        default='./config.ini')
+@click.option('--work-dir',default="./riscof_work",metavar='PATH',
+        type=click.Path(resolve_path=True,writable=True),
+        help='Path to the work directory.')
+@click.option('--no-browser',is_flag=True,
+                     help="Do not open the browser for showing the test report.")
+@click.option('--dbfile',type= click.Path(resolve_path=True,exists=True),
+                        help='The Path to the database file.',
+                        metavar= 'PATH',cls=CustomOption,mutually_exclusive=['--testfile'])
+@click.option('--testfile',
+                        type= click.Path(resolve_path=True,exists=True),
+                        help='The Path to the testlist file.',
+                        metavar= 'PATH',cls=CustomOption,mutually_exclusive=['--dbfile'])
+@click.option('--no-ref-run',is_flag=True,help="Do not run tests on Reference")
+@click.option('--no-dut-run',is_flag=True,help="Do not run tests on DUT")
+@click.pass_context
+def run(ctx,config,work_dir,suite,env,no_browser,dbfile,testfile,no_ref_run,no_dut_run):
+    setup_directories(work_dir,(testfile is not None or dbfile is not None))
+    ctx.obj.mkdir = False
+    constants.env = env
+    constants.suite = suite
+    ctx.obj.config, ctx.obj.config_dir = read_config(config)
+    ctx.obj.dut,ctx.obj.base = prepare_models(ctx.obj.config_dir,ctx.obj.config)
+
+    dut = ctx.obj.dut
+    base = ctx.obj.base
+
+    if testfile is not None or dbfile is not None:
+        if dbfile:
+            constants.framework_db = dbfile
+        else:
+            constants.framework_db = os.path.join(work_dir,"database.yaml")
+        isa_file = dut.isa_spec
+        platform_file = dut.platform_spec
+        isa_file = work_dir+ '/' + (isa_file.rsplit('/', 1)[1]).rsplit('.')[0] + "_checked.yaml"
+        platform_file = work_dir+ '/' + (platform_file.rsplit('/', 1)[1]).rsplit('.')[0] + "_checked.yaml"
+        ctx.obj.isa_file = isa_file
+        ctx.obj.platform_file = platform_file
+    else:
+        ctx.invoke(validate,config=config,work_dir=work_dir)
+        ctx.invoke(generate,suite=suite,env=env,work_dir=work_dir)
+
+
+    with open(ctx.obj.isa_file, "r") as isafile:
+        ispecs = isafile.read()
+    isa_specs = utils.load_yaml(ctx.obj.isa_file)['hart0']
+
+    with open(ctx.obj.platform_file, "r") as platfile:
+        pspecs = platfile.read()
+
+    cntr_args = [dbfile,testfile,no_ref_run,no_dut_run]
+
+    report_objects = {}
+    report_objects['date'] = (datetime.now(
+        pytz.timezone('GMT'))).strftime("%Y-%m-%d %H:%M GMT")
+    report_objects['riscof_version'] = __version__
+    report_objects['dut'] = (dut.__model__).replace("_", " ")
+    report_objects['reference'] = (base.__model__).replace("_", " ")
+
+    rvarch, _ = archtest.get_version(constants.suite)
+    report_objects['rvarch_version'] = rvarch['version'] if rvarch['version'] != "-" else \
+                                        rvarch['commit']
+
+    report_objects['isa'] = isa_specs['ISA']
+    report_objects['usv'] = isa_specs['User_Spec_Version']
+    report_objects['psv'] = isa_specs['Privilege_Spec_Version']
+    report_objects['isa_yaml'] = ctx.obj.isa_file
+    report_objects['platform_yaml'] = ctx.obj.platform_file
+    report_objects['isa_specs'] = ispecs
+    report_objects['platform_specs'] = pspecs
+
+    report_objects['results'] = framework.run(dut, base, ctx.obj.isa_file,
+                                              ctx.obj.platform_file, work_dir, cntr_args)
+
+    report_objects['num_passed'] = 0
+    report_objects['num_failed'] = 0
+
+    for entry in report_objects['results']:
+        if entry['res'] == "Passed":
+            report_objects['num_passed'] += 1
+        else:
+            report_objects['num_failed'] += 1
+
+    with open(constants.html_template, "r") as report_template:
+        template = Template(report_template.read())
+
+    output = template.render(report_objects)
+
+    reportfile = os.path.join(work_dir, "report.html")
+    with open(reportfile, "w") as report:
+        report.write(output)
+
+    shutil.copyfile(constants.css,
+                    os.path.join(work_dir, "style.css"))
+
+    logger.info("Test report generated at "+reportfile+".")
+    if not no_browser:
+        try:
+            import webbrowser
+            logger.info("Openning test report in web-browser")
+            webbrowser.open(reportfile)
+        except:
+            raise SystemExit
+
+
+
+@cli.command('coverage',help='Run the tests on DUT and reference and compare signatures')
+@click.option('--suite',required=True,type=click.Path(resolve_path=True,exists=True),metavar="PATH",
+    help="Path to the custom Suite Directory.")
+@click.option('--env',required=True,type=click.Path(resolve_path=True,exists=True),metavar="PATH",
+    help='Path to the env directory for the suite.')
+@click.option('--config',type= click.Path(resolve_path=True,exists=True),
+                        help='The Path to the config file. [Default=./config.ini]',
+                        metavar= 'PATH',
+                        default='./config.ini')
+@click.option('--work-dir',default="./riscof_work",metavar='PATH',
+        type=click.Path(resolve_path=True,writable=True),
+        help='Path to the work directory. [Default = ./riscof_work]')
+@click.option('--no-browser',is_flag=True,
+                     help="Do not open the browser for showing the test report.")
+@click.option(
+        '--cgf-file','-c',multiple=True,
+        type=click.Path(resolve_path=True,readable=True,exists=True),
+        help="Coverage Group File(s). Multiple allowed.",required=True
+    )
+@click.pass_context
+def coverage(ctx,config,work_dir,suite,env,no_browser,cgf_file):
+    setup_directories(work_dir)
+    ctx.obj.mkdir = False
+    ctx.obj.config, ctx.obj.config_dir = read_config(config)
+    ctx.obj.dut,ctx.obj.base = prepare_models(ctx.obj.config_dir,ctx.obj.config)
+
+    dut = ctx.obj.dut
+    base = ctx.obj.base
+    ctx.invoke(validate,config=config,work_dir=work_dir)
+    ctx.invoke(generate,suite=suite,env=env,work_dir=work_dir)
+    logger.info('Will collect Coverage using RISCV-ISAC')
+    logger.info('CGF file(s) being used : ' + str(cgf_file))
+    isa_file = ctx.obj.isa_file
+    platform_file = ctx.obj.platform_file
+
+    with open(isa_file, "r") as isafile:
+        ispecs = isafile.read()
+    isa_specs = utils.load_yaml(ctx.obj.isa_file)['hart0']
+
+    with open(platform_file, "r") as platfile:
+        pspecs = platfile.read()
+    report, for_html, test_stats, coverpoints = framework.run_coverage(base, isa_file, platform_file,
+            work_dir, cgf_file)
+    report_file = open(work_dir+'/suite_coverage.rpt','w')
+    utils.dump_yaml(report, report_file)
+    report_file.close()
+
+
+    report_objects = {}
+    report_objects['date'] = (datetime.now(
+        pytz.timezone('GMT'))).strftime("%Y-%m-%d %H:%M GMT")
+    report_objects['riscof_version'] = __version__
+    report_objects['reference'] = (base.__model__).replace("_", " ")
+
+    rvarch, _ = archtest.get_version(constants.suite)
+    report_objects['rvarch_version'] = rvarch['version'] if rvarch['version'] != "-" else \
+                                        rvarch['commit']
+
+    report_objects['isa'] = isa_specs['ISA']
+    report_objects['usv'] = isa_specs['User_Spec_Version']
+    report_objects['psv'] = isa_specs['Privilege_Spec_Version']
+    report_objects['isa_yaml'] = isa_file
+    report_objects['platform_yaml'] = platform_file
+    report_objects['isa_specs'] = ispecs
+    report_objects['platform_specs'] = pspecs
+    report_objects['results'] = for_html
+    report_objects['results1'] = test_stats
+    report_objects['coverpoints'] = coverpoints
+    with open(constants.coverage_template, "r") as report_template:
+        template = Template(report_template.read())
+
+    output = template.render(report_objects)
+
+    reportfile = os.path.join(work_dir, "coverage.html")
+    with open(reportfile, "w") as report:
+        report.write(output)
+
+    shutil.copyfile(constants.css,
+                    os.path.join(work_dir, "style.css"))
+
+    logger.info("Test report generated at "+reportfile+".")
+    if not no_browser:
+        try:
+            import webbrowser
+            logger.info("Openning test report in web-browser")
+            webbrowser.open(reportfile)
+        except:
+            raise SystemExit
+
+
+
+@cli.command('arch-test',help='Setup and maintenance for Architectural TestSuite.',
+    no_args_is_help=True)
+@click.option('--dir',type=click.Path(resolve_path=True),
+    help="The Path to the directory to initialise/containing the tests. [Default = ./riscv-arch-test]",
+    metavar = 'PATH',default="./riscv-arch-test")
+@click.option('--get-version',type=str,default='latest',
+    help=' Version of the repository to get. [Default = latest]',
+    cls=CustomOption,requires=[['--clone'],['--update']])
+@click.option('--clone',is_flag=True,
+    help="Clone and setup the architectural tests from the remote repository.",cls=CustomOption,
+    mutually_exclusive=["--update",'--show-version'])
+@click.option('--update',is_flag=True,
+    help="Update the architectural tests from the remote repository.",
+    cls=CustomOption,mutually_exclusive=["--clone",'--show-version'])
+@click.option('--show-version',is_flag=True,
+    help="Print version of the local architectural test suite.",
+    cls=CustomOption,mutually_exclusive=['--clone','--update'])
+def arch_test(dir,get_version,clone,update,show_version):
+    if(clone):
+        if os.path.exists(dir):
+            shutil.rmtree(dir)
+        archtest.clone(dir,get_version)
+    elif(update):
+        archtest.update(dir,get_version)
+    elif(show_version):
+        version, is_repo = archtest.get_version(dir)
+        if not is_repo:
+            logger.error("Not the riscv-arch-test repo.")
+        else:
+            logger.info("Clonned version {0} of the repository with commit hash {1} ".format(
+                    version['version'],version['commit']))
+    else:
+        logger.error("Please specify one of [update,clone,show-version] flags.")
+
+
+
+@cli.command('setup',help='Initiate Setup for riscof.')
+@click.option('--dutname',type=str,help='Name of DUT plugin. [Default=spike]',
+    default='spike',metavar= 'NAME')
+@click.option('--refname',type=str,help='Name of Reference plugin. [Default=sail_cSim]',
+    default='sail_cSim',metavar= 'NAME')
+@click.option('--work-dir',default="./riscof_work",metavar='PATH',
+        type=click.Path(resolve_path=True,writable=True),
+        help='Path to the work directory.')
+def setup(dutname,refname,work_dir):
+    logger.info("Setting up sample plugin requirements [Old files will be overwritten]")
+    try:
+        cwd = os.getcwd()
+        logger.info("Creating sample Plugin directory for [DUT]: " +dutname)
+        src = os.path.join(constants.root, "Templates/setup/model/")
+        dest = os.path.join(cwd, dutname)
+        distutils.dir_util.copy_tree(src, dest)
+
+        os.rename(cwd+'/'+dutname+'/model_isa.yaml',
+                cwd+'/'+dutname+'/'+dutname+'_isa.yaml')
+        os.rename(cwd+'/'+dutname+'/model_platform.yaml',
+                cwd+'/'+dutname+'/'+dutname+'_platform.yaml')
+        os.rename(cwd+'/'+dutname+'/riscof_model.py',
+                cwd+'/'+dutname+'/riscof_'+dutname+'.py')
+        with open(cwd+'/'+dutname+'/riscof_'+dutname+'.py', 'r') as file :
+          filedata = file.read()
+
+        # Replace the target string
+        filedata = filedata.replace('dutname', dutname)
+
+        # Write the file out again
+        with open(cwd+'/'+dutname+'/riscof_'+dutname+'.py', 'w') as file:
+          file.write(filedata)
+
+        logger.info("Creating sample Plugin directory for [REF]: " +\
+              refname)
+        if refname == 'sail_cSim':
+            src = os.path.join(constants.root, "Templates/setup/sail_cSim/")
+            dest = os.path.join(cwd, refname)
+            distutils.dir_util.copy_tree(src, dest)
+        else:
+            src = os.path.join(constants.root, "Templates/setup/reference/")
+            dest = os.path.join(cwd, refname)
+            distutils.dir_util.copy_tree(src, dest)
+            os.rename(cwd+'/'+refname+'/riscof_model.py',
+                cwd+'/'+refname+'/riscof_'+refname+'.py')
+            with open(cwd+'/'+refname+'/riscof_'+refname+'.py', 'r') as file :
+              filedata = file.read()
+
+            # Replace the target string
+            filedata = filedata.replace('refname', refname)
+
+            # Write the file out again
+            with open(cwd+'/'+refname+'/riscof_'+refname+'.py', 'w') as file:
+              file.write(filedata)
+
+        logger.info("Creating Sample Config File")
+        configfile = open('config.ini','w')
+        configfile.write(constants.config_temp.format(refname, \
+                cwd+'/'+refname, dutname,cwd+'/'+dutname))
+        logger.info('**NOTE**: Please update the paths of the reference \
+and plugins in the config.ini file')
+        configfile.close()
+    except FileExistsError as err:
+        logger.error(err)
+        raise SystemExit
+
+if __name__=="__main__":
+    cli()
diff --git a/riscof/constants.py b/riscof/constants.py
index a38d51e..0bbffaa 100644
--- a/riscof/constants.py
+++ b/riscof/constants.py
@@ -34,6 +34,7 @@
 pluginpath={3}
 ispec={3}/{2}_isa.yaml
 pspec={3}/{2}_platform.yaml
+target_run=1
 
 [{0}]
 pluginpath={1}
diff --git a/riscof/log.py b/riscof/log.py
index 34ba6a0..f4d2312 100644
--- a/riscof/log.py
+++ b/riscof/log.py
@@ -3,14 +3,6 @@
 import logging
 import colorlog
 
-# a theme is just a dict of strings to represent each level
-THEME = {logging.CRITICAL: "    critical ",
-         logging.ERROR:    "       error ",
-         logging.WARNING:  "     command ",
-         logging.INFO:     "        info ",
-         logging.DEBUG:    "       debug "}
-
-
 class Log:
     """
     this class holds all the logic; see the end of the script to
@@ -26,21 +18,19 @@ class Log:
         logging.DEBUG:    ("debug", "dbg")
     }
 
-    def __init__(self, lvl=logging.CRITICAL, format=None):
-        self._lvl = lvl
+    def __init__(self, format=None):
         if not format:
-            format = "  %(log_color)s%(styledname)-8s%(reset)s | %(log_color)s%(message)s%(reset)s"
+            format = "%(log_color)s%(levelname)8s%(reset)s | %(log_color)s%(message)s%(reset)s"
         self.format = format
-        logging.root.setLevel(self._lvl)
-        self.formatter = colorlog.ColoredFormatter(self.format)
-        self.stream = logging.StreamHandler()
-        self.stream.setLevel(self._lvl)
-        self.stream.setFormatter(self.formatter)
-        self.logger = logging.getLogger('pythonConfig')
-        self.logger.setLevel(self._lvl)
-        self.logger.addHandler(self.stream)
-        self.theme = THEME
-        self.extra = {"styledname": self.theme[self._lvl]}
+        self.colors = {
+            'DEBUG': 'purple',
+            'INFO': 'green',
+            'WARNING': 'red',
+            'ERROR': 'bold_red',
+            'CRITICAL': 'bold_red',
+        }
+        self.logger = logging.getLogger()
+
 
     # the magic happens here: we use the "extra" argument documented in
     # https://docs.python.org/2/library/logging.html#logging.Logger.debug
@@ -49,35 +39,30 @@ def __init__(self, lvl=logging.CRITICAL, format=None):
     def critical(self, message, *args, **kwargs):
         for line in str(message).splitlines():
             self.logger.critical(line,
-                                 extra={"styledname": self.theme[logging.CRITICAL]},
                                  *args, **kwargs)
     crit = c = fatal = critical
 
     def error(self, message, *args, **kwargs):
         for line in str(message).splitlines():
             self.logger.error(line,
-                              extra={"styledname": self.theme[logging.ERROR]},
                               *args, **kwargs)
     err = e = error
 
     def warn(self, message, *args, **kwargs):
         for line in str(message).splitlines():
             self.logger.warning(line,
-                             extra={"styledname": self.theme[logging.WARNING]},
                              *args, **kwargs)
     warning = w = warn
 
     def info(self, message, *args, **kwargs):
         for line in str(message).splitlines():
             self.logger.info(line,
-                             extra={"styledname": self.theme[logging.INFO]},
                              *args, **kwargs)
     inf = nfo = i = info
 
     def debug(self, message, *args, **kwargs):
         for line in str(message).splitlines():
             self.logger.debug(line,
-                              extra={"styledname": self.theme[logging.DEBUG]},
                               *args, **kwargs)
     dbg = d = debug
 
@@ -86,15 +71,22 @@ def _parse_level(self, lvl):
         for log_level in self.aliases:
             if lvl == log_level or lvl in self.aliases[log_level]:
                 return log_level
-        self.logger.debug('Invalid log level passed. Please select from debug | info | warning | error')
+        print('Invalid log level passed. Please select from debug | info | warning | error')
         raise ValueError("{}-Invalid log level.".format(lvl))
 
-    def level(self, lvl=None):
-        '''Get or set the logging level.'''
-        if not lvl:
-            return self._lvl
+    def level(self, lvl=logging.CRITICAL):
+        '''Setup the Logger.'''
+
         self._lvl = self._parse_level(lvl)
+
+        self.stream = logging.StreamHandler()
+        self.stream.setLevel(self._lvl)
+
         self.stream.setLevel(self._lvl)
+
+        self.stream.setFormatter(colorlog.ColoredFormatter(self.format,log_colors=self.colors))
         self.logger.setLevel(self._lvl)
+
+        self.logger.addHandler(self.stream)
         logging.root.setLevel(self._lvl)
 logger = Log()
diff --git a/riscof/main.py b/riscof/main.py
deleted file mode 100644
index e4a4e23..0000000
--- a/riscof/main.py
+++ /dev/null
@@ -1,379 +0,0 @@
-# See LICENSE.incore for details
-"""Console script for riscof."""
-
-import logging
-import importlib
-from datetime import datetime
-import os
-import sys
-import pytz
-import shutil
-import configparser
-import distutils.dir_util
-
-from jinja2 import Template
-
-#from riscof.log import *
-from riscof.__init__ import __version__
-import riscv_config.checker as checker
-import riscof.framework.main as framework
-import riscof.framework.test as test_routines
-import riscof.arch_test as arch_test
-import riscof.dbgen as dbgen
-import riscof.utils as utils
-import riscof.constants as constants
-from riscv_config.errors import ValidationError
-import riscv_isac.coverage as isac
-import riscv_isac
-import riscv_config
-
-
-def execute():
-    '''
-        Entry point for riscof. This function sets up the models and
-        calls the :py:mod:`riscv_config` and :py:mod:`framework` modules with
-        appropriate arguments.
-    '''
-    # Set up the parser
-    parser = utils.riscof_cmdline_args()
-    args = parser.parse_args()
-    if len(sys.argv)<2:
-        parser.print_help()
-        raise SystemExit
-
-    # Set up the logger
-    utils.setup_logging(args.verbose)
-    logger = logging.getLogger()
-    logger.handlers = []
-    ch = logging.StreamHandler()
-    ch.setFormatter(utils.ColoredFormatter())
-    logger.addHandler(ch)
-
-
-    logger.info('RISCOF: RISC-V Architectural Test Framework')
-    logger.info('Version: '+ __version__)
-    logger.info('using riscv_isac version : ' + str(riscv_isac.__version__))
-    logger.info('using riscv_config version : ' + str(riscv_config.__version__))
-
-    if (args.version):
-        return 0
-    elif (args.command=='setup'):
-        logger.info("Setting up sample plugin requirements [Old files will \
-be overwritten]")
-        try:
-            cwd = os.getcwd()
-            logger.info("Creating sample Plugin directory for [DUT]: " +\
-                    args.dutname)
-            dutname = args.dutname
-            src = os.path.join(constants.root, "Templates/setup/model/")
-            dest = os.path.join(cwd, dutname)
-            distutils.dir_util.copy_tree(src, dest)
-
-            os.rename(cwd+'/'+args.dutname+'/model_isa.yaml',
-                    cwd+'/'+args.dutname+'/'+args.dutname+'_isa.yaml')
-            os.rename(cwd+'/'+args.dutname+'/model_platform.yaml',
-                    cwd+'/'+args.dutname+'/'+args.dutname+'_platform.yaml')
-            os.rename(cwd+'/'+args.dutname+'/riscof_model.py',
-                    cwd+'/'+args.dutname+'/riscof_'+args.dutname+'.py')
-            with open(cwd+'/'+args.dutname+'/riscof_'+args.dutname+'.py', 'r') as file :
-              filedata = file.read()
-
-            # Replace the target string
-            filedata = filedata.replace('dutname', args.dutname)
-
-            # Write the file out again
-            with open(cwd+'/'+args.dutname+'/riscof_'+args.dutname+'.py', 'w') as file:
-              file.write(filedata)
-
-            logger.info("Creating sample Plugin directory for [REF]: " +\
-                  args.refname)
-            if args.refname == 'sail_cSim':
-                src = os.path.join(constants.root, "Templates/setup/sail_cSim/")
-                dest = os.path.join(cwd, args.refname)
-                distutils.dir_util.copy_tree(src, dest)
-            else:
-                src = os.path.join(constants.root, "Templates/setup/reference/")
-                dest = os.path.join(cwd, args.refname)
-                distutils.dir_util.copy_tree(src, dest)
-                os.rename(cwd+'/'+args.refname+'/riscof_model.py',
-                    cwd+'/'+args.refname+'/riscof_'+args.refname+'.py')
-                with open(cwd+'/'+args.refname+'/riscof_'+args.refname+'.py', 'r') as file :
-                  filedata = file.read()
-
-                # Replace the target string
-                filedata = filedata.replace('refname', args.refname)
-
-                # Write the file out again
-                with open(cwd+'/'+args.refname+'/riscof_'+args.refname+'.py', 'w') as file:
-                  file.write(filedata)
-
-            logger.info("Creating Sample Config File")
-            configfile = open('config.ini','w')
-            configfile.write(constants.config_temp.format(args.refname, \
-                    cwd+'/'+args.refname, args.dutname,cwd+'/'+args.dutname))
-            logger.info('**NOTE**: Please update the paths of the reference \
-and DUT plugins in the config.ini file')
-            configfile.close()
-            return 0
-        except FileExistsError as err:
-            logger.error(err)
-            return 1
-    elif (args.command == 'arch-tests'):
-        if(args.clone):
-            if os.path.exists(args.dir):
-                shutil.rmtree(args.dir)
-            arch_test.clone(args.dir,args.get_version)
-        elif(args.update):
-            arch_test.update(args.dir,args.get_version)
-        elif(args.show_version):
-            version, is_repo = arch_test.get_version(args.dir)
-            if not is_repo:
-                logger.error("Not the riscv-arch-test repo.")
-            else:
-                logger.info("Clonned version {0} of the repository with commit hash {1} ".format(
-                        version['version'],version['commit']))
-        else:
-            logger.error("Please specify one of [update,clone,show-version] flags.")
-    else:
-        work_dir = args.work_dir
-        #Creating work directory
-        if not os.path.exists(work_dir):
-            logger.debug('Creating new work directory: ' + work_dir)
-            os.mkdir(work_dir)
-        elif args.command=='run':
-            if args.dbfile is None and args.testfile is None:
-                logger.debug('Removing old work directory: ' + work_dir)
-                shutil.rmtree(work_dir)
-                logger.debug('Creating new work directory: ' + work_dir)
-                os.mkdir(work_dir)
-        else:
-            logger.debug('Removing old work directory: ' + work_dir)
-            shutil.rmtree(work_dir)
-            logger.debug('Creating new work directory: ' + work_dir)
-            os.mkdir(work_dir)
-
-    if (args.command=='run' or args.command=='testlist' or \
-            args.command=='validateyaml' or args.command == 'coverage'):
-        config = configparser.ConfigParser()
-        logger.info("Reading configuration from: "+args.config)
-        try:
-            config.read(args.config)
-        except FileNotFoundError as err:
-            logger.error(err)
-            return 1
-        riscof_config = config['RISCOF']
-        logger.info("Preparing Models")
-
-        config_dir = os.path.dirname(os.path.abspath(args.config))
-
-        # Gathering Models
-        dut_model = riscof_config['DUTPlugin']
-        dut_model_path = utils.absolute_path(config_dir, riscof_config['DUTPluginPath'])
-
-        base_model = riscof_config['ReferencePlugin']
-        base_model_path = utils.absolute_path(config_dir, riscof_config['ReferencePluginPath'])
-
-        logger.debug("Importing " + dut_model + " plugin from: "+str(dut_model_path))
-        sys.path.append(dut_model_path)
-        try:
-            dut_plugin = importlib.import_module("riscof_" + dut_model)
-        except ImportError as msg:
-            logger.error("Error while importing "+dut_model+".\n"+str(msg))
-            raise SystemExit
-        dut_class = getattr(dut_plugin, dut_model)
-        if dut_model in config:
-            dut = dut_class(name="DUT", config=config[dut_model], config_dir=config_dir)
-        else:
-            dut = dut_class(name="DUT")
-
-        logger.debug("Importing " + base_model + " plugin from: "+str(base_model_path))
-        sys.path.append(base_model_path)
-        try:
-            base_plugin = importlib.import_module("riscof_" + base_model)
-        except ImportError as msg:
-            logger.error("Error while importing "+base_model+".\n"+str(msg))
-            raise SystemExit
-        base_class = getattr(base_plugin, base_model)
-        if base_model in config:
-            base = base_class(name="Reference", config=config[base_model], config_dir=config_dir)
-        else:
-            base = base_class(name="Reference")
-
-        #Run riscv_config on inputs
-        isa_file = dut.isa_spec
-        platform_file = dut.platform_spec
-        
-        if args.command=='run' and (args.testfile is not None or args.dbfile is not None):
-            isa_file = work_dir+ '/' + (isa_file.rsplit('/', 1)[1]).rsplit('.')[0] + "_checked.yaml"
-            platform_file = work_dir+ '/' + (platform_file.rsplit('/', 1)[1]).rsplit('.')[0] + "_checked.yaml"
-        else:
-            try:
-                isa_file = checker.check_isa_specs( isa_file, work_dir, True)
-                platform_file = checker.check_platform_specs( platform_file, work_dir, True)
-            except ValidationError as msg:
-                logger.error(msg)
-                return 1
-
-        isa_specs = utils.load_yaml(isa_file)['hart0']
-        platform_specs = utils.load_yaml(platform_file)
-
-    if args.command=='gendb' or args.command=='testlist' or \
-            args.command=='coverage' :
-        logger.info("Generating database for suite: "+args.suite)
-        work_dir = args.work_dir
-        constants.suite = args.suite
-        constants.framework_db = os.path.join(work_dir,"database.yaml")
-        logger.debug('Suite used: '+constants.suite)
-        logger.debug('ENV used: '+ args.env)
-        dbgen.generate()
-        logger.info('Database File Generated: '+constants.framework_db)
-        constants.env = args.env
-        logger.info('Env path set to'+constants.env)
-    elif args.command=='run' :
-        if args.dbfile is None and args.testfile is None:
-            logger.info("Generating database for suite: "+args.suite)
-            work_dir = args.work_dir
-            constants.suite = args.suite
-            constants.framework_db = os.path.join(work_dir,"database.yaml")
-            logger.debug('Suite used: '+constants.suite)
-            logger.debug('ENV used: '+ args.env)
-            dbgen.generate()
-            logger.info('Database File Generated: '+constants.framework_db)
-            constants.env = args.env
-            logger.info('Env path set to'+constants.env)
-        elif args.dbfile is None:
-            constants.suite = args.suite
-            constants.framework_db = os.path.join(work_dir,"database.yaml")
-            constants.env = args.env
-        else:
-            constants.suite = args.suite
-            constants.env = args.env
-
-    if args.command == 'testlist':
-        test_routines.generate_test_pool(isa_specs, platform_specs, work_dir)
-
-    if args.command == 'coverage':
-        logger.info('Will collect Coverage using RISCV-ISAC')
-        cgf_file = args.cgf
-        logger.info('CGF file(s) being used : ' + str(cgf_file))
-
-        with open(isa_file, "r") as isafile:
-            ispecs = isafile.read()
-
-        with open(platform_file, "r") as platfile:
-            pspecs = platfile.read()
-        report, for_html, test_stats, coverpoints = framework.run_coverage(base, isa_file, platform_file,
-                work_dir, cgf_file)
-        report_file = open(args.work_dir+'/suite_coverage.rpt','w')
-        utils.dump_yaml(report, report_file)
-        report_file.close()
-
-
-        report_objects = {}
-        report_objects['date'] = (datetime.now(
-            pytz.timezone('GMT'))).strftime("%Y-%m-%d %H:%M GMT")
-        report_objects['riscof_version'] = __version__
-        report_objects['reference'] = (base.__model__).replace("_", " ")
-
-        rvarch, _ = arch_test.get_version(constants.suite)
-        report_objects['rvarch_version'] = rvarch['version'] if rvarch['version'] != "-" else \
-                                            rvarch['commit']
-
-        report_objects['isa'] = isa_specs['ISA']
-        report_objects['usv'] = isa_specs['User_Spec_Version']
-        report_objects['psv'] = isa_specs['Privilege_Spec_Version']
-        report_objects['isa_yaml'] = isa_file
-        report_objects['platform_yaml'] = platform_file
-        report_objects['isa_specs'] = ispecs
-        report_objects['platform_specs'] = pspecs
-        report_objects['results'] = for_html
-        report_objects['results1'] = test_stats
-        report_objects['coverpoints'] = coverpoints
-        #report_objects['results'] = framework.run(dut, base, isa_file,
-        #                                          platform_file)
-        with open(constants.coverage_template, "r") as report_template:
-            template = Template(report_template.read())
-
-        output = template.render(report_objects)
-
-        reportfile = os.path.join(args.work_dir, "coverage.html")
-        with open(reportfile, "w") as report:
-            report.write(output)
-
-        shutil.copyfile(constants.css,
-                        os.path.join(args.work_dir, "style.css"))
-
-        logger.info("Test report generated at "+reportfile+".")
-        if not args.no_browser:
-            try:
-                import webbrowser
-                logger.info("Openning test report in web-browser")
-                webbrowser.open(reportfile)
-            except:
-                return 1
-        return 0
-
-    if args.command=='run':
-        with open(isa_file, "r") as isafile:
-            ispecs = isafile.read()
-
-        with open(platform_file, "r") as platfile:
-            pspecs = platfile.read()
-        
-        cntr_args = [args.dbfile,args.testfile,args.no_ref_run,args.no_dut_run]
-        
-        report_objects = {}
-        report_objects['date'] = (datetime.now(
-            pytz.timezone('GMT'))).strftime("%Y-%m-%d %H:%M GMT")
-        report_objects['riscof_version'] = __version__
-        report_objects['dut'] = (dut.__model__).replace("_", " ")
-        report_objects['reference'] = (base.__model__).replace("_", " ")
-
-        rvarch, _ = arch_test.get_version(constants.suite)
-        report_objects['rvarch_version'] = rvarch['version'] if rvarch['version'] != "-" else \
-                                            rvarch['commit']
-
-        report_objects['isa'] = isa_specs['ISA']
-        report_objects['usv'] = isa_specs['User_Spec_Version']
-        report_objects['psv'] = isa_specs['Privilege_Spec_Version']
-        report_objects['isa_yaml'] = isa_file
-        report_objects['platform_yaml'] = platform_file
-        report_objects['isa_specs'] = ispecs
-        report_objects['platform_specs'] = pspecs
-
-        report_objects['results'] = framework.run(dut, base, isa_file,
-                                                  platform_file, work_dir, cntr_args)
-
-        report_objects['num_passed'] = 0
-        report_objects['num_failed'] = 0
-
-        for entry in report_objects['results']:
-            if entry['res'] == "Passed":
-                report_objects['num_passed'] += 1
-            else:
-                report_objects['num_failed'] += 1
-
-        with open(constants.html_template, "r") as report_template:
-            template = Template(report_template.read())
-
-        output = template.render(report_objects)
-
-        reportfile = os.path.join(args.work_dir, "report.html")
-        with open(reportfile, "w") as report:
-            report.write(output)
-
-        shutil.copyfile(constants.css,
-                        os.path.join(args.work_dir, "style.css"))
-
-        logger.info("Test report generated at "+reportfile+".")
-        if not args.no_browser:
-            try:
-                import webbrowser
-                logger.info("Openning test report in web-browser")
-                webbrowser.open(reportfile)
-            except:
-                return 1
-
-
-if __name__ == "__main__":
-    exit(execute())
diff --git a/riscof/requirements.txt b/riscof/requirements.txt
index dd5d331..6ae675f 100644
--- a/riscof/requirements.txt
+++ b/riscof/requirements.txt
@@ -1,5 +1,6 @@
-GitPython>=2.1.11
+GitPython==3.1.17
+click
 Jinja2>=2.10.1
 pytz>=2019.1
-riscv-config>=2.4.1
-riscv_isac>=0.3.1
+riscv-config>=2.10.1
+riscv_isac>=0.7.2
diff --git a/riscof/utils.py b/riscof/utils.py
index a70efdc..e8b60a2 100644
--- a/riscof/utils.py
+++ b/riscof/utils.py
@@ -91,7 +91,7 @@ def add_target(self,command,tname=""):
         with open(self.makefilePath,"a") as makefile:
             makefile.write("\n\n.PHONY : " + tname + "\n" + tname + " :\n\t"+command.replace("\n","\n\t"))
             self.targets.append(tname)
-    def execute_target(self,tname,cwd="./"):
+    def execute_target(self,tname,cwd="./",timeout=300):
         """
         Function to execute a particular target only.
 
@@ -107,8 +107,9 @@ def execute_target(self,tname,cwd="./"):
 
         """
         assert tname in self.targets, "Target does not exist."
-        return shellCommand(self.makeCommand+" -f "+self.makefilePath+" "+tname).run(cwd=cwd)
-    def execute_all(self,cwd):
+        return shellCommand(self.makeCommand+" -f "+self.makefilePath+" "+tname).run(cwd=cwd,
+                timeout=timeout)
+    def execute_all(self,cwd="./",timeout=300):
         """
         Function to execute all the defined targets.
 
@@ -117,7 +118,8 @@ def execute_all(self,cwd):
         :type cwd: str
 
         """
-        return shellCommand(self.makeCommand+" -f "+self.makefilePath+" "+" ".join(self.targets)).run(cwd=cwd)
+        return shellCommand(self.makeCommand+" -f "+self.makefilePath+" "+" ".join(self.targets)).run(
+                cwd=cwd,timeout=timeout)
 
 
 class Command():
@@ -197,10 +199,18 @@ def run(self, **kwargs):
                 non-zero value.
         """
         kwargs.setdefault('shell', self._is_shell_command())
+        kwargs.setdefault('timeout', 300)
         cwd = self._path2str(kwargs.get(
             'cwd')) if not kwargs.get('cwd') is None else self._path2str(
                 os.getcwd())
         kwargs.update({'cwd': cwd})
+        process_args = dict(kwargs)
+        timeout = kwargs['timeout']
+        del process_args['timeout']
+        in_val = None
+        if 'input' in kwargs:
+            in_val = kwargs['input']
+            del process_args['input']
         logger.debug(cwd)
         # When running as shell command, subprocess expects
         # The arguments to be string.
@@ -209,10 +219,19 @@ def run(self, **kwargs):
         x = subprocess.Popen(cmd,
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE,
-                             **kwargs)
-        out, err = x.communicate()
-        out = out.rstrip()
-        err = err.rstrip()
+                             **process_args)
+        try:
+            out, err = x.communicate(input=in_val,timeout=timeout)
+            out = out.rstrip()
+            err = err.rstrip()
+        except subprocess.TimeoutExpired as cmd:
+            x.kill()
+            out, err = x.communicate()
+            out = out.rstrip()
+            err = err.rstrip()
+            logger.error("Process Killed.")
+            logger.error("Command did not exit within {0} seconds: {1}".format(timeout,cmd))
+
         if x.returncode != 0:
             if out:
                 logger.error(out.decode("ascii"))
@@ -308,338 +327,3 @@ def __init__(self, *args, pathstyle='auto', ensure_absolute_paths=False):
 
     def _is_shell_command(self):
         return True
-
-
-class ColoredFormatter(logging.Formatter):
-    """
-        Class to create a log output which is colored based on level.
-    """
-
-    def __init__(self, *args, **kwargs):
-        super(ColoredFormatter, self).__init__(*args, **kwargs)
-        self.colors = {
-            'DEBUG': '\033[94m',
-            'INFO': '\033[92m',
-            'WARNING': '\033[93m',
-            'ERROR': '\033[91m',
-        }
-
-        self.reset = '\033[0m'
-
-    def format(self, record):
-        msg = str(record.msg)
-        level_name = str(record.levelname)
-        name = str(record.name)
-        color_prefix = self.colors[level_name]
-        return '{0}{1:>9s} | [--{2}--]: {3}{4}'.format(color_prefix,
-                                            level_name, name, msg,
-                                            self.reset)
-
-
-def setup_logging(log_level):
-    """Setup logging
-
-        Verbosity decided on user input
-
-        :param log_level: User defined log level
-
-        :type log_level: str
-    """
-    numeric_level = getattr(logging, log_level.upper(), None)
-
-    if not isinstance(numeric_level, int):
-        print(
-            "\033[91mInvalid log level passed. Please select from debug | info | warning | error\033[0m"
-        )
-        raise ValueError("{}-Invalid log level.".format(log_level))
-
-    logging.basicConfig(level=numeric_level)
-
-
-class SortingHelpFormatter(argparse.HelpFormatter):
-    def add_arguments(self, actions):
-        actions = sorted(actions, key=operator.attrgetter('option_strings'))
-        super(SortingHelpFormatter, self).add_arguments(actions)
-
-
-class MyParser(argparse.ArgumentParser):
-    def error(self, message):
-        sys.stderr.write('error: %s\n' % message)
-        self.print_help()
-        raise SystemExit
-    def format_help(self):
-        formatter = self._get_formatter()
-
-        # usage
-        formatter.add_usage(self.usage, self._actions,
-                            self._mutually_exclusive_groups)
-
-        # description
-        formatter.add_text(self.description)
-
-        # positionals, optionals and user-defined groups
-        for action_group in self._action_groups:
-            formatter.start_section(action_group.title)
-            formatter.add_text(action_group.description)
-            formatter.add_arguments(action_group._group_actions)
-            formatter.end_section()
-
-        # epilog
-        formatter.add_text(self.epilog)
-
-        # determine help from format above
-        return formatter.format_help()
-    def print_help(self,file=None):
-        if file is None:
-            file = sys.stdout
-        self._print_message(self.format_help(), file)
-        subparsers_actions = [
-        action for action in self._actions
-        if isinstance(action, argparse._SubParsersAction)]
-        for subparsers_action in subparsers_actions:
-            for choice, subparser in subparsers_action.choices.items():
-               self._print_message("Action '{}'\n\n".format(choice),file)
-               self._print_message("\t"+(subparser.format_help()).replace("\n","\n\t")+"\n",file)
-class CustomAction(argparse.Action):
-    def __init__(self,
-                 option_strings,
-                 dest,
-                 nargs=None,
-                 const=None,
-                 default=None,
-                 type=None,
-                 choices=None,
-                 required=False,
-                 help=None,
-                 metavar=None):
-        argparse.Action.__init__(self,
-                                 option_strings=option_strings,
-                                 dest=dest,
-                                 nargs=nargs,
-                                 const=const,
-                                 default=default,
-                                 type=type,
-                                 choices=choices,
-                                 required=required,
-                                 help=help,
-                                 metavar=metavar,
-                                 )
-        return
-
-    def __call__(self, parser, namespace, values, option_string=None):
-
-        if isinstance(values, list):
-            values = [ str(pathlib.Path(v).absolute()) for v in values ]
-        else:
-            values = [str(pathlib.Path(values).absolute())]
-        existing_val = getattr(namespace, self.dest, None)
-        if existing_val:
-            setattr(namespace, self.dest, existing_val + values)
-        else:
-            setattr(namespace, self.dest, values)
-
-def riscof_cmdline_args():
-    parser = MyParser(
-        formatter_class=SortingHelpFormatter,
-        prog="riscof",
-        description="RISCOF is a framework used to run the Architectural Tests on a DUT and check compatibility with the RISC-V ISA")
-    parser.add_argument('--version','-v',
-                        help='Print version of RISCOF being used',
-                        action='store_true')
-    parser.add_argument('--verbose',
-                        action='store',
-                        default='info',
-                        choices = ['debug','info','warning','error'],
-                        help='[Default=info]',
-                        metavar="")
-    subparsers = parser.add_subparsers(dest='command',title="Action",description="The action to be performed by riscof.",help="List of actions supported by riscof.")
-
-    arch_tests =  subparsers.add_parser('arch-tests',help='Setup and maintenance for Architectural \
-            Test Suite.',formatter_class=SortingHelpFormatter)
-    arch_tests.add_argument('--dir',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the directory to initialise/containing the tests.[Default \
-= ./riscv-arch-test]',
-                        default='./riscv-arch-test',
-                        metavar= 'PATH')
-    arch_tests.add_argument('--get-version',
-                        action='store',
-                        default='latest',
-                        help='Version of the repository to get. To be used with the update/clone \
-argument. [Default = latest]',
-                        metavar="")
-    arch_tests.add_argument('--clone',
-                        action='store_true',
-                        help='Clone and setup the architectural tests from the remote repository.')
-    arch_tests.add_argument('--update',
-                        action='store_true',
-                        help='Update the architectural tests to the latest release.')
-    arch_tests.add_argument('--show-version',
-                        action='store_true',
-                        help='Print the version of the architectural tests being used.')
-
-    coverage = subparsers.add_parser('coverage',help='Generate Coverage Report for the given YAML spec.',formatter_class=SortingHelpFormatter)
-    coverage.add_argument('--config',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the config file. [Default=./config.ini]',
-                        metavar= 'PATH',
-                        default=str(pathlib.Path('./config.ini').absolute())
-                          )
-    coverage.add_argument('--cgf',
-                        action=CustomAction,
-                        required=True,
-                        help='The Path to the cgf file(s). Multiple allowed',
-                        metavar= 'PATH')
-    coverage.add_argument('--suite',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        required=True,
-                        action='store',
-                        help='The Path to the custom suite directory.',
-                        metavar= 'PATH')
-    coverage.add_argument('--env',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        required=True,
-                        action='store',
-                        help='The Path to the custom env directory.',
-                        metavar= 'PATH')
-    coverage.add_argument('--work-dir',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the work-dir.',
-                        metavar= 'PATH',
-                        default=str(pathlib.Path('./riscof_work').absolute())
-                        )
-    coverage.add_argument('--no-browser',action='store_true',
-                     help="Do not open the browser for showing the test report.")
-
-    generatedb = subparsers.add_parser('gendb',help='Generate Database for the standard suite.',formatter_class=SortingHelpFormatter)
-    generatedb.add_argument('--suite',
-                            type= lambda p: str(pathlib.Path(p).absolute()),
-                            required=True,
-                            action='store',
-                            help='The Path to the custom suite directory.',
-                            metavar= 'PATH')
-    generatedb.add_argument('--env',
-                            type= lambda p: str(pathlib.Path(p).absolute()),
-                            required=True,
-                            action='store',
-                            help='The Path to the custom env directory.',
-                            metavar= 'PATH')
-    generatedb.add_argument('--work-dir',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the work-dir.',
-                        metavar= 'PATH',
-                        default=str(pathlib.Path('./riscof_work').absolute())
-                        )
-    setup = subparsers.add_parser('setup',help='Initiate setup for riscof.',formatter_class=SortingHelpFormatter)
-    setup.add_argument('--dutname',
-                        action='store',
-                        help='Name of DUT plugin. [Default=spike]',
-                        default='spike',
-                        metavar= 'NAME')
-    setup.add_argument('--refname',
-                        action='store',
-                        help='Name of Reference plugin. [Default=sail_cSim]',
-                        default='sail_cSim',
-                        metavar= 'NAME')
-    setup.add_argument('--work-dir',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the work-dir.',
-                        metavar= 'PATH',
-                        default=str(pathlib.Path('./riscof_work').absolute())
-                        )
-    validate = subparsers.add_parser('validateyaml',
-                        help='Validate the Input YAMLs using riscv-config.',formatter_class=SortingHelpFormatter)
-    validate.add_argument('--config',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the config file. [Default=./config.ini]',
-                        metavar= 'PATH',
-                        default=str(pathlib.Path('./config.ini').absolute())
-                          )
-    validate.add_argument('--work-dir',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the work-dir.',
-                        metavar= 'PATH',
-                        default=str(pathlib.Path('./riscof_work').absolute())
-                        )
-    run = subparsers.add_parser('run',
-                        help='Run the tests on DUT and reference and compare signatures.',formatter_class=SortingHelpFormatter)
-    run.add_argument('--config',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the config file. [Default=./config.ini]',
-                        metavar= 'PATH',
-                        default=str(pathlib.Path('./config.ini').absolute())
-                          )
-    run.add_argument('--suite',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        required=True,
-                        action='store',
-                        help='The Path to the custom suite directory.',
-                        metavar= 'PATH')
-    run.add_argument('--env',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        required=True,
-                        action='store',
-                        help='The Path to the custom env directory.',
-                        metavar= 'PATH')
-    run.add_argument('--no-browser',action='store_true',
-                     help="Do not open the browser for showing the test report.")
-    run.add_argument('--work-dir',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the work-dir.',
-                        metavar= 'PATH',
-                        default=str(pathlib.Path('./riscof_work').absolute())
-                        )
-    run_group = run.add_mutually_exclusive_group()
-    run_group.add_argument('--dbfile',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the database file.',
-                        metavar= 'PATH')
-    run_group.add_argument('--testfile',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the testlist file.',
-                        metavar= 'PATH')
-    run.add_argument('--no-ref-run',action='store_true',
-                     help="Do not run tests on Reference")
-    run.add_argument('--no-dut-run',action='store_true',
-                     help="Do not run tests on DUT")
-    testlist = subparsers.add_parser('testlist',
-                        help='Generate the test list for the given DUT and suite.',formatter_class=SortingHelpFormatter)
-    testlist.add_argument('--work-dir',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the work-dir.',
-                        metavar= 'PATH',
-                        default=str(pathlib.Path('./riscof_work').absolute())
-                        )
-    testlist.add_argument('--config',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        action='store',
-                        help='The Path to the config file. [Default=./config.ini]',
-                        metavar= 'PATH',
-                        default=str(pathlib.Path('./config.ini').absolute())
-                        )
-
-    testlist.add_argument('--suite',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        required=True,
-                        action='store',
-                        help='The Path to the custom suite directory.',
-                        metavar= 'PATH')
-    testlist.add_argument('--env',
-                        type= lambda p: str(pathlib.Path(p).absolute()),
-                        required=True,
-                        action='store',
-                        help='The Path to the custom env directory.',
-                        metavar= 'PATH')
-    return parser
diff --git a/setup.cfg b/setup.cfg
index 67d8b18..0bbf192 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 1.21.1
+current_version = 1.21.2
 commit = True
 tag = True
 
diff --git a/setup.py b/setup.py
index 1d81553..b4d3c34 100644
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@ def read_requires():
 test_requirements = [ ]
 
 setup(name="riscof",
-      version='1.21.1',
+      version='1.21.2',
       description="RISC-V Architectural Test Framework",
       long_description=readme + '\n\n',
       classifiers=[
@@ -40,28 +40,12 @@ def read_requires():
       package_data={
           'riscof': [
               'requirements.txt',
-              'coverage/*.cgf',
-              'suite/env/*', 
-              'suite/rv32i_m/I/*.S', 
-              'suite/rv32i_m/M/*.S',
-              'suite/rv32i_m/C/*.S', 
-              'suite/rv32i_m/privilege/*.S',
-              'suite/rv32i_m/Zicsr/*.S', 
-              'suite/rv32i_m/Zifencei/*.S',
-              'suite/rv32i_m/K_unratified/*.S',
-              'suite/rv64i_m/I/*.S', 
-              'suite/rv64i_m/M/*.S',
-              'suite/rv64i_m/C/*.S', 
-              'suite/rv64i_m/privilege/*.S',
-              'suite/rv64i_m/Zicsr/*.S', 
-              'suite/rv64i_m/Zifencei/*.S',
-              'suite/rv64i_m/K_unratified/*.S',
               'Templates/coverage.html',
-              'framework/database.yaml', 
+              'framework/database.yaml',
               'Templates/report.html',
-              'Templates/style.css', 
+              'Templates/style.css',
               'Templates/setup/*',
-              'Templates/setup/model/env/*', 
+              'Templates/setup/model/env/*',
               'Templates/setup/model/*',
               'Templates/setup/sail_cSim/*',
               'Templates/setup/sail_cSim/env/*',
@@ -72,7 +56,7 @@ def read_requires():
       install_requires=read_requires(),
       python_requires=">=3.6.0",
       entry_points={
-          "console_scripts": ["riscof=riscof.main:execute"],
+          "console_scripts": ["riscof=riscof.cli:cli"],
       },
     include_package_data=True,
     keywords='riscof',


Test patch:
diff --git a/docs/source/arch-tests.rst b/docs/source/arch-tests.rst
index 8089ad5..92e3636 100644
--- a/docs/source/arch-tests.rst
+++ b/docs/source/arch-tests.rst
@@ -73,7 +73,7 @@ Running Tests with RISCOF
 
      .. code-block:: console
 
-       $ riscof --verbose info run --config ~/config.ini --suite ./riscv-arch-test/riscv-test-suite/rv32i_m --env ./riscv-arch-test/riscv-test-suite/env
+       $ riscof --verbose info run --config ./config.ini --suite ./riscv-arch-test/riscv-test-suite/rv32i_m --env ./riscv-arch-test/riscv-test-suite/env
 
      The above step will first create a database of the all tests from the ``rv32i_m`` directory 
      (recursively). This database can be found in the `riscof_work/database.yaml` file that is 
diff --git a/riscof/framework/test.py b/riscof/framework/test.py
index c4dde3b..7e7c745 100644
--- a/riscof/framework/test.py
+++ b/riscof/framework/test.py
@@ -8,6 +8,7 @@
 import pytz
 import random
 from datetime import datetime
+from copy import deepcopy
 
 from riscof.utils import yaml
 import riscof.utils as utils
@@ -301,7 +302,7 @@ def generate_test_pool(ispec, pspec, workdir, dbfile = None):
         test_list[entry[0]]=temp
     if (len(test_list) == 0):
         logger.error('No Tests Selected')
-        sys.exit(1)
+        raise SystemExit
 
     with open(os.path.join(workdir,"test_list.yaml"),"w") as tfile:
         tfile.write('# testlist generated on ' + (datetime.now(pytz.timezone('GMT'))).strftime("%Y-%m-%d %H:%M GMT")+'\n')
@@ -321,7 +322,7 @@ def run_tests(dut, base, ispec, pspec, work_dir, cntr_args):
         :param ispec: The isa specifications of the DUT.
 
         :param pspec: The platform specifications of the DUT.
-        
+
         :param cntr_args: dbfile, testfile, no_ref_run, no_dut_run
 
         :type ispec: dict
@@ -335,29 +336,39 @@ def run_tests(dut, base, ispec, pspec, work_dir, cntr_args):
         test_list = utils.load_yaml(cntr_args[1])
     else:
         test_list, test_pool = generate_test_pool(ispec, pspec, work_dir, cntr_args[0])
+    dut_test_list = {}
+    base_test_list = {}
+    for entry in test_list:
+        node = test_list[entry]
+        dut_test_list[entry] = deepcopy(node)
+        base_test_list[entry] = deepcopy(node)
+        dut_test_list[entry]['work_dir'] = os.path.join(node['work_dir'],'dut')
+        os.mkdir(dut_test_list[entry]['work_dir'])
+        base_test_list[entry]['work_dir'] = os.path.join(node['work_dir'],'ref')
+        os.mkdir(base_test_list[entry]['work_dir'])
     results = []
     if cntr_args[2]:
         logger.info("Running Tests on DUT.")
-        dut.runTests(test_list)
+        dut.runTests(dut_test_list)
         logger.info("Tests run on DUT done.")
         raise SystemExit
     elif cntr_args[3]:
         logger.info("Running Tests on Reference Model.")
-        base.runTests(test_list)
+        base.runTests(base_test_list)
         logger.info("Tests run on Reference done.")
         raise SystemExit
     else:
         logger.info("Running Tests on DUT.")
-        dut.runTests(test_list)
+        dut.runTests(dut_test_list)
         logger.info("Running Tests on Reference Model.")
-        base.runTests(test_list)
+        base.runTests(base_test_list)
 
     logger.info("Initiating signature checking.")
     for file in test_list:
         testentry = test_list[file]
         work_dir = testentry['work_dir']
-        res = os.path.join(testentry['work_dir'],dut.name[:-1]+".signature")
-        ref = os.path.join(testentry['work_dir'],base.name[:-1]+".signature")
+        res = os.path.join(dut_test_list[file]['work_dir'],dut.name[:-1]+".signature")
+        ref = os.path.join(base_test_list[file]['work_dir'],base.name[:-1]+".signature")
         result, diff = compare_signature(res, ref)
         res = {
             'name':