diff --git a/CHANGELOG.md b/CHANGELOG.md index 33ecc45..02babb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.2.0] - 2021-10-27 +- documentation. +- feature to use index.yaml for test selection. +- update instruction constants. +- add custom minimal trap handler for illegal insts and misaligned access. +- add feature to generate multiple tests from one plugin. +- update test_list generation for compile macros and march. +- Fix generate_asm return types for rvtest_data, name_postfix and signature. + ## [1.1.0] - 2021-09-24 - add support for updated chromite's config YAMLs - update documentation for the new changes diff --git a/docs/source/creating_new_tests.rst b/docs/source/creating_new_tests.rst index faf8276..06fdbf0 100644 --- a/docs/source/creating_new_tests.rst +++ b/docs/source/creating_new_tests.rst @@ -35,46 +35,92 @@ The directory tree of the ``chromite_uatg_tests`` is as follows. │   ├── uatg_gshare_fa_mispredict_loop_01.py │   └── uatg_gshare_fa_ras_push_pop_01.py ├── decoder - │   └── uatg_decoder_i_ext_r_type.py + │   ├── uatg_decoder_arithmetic_insts_1.py + │   ├── uatg_decoder_arithmetic_insts_2.py + │   ├── uatg_decoder_arithmetic_insts_3.py + │   ├── uatg_decoder_arithmetic_insts_4.py + │   ├── uatg_decoder_arithmetic_insts_ui.py + │   ├── uatg_decoder_branch_insts_1.py + │   ├── uatg_decoder_jump_jal.py + │   ├── uatg_decoder_jump_jalr.py + │   ├── uatg_decoder_logical_insts_1.py + │   ├── uatg_decoder_logical_insts_2.py + │   ├── uatg_decoder_memory_insts_1.py + │   └── uatg_decoder_mext.py ├── decompressor - │   └── uatg_decompressor.py - └── index.yaml + │   ├── uatg_decompressor_01.py + │   ├── uatg_decompressor_02.py + │   └── uatg_decompressor_floating_01.py + ├── index.yaml + └── mbox + ├── uatg_mbox_divu_insts_01.py + ├── uatg_mbox_mul_div_insts_01.py + └── uatg_mbox_mulh_insts_01.py Irrespective of the name, every directory purposed to host tests for UATG should have a similiar structure. Other than that, it is necessary that the module specific directories. like -*branch_predictor*, *decoder* and *decompressor* should be named same as the -verilog module name in order to improve comprehension. +*branch_predictor*, *decoder*, *mbox* and *decompressor* should be named same +as the verilog module name in order to improve comprehension. -The ``index.yaml`` should contain the names of all modules for which test classes -exist in the directory, and for which the tests are to be generated. +The ``index.yaml`` should contain the names of **all the tests** as well +as the modules for which test classes exist in the directory. When invoked, UATG reads the *index.yaml* file first and checks for test classes -only in the directories which were specified in the yaml file. Other -folders will not be used to pick up test classes. - -The structure of the *index.yaml* file is presented as follows, - -.. code-block:: yaml - - branch_predictor: - uatg_gshare_fa_btb_fill_01: "fill the BTB with entries" - uatg_gshare_fa_btb_selfmodifying_01: "ASM that modifies itself, also used to verify functioning of fence instruction" - uatg_gshare_fa_fence_01: "Verify the functioning of fence instruction" - uatg_gshare_fa_ghr_alternating_01: "fill the GHR Register with alternating 1-0 pattern" - uatg_gshare_fa_ghr_ones_01: "fill the GHR Register with ones" - uatg_gshare_fa_ghr_zeros_01: "fill the GHR Register with zeros" - uatg_gshare_fa_mispredict_loop_01: "" - uatg_gshare_fa_ras_push_pop_01: "Pushing and Popping the return address stack using call-ret instructions" - decoder: - uatg_decoder_arithmetic_insts: "tests arithmetic instructions" +**only** in the directories which were specified in the yaml file. In addition +to that, the test_generator will generate tests for which the value is set to +``True``. Other tests for which the value is ``False`` will be skipped +with a warning. The modules not specified in the yaml will be +directly skipped. - decompressor: - uatg_decompressor: "checks if mis-predictions occur and tests macro's" +.. warning:: The ``index.yaml`` file should contain the module name and the + test name as keys. The value of the ``test_name`` keys will govern if the + test is generated or not. It is mandatory for the user to update the file + when every new test is added. +The structure of the *index.yaml* file is presented as follows, -The above file contains information required for UATG to pick-up the tests from -your directory. +.. code-block:: yaml + :linenos: + + branch_predictor: + uatg_gshare_fa_btb_fill_01: True + uatg_gshare_fa_btb_selfmodifying_01: True + uatg_gshare_fa_fence_01: True + uatg_gshare_fa_ghr_alternating_01: True + uatg_gshare_fa_ghr_ones_01: True + uatg_gshare_fa_ghr_zeros_01: True + uatg_gshare_fa_mispredict_loop_01: True + uatg_gshare_fa_ras_push_pop_01: True + + decoder: + uatg_decoder_arithmetic_insts_1: True + uatg_decoder_arithmetic_insts_2: True + uatg_decoder_arithmetic_insts_3: True + uatg_decoder_arithmetic_insts_4: True + uatg_decoder_arithmetic_insts_ui: True + uatg_decoder_branch_insts_1: True + uatg_decoder_jump_jal: True + uatg_decoder_jump_jalr: True + uatg_decoder_logical_insts_1: True + uatg_decoder_logical_insts_2: True + uatg_decoder_memory_insts_1: True + uatg_decoder_mext: True + + decompressor: + uatg_decompressor_01: False + uatg_decompressor_02: True + uatg_decompressor_floating_01: True + + mbox: + uatg_mbox_divu_insts_01: False + uatg_mbox_mul_div_insts_01: True + uatg_mbox_mulh_insts_01: False + +The above file contains information required for UATG to selectively +pick-up the tests from your directory. This feature allows you to select tests +even within the module level. The tests for which the value is false will not be +generated. This index file is written based on the modules present in the chromite core. The tests for the branch_predictor unit are present in the branch_predictor @@ -122,7 +168,8 @@ For now, we are creating a test which would overflow the stack. $ vi uatg_stack_overflow.py Once you have created the test_class, return to your ``~/tests/`` directory and -create a, ``index.yaml`` file. +create a, ``index.yaml`` file. Here, we suggest ``vi`` as the text editor. The +user can choose his preferred text editor. .. code-block:: console @@ -139,17 +186,16 @@ The content to typed within the yaml file for UATG to recognize the test is this .. code-block:: yaml stack: - uatg_stack_overflow: "Overflows the stack" + uatg_stack_overflow: True Here, the first key ``stack`` indicates that the module is a ``stack``, for which the tests have been generated. The next key ``uatg_stack_overflow`` -is the name of the actual test_class. +is the name of the actual test_class. Since, the value is **True** this test +will be generated. Had it been false, UATG would have skipped this test. .. warning:: if the module name or test_class are inconsistent between the index.yaml and actual test files, UATG will not pickup the tests. -The string value is just a comment which serves the purpose of documentation. - Your directory structure at the end of this activity should be this .. code-block:: console @@ -229,6 +275,11 @@ Optional Packages: Currently the file has few patterns for checking Branch Predictor unit. The user can add new expressions to the same file. + .. note:: The user can import as many other packages and methods he deems + necessary for his tests. In fact, uatg contains few methods like + ``bit_walker``, ``rvtest_data``, and modules like + ``instruction_constants`` which the user may need to import. + .. code-block:: python from yapsy.IPlugin import IPlugin # class necessary from plugin management @@ -311,21 +362,27 @@ methods of their class. In such case the user may declare a *xyz* as self.xyz = 5 # initialize the variables which are needed throughout the class as self. self.parameter_name2 = None # The self variable, like any variable, can be of any type. -execute(self, config_dict): ---------------------------- -The execute method of the test class requires a dictionary (possibly extracted -from a yaml file) as an input. The user can parse and select from this +execute(self, core_yaml, isa_yaml): +----------------------------------- +The execute method of the test class requires two dictionary (possibly extracted +from yaml files) as an input. The user can parse and select from this dictionary the parameters which would make their current test valid to be run on the DUT. +The isa_yaml is the ``ISA_configuration`` yaml file (as a dict) of the DUT, +and the core_yaml is the ``Core_configuration`` yaml file (as a dictionary). + .. code-block:: python - def execute(self, config_dict): + def execute(self, core_yaml, isa_yaml): """ Docstring explaining the rationale behind why the test was created or not based on the chosen parameters""" # _block_parameters( in this case config_dict) are the details of the configuration of a particular block given as a dictionary - self._history_len = config_dict['history_len'] #self variable as _history_len will be used in other methods within the class. + self._history_len = core_yaml['branch_predictor']['history_len'] #self variable as _history_len will be used in other methods within the class. # obtain the needed external parameters from the input dictionary - _bpu_enabled = config_dict['instantiate'] + _bpu_enabled = core_yaml['branch_predictor']['instantiate'] + + # obtain the ISA supported by the DUT as a variable + isa = isa_yaml['hart0']['ISA'] # IMPORTANT: check for conditions in which the test needs to be generated if _history_len >= 1 and _bpu_enabled: # Since BPU is an optional feature, we check for it to be enabled. @@ -353,20 +410,56 @@ sure he creates a ``self.history_len = config_dict[history_len]`` within this method if he thinks he'd need the ``history_len`` somewhere in the following methods. -.. warning:: **Only** the ``execute()`` method can take in the config_dict among - all the methods of the test class. There are no other methods in the class - which receives this dictionary as an input. +.. warning:: **Only** the ``execute()`` method can take in the core_yaml + and isa_yaml as arguments among all the methods of the test class. There are + no other methods in the class which receives these dictionaries as an input. generate_asm(self): ------------------- -This function should be written in a way that it returns a well formatted -string, which complies with the RISC-V assembly format. - -The function does not take in any arguments. -The string returned by this function will be directly written into an assembly -file titled ``.S``. Here, the test_class_name is the name of -the class within which the generate_asm() method is present. +This function should be written in a way that it returns a well formatted +string, which complies with the RISC-V assembly format. We make use of the +``test_format`` and ``test_macros`` specified `here +`_ + +.. warning:: All tests written for UATG should comply with the test format. + It is encouraged that the user goes through the link given above to + understand the format. This would make test writing experience a bit more + streamlined. + +The ``generate_asm()`` function does not take in any arguments. + +The ``generate_asm()`` function will return a list of dictionaries. The list +will contain ``n`` dictionaries for the ``n`` instructions being tested by the +test_class. The elements of the dict are, + + 1. ``asm_code`` : A formatted string, which will be directly written as an + Assembly file. + 2. ``asm_data`` : A formatted string of the data and labels required to be + populated in the ``RVTEST_DATA`` section of the Assembly test. + If not required, the user can return an empty string ``''``. + 3. ``asm_sig`` : A formatted string of the data and labels required to be + populated in the ``RVMODEL_DATA`` section of the Assembly test. If not + utilized, the user can specify an empty string ``''``. To learn more + about test signatures, the user can refer to the test_format + documentation previously shared. + 4. ``compile_macros`` : The compile macros key of the return dict will contain + a list of the all macros required to be passed along while compiling that + test. If there are no macros, the user can return an empty list. + 5. ``name_postfix`` : The name_postfix key requires a string specifiying the + name to be postfixed along with the test name. This is done in order to + split a large test containing multiple instructions into a single test + per instruction. The user can return an empty string. + +The list returned by this function will be parsed and written into an assembly +file titled ``-.S``. +Here, the test_class_name is the name of the class within which the +generate_asm() method is present. The ```` will indicate the number of +sub-tests that were generated from ````. The ``name_postfix`` +is added to the test_name if the user specifies it in his return list[dict{}]. + +In this first example we can see a test which only fills the ``asm_code`` key, +while all other keys are assigned their default values. .. code-block:: python @@ -378,15 +471,128 @@ the class within which the generate_asm() method is present. hist_len = self._history_len # we reuse the self._history_len variable here. # Since, it is not possible to access the config_dict from this method, the necessary variables # are to be stored as self variables to access across the methods of the class. - asm = "" # assembly code to be generated as a formatted string. It is left empty, which is the default state. + asm_code = "" # assembly code to be generated as a formatted string. It is left empty, which is the default state. for var_i in range(0,hist_len): - asm = asm + " addi x0,x0,0\n" # inserting (hist_len)x NOPs + asm_code += " addi x0,x0,0\n" # inserting (hist_len)x NOPs + + # compile macros for the test + compile_macros = [] - return asm # generate_asm returns the assembly code as a string + # return asm_code and sig_code + test_dict.append({ + 'asm_code': asm_code, # formatted string to be dumped as ASM + 'asm_data': '', + 'asm_sig': '', + 'compile_macros': compile_macros, + 'name_postfix': '' + }) -The string returned from the above function contains a formatted string which +The string assigned to ``asm_code`` key contains a formatted string which can be directly dumped into an assembly file. The string will contain *hist_len* -amount of *NOPs*. +amount of *NOPs*. The other keys are assigned default values. + +This following example demonstrates a method with all the keys being assigned +different values. + +.. code-block:: python + + def generate_asm(self) -> Dict[str, str]: + """x + Generates the ASM instructions for R type arithmetic instructions. + It creates asm for the following instructions based upon ISA + mul[w], mulh, mulhsu, mulhu, div[w], divu[w], rem[w], remu[w] + """ + # rd, rs1, rs2 iterate through all the 32 register combinations for + # every instruction in arithmetic_instructions['rv32-add-reg'] + + test_dict = [] + + reg_file = base_reg_file.copy() + + instruction_list = [] + if 'M' in self.isa or 'Zmmul' in self.isa: + instruction_list += mext_instructions[f'{self.isa_bit}-mul'] + if 'M' in self.isa: + instruction_list += mext_instructions[f'{self.isa_bit}-div'] + + for inst in instruction_list: + asm_code = '#' * 5 + ' mul/div reg, reg, reg ' + '#' * 5 + '\n' + + # initial register to use as signature pointer + swreg = 'x31' + + # initialize swreg to point to signature_start label + asm_code += f'RVTEST_SIGBASE({swreg}, signature_start)\n' + + # initial offset to with respect to signature label + offset = 0 + + # variable to hold the total number of signature bytes to be used. + sig_bytes = 0 + + inst_count = 0 + + for rd in reg_file: + for rs1 in reg_file: + for rs2 in reg_file: + + rs1_val = hex(random.getrandbits(self.xlen)) + rs2_val = hex(random.getrandbits(self.xlen)) + + # if signature register needs to be used for operations + # then first choose a new signature pointer and move the + # value to it. + if swreg in [rd, rs1, rs2]: + newswreg = random.choice([ + x for x in reg_file + if x not in [rd, rs1, rs2, 'x0'] + ]) + asm_code += f'mv {newswreg}, {swreg}\n' + swreg = newswreg + + # perform the required assembly operation + asm_code += f'\ninst_{inst_count}:' + asm_code += f'\n#operation: {inst}, rs1={rs1}, rs2={rs2}, rd={rd}\n' + asm_code += f'TEST_RR_OP({inst}, {rd}, {rs1}, {rs2}, 0, {rs1_val}, {rs2_val}, {swreg}, {offset}, x0)\n' + + # adjust the offset. reset to 0 if it crosses 2048 and + # increment the current signature pointer with the + # current offset value + if offset + self.offset_inc >= 2048: + asm_code += f'addi {swreg}, {swreg}, {offset}\n' + offset = 0 + + # increment offset by the amount of bytes updated in + # signature by each test-macro. + offset = offset + self.offset_inc + + # keep track of the total number of signature bytes used + # so far. + sig_bytes = sig_bytes + self.offset_inc + + inst_count += 1 + + # asm code to populate the signature region + sig_code = 'signature_start:\n' + sig_code += ' .fill {0},4,0xdeadbeef\n'.format(int(sig_bytes / 4)) + + # compile macros for the test + compile_macros = [] + + # return asm_code and sig_code + test_dict.append({ + 'asm_code': asm_code, + 'asm_data': '', + 'asm_sig': sig_code, + 'compile_macros': compile_macros, + 'name_postfix': inst + }) + return test_dict + +In this example, all the keys are being populated. In addition to that, this +one test_class would return tests for all the instructions in the RV64-M +Extension, hence the name_potfix will come in to be beneficial to identify +the ASM files. .. note:: The above snippet is just an example demostrating how to use the generate_asm() method. @@ -547,12 +753,12 @@ chromite's configuration file. self.parameter_name1 = 5 # initialize the internal parameters needed for the script self.parameter_name2 = None - def execute(self, config_dict): + def execute(self, core_yaml, isa_yaml): """ Docstring explaining the rationale behind why the test was created or not based on the chosen parameters""" # _block_parameters( in this case config_dict) are the details of the configuration of a particular block given as a dictionary - self._history_len = config_dict['history_len'] #self variable as _history_len will be used in other methods within the class. + self._history_len = core_yaml['branch_predictor']['history_len'] #self variable as _history_len will be used in other methods within the class. # obtain the needed external parameters from the input dictionary - _bpu_enabled = config_dict['instantiate'] + _bpu_enabled = core_yaml['branch_predictor']['instantiate'] # IMPORTANT: check for conditions in which the test needs to be generated if _history_len >= 1 and _bpu_enabled: # Since BPU is an optional feature, we check for it to be enabled. @@ -561,19 +767,30 @@ chromite's configuration file. else: return False - def execute(self, config_dict): - """ Docstring explaining the rationale behind why the test was created or not based on the chosen parameters""" - # _block_parameters( in this case config_dict) are the details of the configuration of a particular block given as a dictionary - self._history_len = config_dict['history_len'] #self variable as _history_len will be used in other methods within the class. - # obtain the needed external parameters from the input dictionary - _bpu_enabled = config_dict['instantiate'] + def generate_asm(self): + + """ Docstring for the generate_asm method explaining the asm code's details""" + """ Registers used and their functions, instructions called and their purposes etc""" + + hist_len = self._history_len # we reuse the self._history_len variable here. + # Since, it is not possible to access the config_dict from this method, the necessary variables + # are to be stored as self variables to access across the methods of the class. + asm_code = "" # assembly code to be generated as a formatted string. It is left empty, which is the default state. + for var_i in range(0,hist_len): + asm_code += " addi x0,x0,0\n" # inserting (hist_len)x NOPs + + # compile macros for the test + compile_macros = [] + + # return asm_code and sig_code + test_dict.append({ + 'asm_code': asm_code, # formatted string to be dumped as ASM + 'asm_data': '', + 'asm_sig': '', + 'compile_macros': compile_macros, + 'name_postfix': '' + }) - # IMPORTANT: check for conditions in which the test needs to be generated - if _history_len >= 1 and _bpu_enabled: # Since BPU is an optional feature, we check for it to be enabled. - # Likewise with the history_register - return True - else: - return False # generate_asm returns the assembly code as a string def generate_covergroups(self, alias_file): diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 480414f..cc099fb 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -206,7 +206,7 @@ Details and further specification of the config file syntax is available at the user when he tries to write his own YAMLs. These files, eventhough working, are likely to be very old and can be factually wrong. So, the user is required to use these files only to get a feel of how UATG - works. The user should move to the actual YAMLs once they understand the + works. The user must start using the actual YAMLs once they understand the UATG flow by following this quickstart or the tutorial. .. warning:: You will need to change ``user`` to your username in the below file. diff --git a/docs/source/overview.rst b/docs/source/overview.rst index 96169f6..5af5a04 100644 --- a/docs/source/overview.rst +++ b/docs/source/overview.rst @@ -29,7 +29,7 @@ parameterized set of coverpoints which can help indicate the health of the test- target. The parameterized tests in UATG are written as python-plugins which can generate assembly programs. -These python programs are required to follow a certain API has outlined by UATG and produce artifacts +These python programs are required to follow a certain API as outlined by UATG and produce artifacts which UATG can use to generate the final Assembly tests. These python plugins have the capability to generate relevant tests based on the configuration of the target DUT or skip generation completely if certain features in the configuration have been disabled. diff --git a/docs/source/tutorial.rst b/docs/source/tutorial.rst index 860d453..5dc3d5c 100644 --- a/docs/source/tutorial.rst +++ b/docs/source/tutorial.rst @@ -639,9 +639,14 @@ executable: $ cd ~/myquickstart $ git clone https://gitlab.com/incoresemi/core-generators/chromite.git $ cd chromite - $ pip install -r requirements.txt - $ python -m configure.main + $ git checkout using-csrbox + $ pip install -U -r requirements.txt + $ python -m configure.main -ispec sample_config/c64/rv64i_isa.yaml \ + -customspec sample_config/c64/rv64i_custom.yaml \ + -cspec sample_config/c64/core64.yaml \ + -gspec sample_config/c64/csr_grouping64.yaml --verbose debug $ make -j generate_verilog + $ make link_verilator generate_boot_files The above steps shall generate a directory: ``build/hw/verilog`` which includes all the generated verilog files. diff --git a/setup.cfg b/setup.cfg index 287669f..7ad9785 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.1.0 +current_version = 1.2.0 commit = True tag = True diff --git a/setup.py b/setup.py index ce56341..d120fc2 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ def read_requires(): setup( name='uatg', - version='1.1.0', + version='1.2.0', description="UATG - Micro-Architecture (µArch) Tests Generator", long_description=readme + '\n\n', classifiers=[ diff --git a/uatg/__init__.py b/uatg/__init__.py index baf9ac8..6f51bab 100644 --- a/uatg/__init__.py +++ b/uatg/__init__.py @@ -4,4 +4,4 @@ __author__ = """InCore Semiconductors Pvt Ltd""" __email__ = 'neelgala@incoresemi.com' -__version__ = '1.1.0' +__version__ = '1.2.0' diff --git a/uatg/env/arch_test.h b/uatg/env/arch_test.h index d2ace49..2238d6a 100644 --- a/uatg/env/arch_test.h +++ b/uatg/env/arch_test.h @@ -11,8 +11,6 @@ // #define rvtest_gpr_save // #endif -#define TEST_CASE_1 - //----------------------------------------------------------------------- // RV Arch Test Macros //----------------------------------------------------------------------- @@ -94,378 +92,134 @@ #define CODE_REL_TVAL_MSK 0xD008 << (REGWIDTH*8-16) #endif +// load the trap handler address to mtvec +// on trap, update mcause and copy mepc +// save mepc, increment by 4 or 2 -// ----------------------------------- CODE BEGIN w/ TRAP HANDLER START ------------------------ // +//initialize trap address into mtvec .macro RVTEST_CODE_BEGIN .align UNROLLSZ .section .text.init; - .globl rvtest_init; \ + .global rvtest_init; + rvtest_init: #ifdef rvtest_mtrap_routine - LA(x1, rvtest_trap_prolog ); - jalr ra, x1 - rvtest_prolog_done: + la t1, trap_handler_entry + csrw CSR_MTVEC, t1 #endif - LI (x1, (0xFEEDBEADFEEDBEAD & MASK)); - LI (x2, (0xFF76DF56FF76DF56 & MASK)); - LI (x3, (0x7FBB6FAB7FBB6FAB & MASK)); - LI (x4, (0xBFDDB7D5BFDDB7D5 & MASK)); - LA (x5, rvtest_code_begin); - LA (x6, rvtest_data_begin); - LI (x7, (0xB7FBB6FAB7FBB6FA & MASK)); - LI (x8, (0x5BFDDB7D5BFDDB7D & MASK)); - LI (x9, (0xADFEEDBEADFEEDBE & MASK)); - LI (x10, (0x56FF76DF56FF76DF & MASK)); - LI (x11, (0xAB7FBB6FAB7FBB6F & MASK)); - LI (x12, (0xD5BFDDB7D5BFDDB7 & MASK)); - LI (x13, (0xEADFEEDBEADFEEDB & MASK)); - LI (x14, (0xF56FF76DF56FF76D & MASK)); - LI (x15, (0xFAB7FBB6FAB7FBB6 & MASK)); - LI (x16, (0x7D5BFDDB7D5BFDDB & MASK)); - LI (x17, (0xBEADFEEDBEADFEED & MASK)); - LI (x18, (0xDF56FF76DF56FF76 & MASK)); - LI (x19, (0x6FAB7FBB6FAB7FBB & MASK)); - LI (x20, (0xB7D5BFDDB7D5BFDD & MASK)); - LI (x21, (0xDBEADFEEDBEADFEE & MASK)); - LI (x22, (0x6DF56FF76DF56FF7 & MASK)); - LI (x23, (0xB6FAB7FBB6FAB7FB & MASK)); - LI (x24, (0xDB7D5BFDDB7D5BFD & MASK)); - LI (x25, (0xEDBEADFEEDBEADFE & MASK)); - LI (x26, (0x76DF56FF76DF56FF & MASK)); - LI (x27, (0xBB6FAB7FBB6FAB7F & MASK)); - LI (x28, (0xDDB7D5BFDDB7D5BF & MASK)); - LI (x29, (0xEEDBEADFEEDBEADF & MASK)); - LI (x30, (0xF76DF56FF76DF56F & MASK)); - LI (x31, (0xFBB6FAB7FBB6FAB7 & MASK)); .globl rvtest_code_begin rvtest_code_begin: .endm -// --------------------------------- CODE BEGIN w/ TRAP HANDLER END -----------------------------// - .macro RVTEST_CODE_END - .align 4; + .align 4; .global rvtest_code_end rvtest_code_end: #ifdef rvtest_mtrap_routine .option push - .option norvc - j exit_cleanup - - rvtest_trap_prolog: - /******************************************************************************/ - /**** Prolog, to be run before any tests ****/ - /**** #include 1 copy of this per mode in rvmodel_boot code? ****/ - /**** ------------------------------------------------------------------- ****/ - /**** if xTVEC isn't completely RW, then we need to change the code at its ****/ - /**** target. The entire trap trampoline and mtrap handler replaces the ****/ - /**** area pointed to by mtvec, after saving its original contents first. ****/ - /**** If it isn't possible to fully write that area, restore and fail. ****/ - /******************************************************************************/ - - //trap_handler_prolog; enter with t1..t6 available - - init_mscratch: - la t1, trapreg_sv - csrrw t1, CSR_MSCRATCH, t1 // swap old mscratch. mscratch not points to trapreg_sv - la t2, mscratch_save - SREG t1, 0(t2) // save old mscratch in mscratch_save region - csrr t1, CSR_MSCRATCH // read the trapreg_sv address - LA( t2, mtrap_sigptr ) // locate the start of the trap signature - SREG t2, 0(t1) // save mtrap_sigptr at first location of trapreg_sv - init_mtvec: - la t1, mtrampoline - la t4, mtvec_save - csrrw t2, CSR_MTVEC, t1 // swap mtvec and trap_trampoline - SREG t2, 0(t4) // save orig mtvec - csrr t3, CSR_MTVEC // now read new_mtval back - beq t3, t1, rvtest_prolog_done // if mtvec==trap_trampoline, mtvec is writable, continue - - /****************************************************************/ - /**** fixed mtvec, can't move it so move trampoline instead ****/ - /**** t1=trampoline, t2=oldmtvec, t3=save area, t4=save end ****/ - /****************************************************************/ - - // t2 = dut's original mtvec setting - // t1 = mtrampoline address - init_tramp: /**** copy trampoline at mtvec tgt ****/ - - csrw CSR_MTVEC, t2 // restore orig mtvec, will now attemp to copy trampoline to it - la t3, tramptbl_sv // addr of save area - addi t4, t3, NUM_SPECD_INTCAUSES*4 // end of save area - - overwrite_tt: // now build new trampoline table with offsets base from curr mtvec - lw t6, 0(t2) // get original mtvec target - sw t6, 0(t3) // save it - lw t5, 0(t1) // get trampoline src - sw t5, 0(t2) // overwrite mtvec target - lw t6, 0(t2) // rd it back to make sure it was written - bne t6, t5, resto_tramp // table isn't fully writable, restore and give up - addi t1, t1, 4 // next src index - addi t2, t2, 4 // next tgt index - addi t3, t3, 4 // next save index - bne t3, t4, overwrite_tt // not done, loop - j rvtest_prolog_done - - resto_tramp: // vector table not writeable, restore - LREG t1, 16(t4) // load mscratch_SAVE at fixed offset from table end - csrw CSR_MSCRATCH, t1 // restore mscratch - LREG t4, 8(t4) // load mtvec_SAVE (used as end of loop marker) - - - resto_loop: // goes backwards, t2= dest vec tbl ptr, t3=src save area ptr, t4=vec tbl begin - lw t6, 0(t3) // read saved tgt entry - sw t6, 0(t2) // restore original tgt - addi t2, t2, -4 // prev tgt index - addi t3, t3, -4 // prev save index - bne t2, t4, resto_loop // didn't restore to begining yet, loop - - j rvtest_end // failure to replace trampoline + .option norvc + j end_code - #define mhandler \ - csrrw sp, CSR_MSCRATCH, sp; \ - SREG t6, 6*REGWIDTH(sp); \ - jal t6, common_prolog; - - /**********************************************************************/ - /**** This is the entry point for all m-modetraps, vectored or not.****/ - /**** At entry, mscratch will contain a pointer to a scratch area. ****/ - /**** This is an array of branches at 4B intevals that spreads out ****/ - /**** to an array of 32B mhandler macros for specd int causes, and ****/ - /**** to a return for anything above that (which causes a mismatch)****/ - /**********************************************************************/ - mtrampoline: // 64 or 32 entry table - value = 0 - .rept NUM_SPECD_INTCAUSES // located at each possible int vectors - j mtrap_handler + 12*(value) //offset < +/- 1MB - value = value + 1 - .endr - .rept RLENG-NUM_SPECD_INTCAUSES // fill at each impossible entry - mret - .endr - - mtrap_handler: /* after executing, sp points to temp save area, t4 is PC */ - .rept NUM_SPECD_INTCAUSES - mhandler - .endr - - common_prolog: - la t5, common_mhandler - jr t5 - /*********************************************************************/ - /**** common code for all ints & exceptions, will fork to handle ****/ - /**** each separately. The common handler first stores trap mode+ ****/ - /**** vector, and mcause signatures. All traps have 4wd sigs, but ****/ - /**** sw and timer ints only store 3 of the 4. ****/ - /**** sig offset Exception ExtInt SWInt TimerInt ****/ - /**** 0: tval IntID -1 -1 ****/ - /**** 4: mepc mip mip mip ****/ - /**** 8: <---------------------- mcause -------------> ****/ - /**** 12: <--------------------- Vect+mode ----------> ****/ - /*********************************************************************/ - /* in general, CSRs loaded in t2, addresses into t3 */ - - common_mhandler: /* enter with link in t6 */ - SREG t5, 5*REGWIDTH(sp) - SREG t4, 4*REGWIDTH(sp) - SREG t3, 3*REGWIDTH(sp) - SREG t2, 2*REGWIDTH(sp) - SREG t1, 1*REGWIDTH(sp) /* save other temporaries */ - - LREG t1, 0(sp) /* load trap sig pointer (runs backwards from DATA_END) */ - - LA( t3, mtrampoline) - sub t2, t6, t3 /* reloc “link” to 0..63 to show which int vector was taken */ - addi t2, t2, MMODE_SIG /* insert mode# into 1:0 */ - SREG t2, 0*REGWIDTH(t1) /* save 1st sig value, (vect, trapmode) */ - sv_mcause: - csrr t2, CSR_MCAUSE - SREG t2, 1*REGWIDTH(t1) /* save 2nd sig value, (mcause) */ - - bltz t2, common_mint_handler /* this is a interrupt, not a trap */ - - /********************************************************************/ - /**** This is the exceptions specific code, storing relative mepc****/ - /**** & relative tval signatures. tval is relocated by code or ****/ - /**** data start, or 0 depending on mcause. mepc signature value ****/ - /**** is relocated by code start, and restored adjusted depending****/ - /**** on op alignment so trapped op isn't re-executed. ****/ - /********************************************************************/ - common_mexcpt_handler: - csrr t2, CSR_MEPC - sv_mepc: - LA( t3, rvtest_prolog_done) /* offset to compensate for different loader offsets */ - sub t4, t2, t3 /* convert mepc to rel offset of beginning of test*/ - SREG t4, 2*REGWIDTH(t1) /* save 3rd sig value, (rel mepc) into trap signature area */ - adj_mepc: //adj mepc so there is padding after op, and its 8B aligned - andi t4, t2, 0x2 /* set to 2 if mepc was misaligned */ - sub t2, t2, t4 /* adjust mepc to prev 4B alignment */ - addi t2, t2, 0x8 /* adjust mepc, so it skips past the op, has padding & is 4B aligned */ - csrw CSR_MEPC, t2 /* restore adjusted value, has 1,2, or 3 bytes of padding */ - - - /* calculate relative mtval if it’s an address (by code_begin or data_begin amt) */ - /* note that masks that determine this are implementation specific from YAML */ - - /* masks are bit reversed, so mcause==0 bit is in MSB (so different for RV32 and RV64) */ - - adj_mtval: - csrr t2, CSR_MCAUSE /* code begin adjustment amount already in t3 */ - - LI(t4, CODE_REL_TVAL_MSK) /* trap#s 12, 3,1,0, -- adjust w/ code_begin */ - sll t4, t4, t2 /* put bit# in MSB */ - bltz t4, sv_mtval /* correct adjustment is code_begin in t3 */ - - LA( t3, mtrap_sigptr) /* adjustment assuming access is to signature region */ - LI(t4, DATA_REL_TVAL_MSK) /* trap#s not 14, 11..8, 2 adjust w/ data_begin */ - sll t4, t4, t2 /* put bit# in MSB */ - bgez t4, no_adj /* correct adjustment is data_begin in t3 */ - sigbound_chk: - csrr t4, CSR_MTVAL /* do a bounds check on mtval */ - bge t3, t4, sv_mtval /* if mtval is greater than the rvmodel_data_begin then use that as anchor */ - LA( t3, rvtest_data_begin) /* else change anchor to rvtest_data_begin */ - blt t3, t4, sv_mtval /* before the signature, use data_begin adj */ - mv t4, t3 /* use sig relative adjust */ - no_adj: - LI(t3, 0) /* else zero adjustment amt */ - - // For Illegal op handling - addi t2, t2, -2 /* check if mcause==2 (illegal op) */ - bnez t2, sv_mtval /* not illegal op, no special treatment */ - csrr t2, CSR_MTVAL - bnez t2, sv_mtval /* mtval isn’t zero, no special treatment */ - illop: - LI(t5, 0x20000) /* get mprv mask */ - csrrs t5, CSR_MSTATUS, t5 /* set mprv while saving the old value */ - csrr t3, CSR_MEPC - lhu t2, 0(t3) /* load 1st 16b of opc w/ old priv, endianess*/ - andi t4, t2, 0x3 - addi t4, t4, -0x3 /* does opcode[1:0]==0b11? (Meaning >16b op) */ - bnez t4, sv_mtval /* entire mtval is in tt2, adj amt will be set to zero */ - lhu t4, 2(t3) - sll t4, t4, 16 - or t3, t2, t4 /* get 2nd hwd, align it & insert it into opcode */ - csrw CSR_MSTATUS, t5 /* restore mstatus */ - -/*******FIXME: this will not handle 48 or 64b opcodes in an RV64) ********/ - - sv_mtval: - csrr t2, CSR_MTVAL - sub t2, t2, t3 /* perform mtval adjust by either code or data position or zero*/ - SREG t2, 3*REGWIDTH(t1) /* save 4th sig value, (rel mtval) into trap signature area */ - - resto_rtn: /* restore and return */ - addi t1, t1,4*REGWIDTH /* adjust trap signature ptr (traps always save 4 words) */ - SREG t1, 0*REGWIDTH(sp) /* save updated trap sig pointer (pts to trap_sigptr */ - - LREG t1, 1*REGWIDTH(sp) - LREG t2, 2*REGWIDTH(sp) - LREG t3, 3*REGWIDTH(sp) - LREG t4, 4*REGWIDTH(sp) - LREG t5, 5*REGWIDTH(sp) - LREG t6, 6*REGWIDTH(sp) /* restore temporaries */ - - csrrw sp, CSR_MSCRATCH, sp /* restore sp from scratch */ - mret - - common_mint_handler: /* t1 has sig ptr, t2 has mcause */ - - LI(t3, 1) - sll t3, t3, t2 /* create mask 1< ****/ +// /**** 12: <--------------------- Vect+mode ----------> ****/ +// /*********************************************************************/ +// /* in general, CSRs loaded in t2, addresses into t3 */ +// +// common_mhandler: /* enter with link in t6 */ +// SREG t5, 5*REGWIDTH(sp) +// SREG t4, 4*REGWIDTH(sp) +// SREG t3, 3*REGWIDTH(sp) +// SREG t2, 2*REGWIDTH(sp) +// SREG t1, 1*REGWIDTH(sp) /* save other temporaries */ +// +// LREG t1, 0(sp) /* load trap sig pointer (runs backwards from DATA_END) */ +// +// LA( t3, mtrampoline) +// sub t2, t6, t3 /* reloc “link” to 0..63 to show which int vector was taken */ +// addi t2, t2, MMODE_SIG /* insert mode# into 1:0 */ +// SREG t2, 0*REGWIDTH(t1) /* save 1st sig value, (vect, trapmode) */ +// sv_mcause: +// csrr t2, CSR_MCAUSE +// SREG t2, 1*REGWIDTH(t1) /* save 2nd sig value, (mcause) */ +// +// bltz t2, common_mint_handler /* this is a interrupt, not a trap */ +// +// /********************************************************************/ +// /**** This is the exceptions specific code, storing relative mepc****/ +// /**** & relative tval signatures. tval is relocated by code or ****/ +// /**** data start, or 0 depending on mcause. mepc signature value ****/ +// /**** is relocated by code start, and restored adjusted depending****/ +// /**** on op alignment so trapped op isn't re-executed. ****/ +// /********************************************************************/ +// common_mexcpt_handler: +// csrr t2, CSR_MEPC +// sv_mepc: +// LA( t3, rvtest_prolog_done) /* offset to compensate for different loader offsets */ +// sub t4, t2, t3 /* convert mepc to rel offset of beginning of test*/ +// SREG t4, 2*REGWIDTH(t1) /* save 3rd sig value, (rel mepc) into trap signature area */ +// adj_mepc: //adj mepc so there is padding after op, and its 8B aligned +// andi t4, t2, 0x2 /* set to 2 if mepc was misaligned */ +// sub t2, t2, t4 /* adjust mepc to prev 4B alignment */ +// addi t2, t2, 0x8 /* adjust mepc, so it skips past the op, has padding & is 4B aligned */ +// csrw CSR_MEPC, t2 /* restore adjusted value, has 1,2, or 3 bytes of padding */ +// +// +// /* calculate relative mtval if it’s an address (by code_begin or data_begin amt) */ +// /* note that masks that determine this are implementation specific from YAML */ +// +// /* masks are bit reversed, so mcause==0 bit is in MSB (so different for RV32 and RV64) */ +// +// adj_mtval: +// csrr t2, CSR_MCAUSE /* code begin adjustment amount already in t3 */ +// +// LI(t4, CODE_REL_TVAL_MSK) /* trap#s 12, 3,1,0, -- adjust w/ code_begin */ +// sll t4, t4, t2 /* put bit# in MSB */ +// bltz t4, sv_mtval /* correct adjustment is code_begin in t3 */ +// +// LA( t3, mtrap_sigptr) /* adjustment assuming access is to signature region */ +// LI(t4, DATA_REL_TVAL_MSK) /* trap#s not 14, 11..8, 2 adjust w/ data_begin */ +// sll t4, t4, t2 /* put bit# in MSB */ +// bgez t4, no_adj /* correct adjustment is data_begin in t3 */ +// sigbound_chk: +// csrr t4, CSR_MTVAL /* do a bounds check on mtval */ +// bge t3, t4, sv_mtval /* if mtval is greater than the rvmodel_data_begin then use that as anchor */ +// LA( t3, rvtest_data_begin) /* else change anchor to rvtest_data_begin */ +// blt t3, t4, sv_mtval /* before the signature, use data_begin adj */ +// mv t4, t3 /* use sig relative adjust */ +// no_adj: +// LI(t3, 0) /* else zero adjustment amt */ +// +// // For Illegal op handling +// addi t2, t2, -2 /* check if mcause==2 (illegal op) */ +// bnez t2, sv_mtval /* not illegal op, no special treatment */ +// csrr t2, CSR_MTVAL +// bnez t2, sv_mtval /* mtval isn’t zero, no special treatment */ +// illop: +// LI(t5, 0x20000) /* get mprv mask */ +// csrrs t5, CSR_MSTATUS, t5 /* set mprv while saving the old value */ +// csrr t3, CSR_MEPC +// lhu t2, 0(t3) /* load 1st 16b of opc w/ old priv, endianess*/ +// andi t4, t2, 0x3 +// addi t4, t4, -0x3 /* does opcode[1:0]==0b11? (Meaning >16b op) */ +// bnez t4, sv_mtval /* entire mtval is in tt2, adj amt will be set to zero */ +// lhu t4, 2(t3) +// sll t4, t4, 16 +// or t3, t2, t4 /* get 2nd hwd, align it & insert it into opcode */ +// csrw CSR_MSTATUS, t5 /* restore mstatus */ +// +///*******FIXME: this will not handle 48 or 64b opcodes in an RV64) ********/ +// +// sv_mtval: +// csrr t2, CSR_MTVAL +// sub t2, t2, t3 /* perform mtval adjust by either code or data position or zero*/ +// SREG t2, 3*REGWIDTH(t1) /* save 4th sig value, (rel mtval) into trap signature area */ +// +// resto_rtn: /* restore and return */ +// addi t1, t1,4*REGWIDTH /* adjust trap signature ptr (traps always save 4 words) */ +// SREG t1, 0*REGWIDTH(sp) /* save updated trap sig pointer (pts to trap_sigptr */ +// +// LREG t1, 1*REGWIDTH(sp) +// LREG t2, 2*REGWIDTH(sp) +// LREG t3, 3*REGWIDTH(sp) +// LREG t4, 4*REGWIDTH(sp) +// LREG t5, 5*REGWIDTH(sp) +// LREG t6, 6*REGWIDTH(sp) /* restore temporaries */ +// +// csrrw sp, CSR_MSCRATCH, sp /* restore sp from scratch */ +// mret +// +// common_mint_handler: /* t1 has sig ptr, t2 has mcause */ +// +// LI(t3, 1) +// sll t3, t3, t2 /* create mask 1<= 0 ;\ @@ -648,19 +827,22 @@ .else ;\ .set num,0 ;\ .endif ;\ + .if (adj & 2 == 2) && num >= 2 ;\ + .set num, num-2 ;\ + .endif ;\ .if label == 1b ;\ .set num,0 ;\ .endif ;\ .rept num ;\ nop ;\ .endr ;\ -3: .if adj & 2 == 2 ;\ +3: .if (adj & 2 == 2) && (label == 3f) ;\ .fill 2,1,0x00 ;\ .endif ;\ xori rd,rd, 0x3 ;\ LA(tempreg, 4f ) ;\ jalr x0,0(tempreg) ;\ - .if adj&2 == 2 ;\ + .if (adj&2 == 2) && (label == 3f) ;\ .fill 2,1,0x00 ;\ .endif ;\ 4: LA(tempreg, 5b ) ;\ diff --git a/uatg/instruction_constants.py b/uatg/instruction_constants.py index 62d4c11..272580f 100644 --- a/uatg/instruction_constants.py +++ b/uatg/instruction_constants.py @@ -1,75 +1,271 @@ # See LICENSE.incore for license details -from typing import List base_reg_file = ['x' + str(reg_no) for reg_no in range(32)] +# Opcode data +rv32_encodings = { + 'i': [ + "add rd rs1 rs2 31..25=0 14..12=0 6..2=0x0C 1..0=3", + "sub rd rs1 rs2 31..25=0 14..12=0 6..2=0x0C 1..0=3", + "sll rd rs1 rs2 31..25=0 14..12=1 6..2=0x0C 1..0=3", + "slt rd rs1 rs2 31..25=0 14..12=2 6..2=0x0C 1..0=3", + "sltu rd rs1 rs2 31..25=0 14..12=3 6..2=0x0C 1..0=3", + "xor rd rs1 rs2 31..25=0 14..12=4 6..2=0x0C 1..0=3", + "srl rd rs1 rs2 31..25=0 14..12=5 6..2=0x0C 1..0=3", + "sra rd rs1 rs2 31..25=0 14..12=5 6..2=0x0C 1..0=3", + "or rd rs1 rs2 31..25=0 14..12=6 6..2=0x0C 1..0=3", + "and rd rs1 rs2 31..25=0 14..12=7 6..2=0x0C 1..0=3", + ], + 'm': [ + "mul rd rs1 rs2 31..25=1 14..12=0 6..2=0x0C 1..0=3", + "mulh rd rs1 rs2 31..25=1 14..12=1 6..2=0x0C 1..0=3", + "mulhsu rd rs1 rs2 31..25=1 14..12=2 6..2=0x0C 1..0=3", + "mulhu rd rs1 rs2 31..25=1 14..12=3 6..2=0x0C 1..0=3", + "div rd rs1 rs2 31..25=1 14..12=4 6..2=0x0C 1..0=3", + "divu rd rs1 rs2 31..25=1 14..12=5 6..2=0x0C 1..0=3", + "rem rd rs1 rs2 31..25=1 14..12=6 6..2=0x0C 1..0=3", + "remu rd rs1 rs2 31..25=1 14..12=7 6..2=0x0C 1..0=3", + ], + 'a': [ + "amoadd.w rd rs1 rs2 aqrl 31..29=0 28..27=0 14..12=2 6..2=0x0B " + "1..0=3", + "amoxor.w rd rs1 rs2 aqrl 31..29=1 28..27=0 14..12=2 6..2=0x0B " + "1..0=3", + "amoor.w rd rs1 rs2 aqrl 31..29=2 28..27=0 14..12=2 6..2=0x0B " + "1..0=3", + "amoand.w rd rs1 rs2 aqrl 31..29=3 28..27=0 14..12=2 6..2=0x0B " + "1..0=3", + "amomin.w rd rs1 rs2 aqrl 31..29=4 28..27=0 14..12=2 6..2=0x0B " + "1..0=3", + "amomax.w rd rs1 rs2 aqrl 31..29=5 28..27=0 14..12=2 6..2=0x0B " + "1..0=3", + "amominu.w rd rs1 rs2 aqrl 31..29=6 28..27=0 14..12=2 6..2=0x0B " + "1..0=3", + "amomaxu.w rd rs1 rs2 aqrl 31..29=7 28..27=0 14..12=2 6..2=0x0B " + "1..0=3", + "amoswap.w rd rs1 rs2 aqrl 31..29=0 28..27=1 14..12=2 6..2=0x0B " + "1..0=3", + "lr.w rd rs1 24..20=0 aqrl 31..29=0 28..27=2 14..12=2 6..2=0x0B " + "1..0=3", + "sc.w rd rs1 rs2 aqrl 31..29=0 28..27=3 14..12=2 6..2=0x0B " + "1..0=3", + ], + 'f': [ + "fadd.s rd rs1 rs2 31..27=0x00 rm 26..25=0 6..2=0x14 1..0=3", + "fsub.s rd rs1 rs2 31..27=0x01 rm 26..25=0 6..2=0x14 1..0=3", + "fmul.s rd rs1 rs2 31..27=0x02 rm 26..25=0 6..2=0x14 1..0=3", + "fdiv.s rd rs1 rs2 31..27=0x03 rm 26..25=0 6..2=0x14 1..0=3", + "fsgnj.s rd rs1 rs2 31..27=0x04 14..12=0 26..25=0 6..2=0x14 1..0=3", + "fsgnjn.s rd rs1 rs2 31..27=0x04 14..12=1 26..25=0 6..2=0x14 1..0=3", + "fsgnjx.s rd rs1 rs2 31..27=0x04 14..12=2 26..25=0 6..2=0x14 1..0=3", + "fmin.s rd rs1 rs2 31..27=0x05 14..12=0 26..25=0 6..2=0x14 1..0=3", + "fmax.s rd rs1 rs2 31..27=0x05 14..12=1 26..25=0 6..2=0x14 1..0=3", + "fsqrt.s rd rs1 24..20=0 31..27=0x0B rm 26..25=0 6..2=0x14 1..0=3", + "fle.s rd rs1 rs2 31..27=0x14 14..12=0 26..25=0 6..2=0x14 1..0=3", + "flt.s rd rs1 rs2 31..27=0x14 14..12=1 26..25=0 6..2=0x14 1..0=3", + "feq.s rd rs1 rs2 31..27=0x14 14..12=2 26..25=0 6..2=0x14 1..0=3", + "fcvt.w.s rd rs1 24..20=0 31..27=0x18 rm 26..25=0 6..2=0x14 1..0=3", + "fcvt.wu.s rd rs1 24..20=1 31..27=0x18 rm 26..25=0 6..2=0x14 1..0=3", + "fmv.x.w rd rs1 24..20=0 31..27=0x1C 14..12=0 26..25=0 6..2=0x14 1..0=3", + "fclass.s rd rs1 24..20=0 31..27=0x1C 14..12=1 26..25=0 6..2=0x14 1..0=3", + "fcvt.s.w rd rs1 24..20=0 31..27=0x1A rm 26..25=0 6..2=0x14 1..0=3", + "fcvt.s.wu rd rs1 24..20=1 31..27=0x1A rm 26..25=0 6..2=0x14 1..0=3", + "fmv.w.x rd rs1 24..20=0 31..27=0x1E 14..12=0 26..25=0 6..2=0x14 1..0=3", + "flw rd rs1 imm12 14..12=2 6..2=0x01 1..0=3", + "fsw imm12hi rs1 rs2 imm12lo 14..12=2 6..2=0x09 1..0=3", + "fmadd.s rd rs1 rs2 rs3 rm 26..25=0 6..2=0x10 1..0=3", + "fmsub.s rd rs1 rs2 rs3 rm 26..25=0 6..2=0x11 1..0=3", + "fnmsub.s rd rs1 rs2 rs3 rm 26..25=0 6..2=0x12 1..0=3", + "fnmadd.s rd rs1 rs2 rs3 rm 26..25=0 6..2=0x13 1..0=3", + ], + 'd': [ + "fadd.d rd rs1 rs2 31..27=0x00 rm 26..25=1 6..2=0x14 1..0=3", + "fsub.d rd rs1 rs2 31..27=0x01 rm 26..25=1 6..2=0x14 1..0=3", + "fmul.d rd rs1 rs2 31..27=0x02 rm 26..25=1 6..2=0x14 1..0=3", + "fdiv.d rd rs1 rs2 31..27=0x03 rm 26..25=1 6..2=0x14 1..0=3", + "fsgnj.d rd rs1 rs2 31..27=0x04 14..12=0 26..25=1 6..2=0x14 1..0=3", + "fsgnjn.d rd rs1 rs2 31..27=0x04 14..12=1 26..25=1 6..2=0x14 1..0=3", + "fsgnjx.d rd rs1 rs2 31..27=0x04 14..12=2 26..25=1 6..2=0x14 1..0=3", + "fmin.d rd rs1 rs2 31..27=0x05 14..12=0 26..25=1 6..2=0x14 1..0=3", + "fmax.d rd rs1 rs2 31..27=0x05 14..12=1 26..25=1 6..2=0x14 1..0=3", + "fcvt.s.d rd rs1 24..20=1 31..27=0x08 rm 26..25=0 6..2=0x14 1..0=3", + "fcvt.d.s rd rs1 24..20=0 31..27=0x08 rm 26..25=1 6..2=0x14 1..0=3", + "fsqrt.d rd rs1 24..20=0 31..27=0x0B rm 26..25=1 6..2=0x14 1..0=3", + "fle.d rd rs1 rs2 31..27=0x14 14..12=0 26..25=1 6..2=0x14 1..0=3", + "flt.d rd rs1 rs2 31..27=0x14 14..12=1 26..25=1 6..2=0x14 1..0=3", + "feq.d rd rs1 rs2 31..27=0x14 14..12=2 26..25=1 6..2=0x14 1..0=3", + "fcvt.w.d rd rs1 24..20=0 31..27=0x18 rm 26..25=1 6..2=0x14 1..0=3", + "fcvt.wu.d rd rs1 24..20=1 31..27=0x18 rm 26..25=1 6..2=0x14 1..0=3", + "fclass.d rd rs1 24..20=0 31..27=0x1C 14..12=1 26..25=1 6..2=0x14 1..0=3", + "fcvt.d.w rd rs1 24..20=0 31..27=0x1A rm 26..25=1 6..2=0x14 1..0=3", + "fcvt.d.wu rd rs1 24..20=1 31..27=0x1A rm 26..25=1 6..2=0x14 1..0=3", + "fld rd rs1 imm12 14..12=3 6..2=0x01 1..0=3", + "fsd imm12hi rs1 rs2 imm12lo 14..12=3 6..2=0x09 1..0=3", + "fmadd.d rd rs1 rs2 rs3 rm 26..25=1 6..2=0x10 1..0=3", + "fmsub.d rd rs1 rs2 rs3 rm 26..25=1 6..2=0x11 1..0=3", + "fnmsub.d rd rs1 rs2 rs3 rm 26..25=1 6..2=0x12 1..0=3", + "fnmadd.d rd rs1 rs2 rs3 rm 26..25=1 6..2=0x13 1..0=3", + ], + # 'c': [ + # "@c.srli.rv32 1..0=1 15..13=4 12=0 11..10=0 9..2=ignore", + # "@c.srai.rv32 1..0=1 15..13=4 12=0 11..10=1 9..2=ignore", + # "@c.slli.rv32 1..0=2 15..13=0 12=0 11..2=ignore", + # ], +} +rv64_encodings = { + 'i': rv32_encodings['i'] + [ + "addiw rd rs1 imm12 14..12=0 6..2=0x06 1..0=3", + "slliw rd rs1 31..25=0 shamtw 14..12=1 6..2=0x06 1..0=3", + "srliw rd rs1 31..25=0 shamtw 14..12=5 6..2=0x06 1..0=3", + "sraiw rd rs1 31..25=32 shamtw 14..12=5 6..2=0x06 1..0=3", + "addw rd rs1 rs2 31..25=0 14..12=0 6..2=0x0E 1..0=3", + "subw rd rs1 rs2 31..25=32 14..12=0 6..2=0x0E 1..0=3", + "sllw rd rs1 rs2 31..25=0 14..12=1 6..2=0x0E 1..0=3", + "srlw rd rs1 rs2 31..25=0 14..12=5 6..2=0x0E 1..0=3", + "sraw rd rs1 rs2 31..25=32 14..12=5 6..2=0x0E 1..0=3", + "ld rd rs1 imm12 14..12=3 6..2=0x00 1..0=3", + "lwu rd rs1 imm12 14..12=6 6..2=0x00 1..0=3", + "sd imm12hi rs1 rs2 imm12lo 14..12=3 6..2=0x08 1..0=3", + "slli rd rs1 31..26=0 shamt 14..12=1 6..2=0x04 1..0=3", + "srli rd rs1 31..26=0 shamt 14..12=5 6..2=0x04 1..0=3", + "srai rd rs1 31..26=16 shamt 14..12=5 6..2=0x04 1..0=3", + ], + 'm': rv32_encodings['i'] + [ + "mulw rd rs1 rs2 31..25=1 14..12=0 6..2=0x0E 1..0=3", + "divw rd rs1 rs2 31..25=1 14..12=4 6..2=0x0E 1..0=3", + "divuw rd rs1 rs2 31..25=1 14..12=5 6..2=0x0E 1..0=3", + "remw rd rs1 rs2 31..25=1 14..12=6 6..2=0x0E 1..0=3", + "remuw rd rs1 rs2 31..25=1 14..12=7 6..2=0x0E 1..0=3", + ], + 'a': rv32_encodings['a'] + [ + "amoadd.d rd rs1 rs2 aqrl 31..29=0 28..27=0 14..12=3 6..2=0x0B" + " 1..0=3", + "amoxor.d rd rs1 rs2 aqrl 31..29=1 28..27=0 14..12=3 6..2=0x0B" + " 1..0=3", + "amoor.d rd rs1 rs2 aqrl 31..29=2 28..27=0 14..12=3 6..2=0x0B" + " 1..0=3", + "amoand.d rd rs1 rs2 aqrl 31..29=3 28..27=0 14..12=3 6..2=0x0B" + " 1..0=3", + "amomin.d rd rs1 rs2 aqrl 31..29=4 28..27=0 14..12=3 6..2=0x0B" + " 1..0=3", + "amomax.d rd rs1 rs2 aqrl 31..29=5 28..27=0 14..12=3 6..2=0x0B" + " 1..0=3", + "amominu.d rd rs1 rs2 aqrl 31..29=6 28..27=0 14..12=3 6..2=0x0B" + " 1..0=3", + "amomaxu.d rd rs1 rs2 aqrl 31..29=7 28..27=0 14..12=3 6..2=0x0B" + " 1..0=3", + "amoswap.d rd rs1 rs2 aqrl 31..29=0 28..27=1 14..12=3 6..2=0x0B" + " 1..0=3", + "lr.d rd rs1 24..20=0 aqrl 31..29=0 28..27=2 14..12=3 6..2=0x0B" + " 1..0=3", + "sc.d rd rs1 rs2 aqrl 31..29=0 28..27=3 14..12=3 6..2=0x0B" + " 1..0=3", + ], + 'f': rv32_encodings['f'] + [ + "fcvt.l.s rd rs1 24..20=2 31..27=0x18 rm 26..25=0 6..2=0x14 1..0=3", + "fcvt.lu.s rd rs1 24..20=3 31..27=0x18 rm 26..25=0 6..2=0x14 1..0=3", + "fcvt.s.l rd rs1 24..20=2 31..27=0x1A rm 26..25=0 6..2=0x14 1..0=3", + "fcvt.s.lu rd rs1 24..20=3 31..27=0x1A rm 26..25=0 6..2=0x14 1..0=3", + ], + 'd': rv32_encodings['d'] + [ + "fcvt.l.d rd rs1 24..20=2 31..27=0x18 rm 26..25=1 6..2=0x14 1..0=3", + "fcvt.lu.d rd rs1 24..20=3 31..27=0x18 rm 26..25=1 6..2=0x14 1..0=3", + "fmv.x.d rd rs1 24..20=0 31..27=0x1C 14..12=0 26..25=1 6..2=0x14 " + "1..0=3", + "fcvt.d.l rd rs1 24..20=2 31..27=0x1A rm 26..25=1 6..2=0x14 1..0=3", + "fcvt.d.lu rd rs1 24..20=3 31..27=0x1A rm 26..25=1 6..2=0x14 1..0=3", + "fmv.d.x rd rs1 24..20=0 31..27=0x1E 14..12=0 26..25=1 6..2=0x14 " + "1..0=3", + ], + # 'c': rv32_encodings['c'] + [ + # "@c.ld 1..0=0 15..13=3 12=ignore 11..2=ignore # c.flw for RV32", + # "@c.sd 1..0=0 15..13=7 12=ignore 11..2=ignore # c.fsw for RV32", + # "c.subw 1..0=1 15..13=4 12=1 11..10=3 9..7=ignore 6..5=0 " + # "4..2=ignore", + # "c.addw 1..0=1 15..13=4 12=1 11..10=3 9..7=ignore 6..5=1 " + # "4..2=ignore", + # "@c.addiw 1..0=1 15..13=1 12=ignore 11..2=ignore # c.jal for RV32", + # "@c.ldsp 1..0=2 15..13=3 12=ignore 11..2=ignore # c.flwsp for RV32", + # "@c.sdsp 1..0=2 15..13=7 12=ignore 11..2=ignore # c.fswsp for RV32", + # ], +} + +# Instructions classified based on functions +mext_instructions = { + 'rv32-mul': ['mul', 'mulh', 'mulhsu', 'mulhu'], + 'rv32-div': ['div', 'divu', 'rem', 'remu'], + 'rv64-mul': ['mul', 'mulh', 'mulhsu', 'mulhu', 'mulw'], + 'rv64-div': ['div', 'divu', 'rem', 'remu', 'divw', 'divuw', 'remuw', 'remw'] +} arithmetic_instructions = { 'rv32-add-reg': ['add', 'sub'], - 'rv64-add-reg': ['add', 'addw', - 'sub', 'subw'], - 'rv128-add-reg': ['add', 'addw', 'addd', - 'sub', 'subw', 'subd'], - + 'rv64-add-reg': ['add', 'addw', 'sub', 'subw'], + 'rv128-add-reg': ['add', 'addw', 'addd', 'sub', 'subw', 'subd'], 'rv32-add-imm': ['addi'], 'rv64-add-imm': ['addi', 'addiw'], 'rv128-add-imm': ['addi', 'addiw', 'addid'], - 'rv32-shift-reg': ['sll', 'sra', 'srl'], - 'rv64-shift-reg': ['sll', 'sra', 'srl', - 'sllw', 'sraw', 'srlw'], - 'rv128-shift-reg': ['sll', 'sra', 'srl', - 'sllw', 'sraw', 'srlw' - 'slld', 'srad', 'srld'], - + 'rv64-shift-reg': ['sll', 'sra', 'srl', 'sllw', 'sraw', 'srlw'], + 'rv128-shift-reg': [ + 'sll', 'sra', 'srl', 'sllw', 'sraw', 'srlw' + 'slld', 'srad', 'srld' + ], 'rv32-shift-imm': ['slli', 'srli', 'srai'], 'rv64-shift-imm': ['slli', 'srli', 'srai', 'slliw', 'srliw', 'sraiw'], 'rv128-shift-imm': ['slli', 'srli', 'srai', 'slliw', 'srliw', 'sraiw', - 'sllid', 'srlid', 'sraid'] -} - -branch_instructions = { - 'branch': ['beq', 'bge', 'bgeu', 'blt', 'bltu', 'bne'] + 'sllid', 'srlid', 'sraid'], + 'rv32-ui': ['auipc', 'lui'], + 'rv64-ui': ['auipc', 'lui'], + 'rv128-ui': ['auipc', 'lui'] } - +branch_instructions = {'branch': ['beq', 'bge', 'bgeu', 'blt', 'bltu', 'bne']} csr_insts = { 'csr-reg': ['csrrc', 'csrrs', 'csrrw'], 'csr-imm': ['csrrci', 'csrrsi', 'csrrwi'], } -environment_instructions = { - 'env': ['ebreak', 'ecall'] -} -fence_instructions = { - 'fence': ['fence'], 'fencei': ['fence.i'] -} - -jump_instructions = { - 'jal': ['jal'], 'jalr': ['jalr'] -} - +environment_instructions = {'env': ['ebreak', 'ecall']} +fence_instructions = {'fence': ['fence'], 'fencei': ['fence.i']} +jump_instructions = {'jal': ['jal'], 'jalr': ['jalr']} logic_instructions = { 'logic-reg': ['and', 'or', 'slt', 'sltu', 'xor'], 'logic-imm': ['andi', 'ori', 'slti', 'sltiu', 'xori'], } - load_store_instructions = { - 'auipc': ['auipc'], 'rv32-loads': ['lb', 'lbu', 'lh', 'lhu', 'lw'], - 'rv64-loads': ['lb', 'lbu', 'lh', 'lhu', 'lw', - 'ld', 'lwu'], - 'rv128-loads': ['lb', 'lbu', 'lh', 'lhu', 'lw', - 'ld', 'lq', 'lwu', 'ldu'], + 'rv64-loads': ['lb', 'lbu', 'lh', 'lhu', 'lw', 'ld', 'lwu'], + 'rv128-loads': ['lb', 'lbu', 'lh', 'lhu', 'lw', 'ld', 'lq', 'lwu', 'ldu'], 'rv32-stores': ['sb', 'sh', 'sw'], - 'rv64-stores': ['sb', 'sh', 'sw', - 'sd'], - 'rv128s-stores': ['sb', 'sh', 'sw', - 'sd', 'sq'] + 'rv64-stores': ['sb', 'sh', 'sw', 'sd'], + 'rv128s-stores': ['sb', 'sh', 'sw', 'sd', 'sq'] } -def bit_walker(bit_width=8, n_ones=1, invert=False): +def twos(val, bits): + """ + Finds the twos complement of the number + :param val: input to be complemented + :param bits: size of the input + + :type val: str or int + :type bits: int + + :result: two's complement version of the input + + """ + if isinstance(val, str): + if '0x' in val: + val = int(val, 16) + else: + val = int(val, 2) + if (val & (1 << (bits - 1))) != 0: + val = val - (1 << bits) + return val + + +def bit_walker(bit_width=8, n_ones=1, invert=False, signed=True): """ Returns a list of binary values each with a width of bit_width that walks with n_ones walking from lsb to msb. If invert is True, then list @@ -78,6 +274,7 @@ def bit_walker(bit_width=8, n_ones=1, invert=False): :param bit_width: bit-width of register/value to fill. :param n_ones: number of ones to walk. :param invert: whether to walk one's or zeros + :param signed: whether to generate signed values :return: list of strings """ if n_ones < 1: @@ -86,14 +283,21 @@ def bit_walker(bit_width=8, n_ones=1, invert=False): raise Exception(f'You cant store {hex((1 << n_ones) - 1)} ' f' in {bit_width} bits') else: - walked: List[str] = [] + walked = [] temp = (1 << n_ones) - 1 for loop_var in range(bit_width): if temp <= (1 << bit_width) - 1: if not invert: - walked.append(hex(temp)) + if signed: + walked.append(twos(temp, bit_width)) + else: + walked.append(temp) elif invert: - walked.append(hex(temp ^ ((1 << bit_width) - 1))) + if signed: + walked.append( + twos(temp ^ ((1 << bit_width) - 1), bit_width)) + else: + walked.append(temp ^ ((1 << bit_width) - 1)) temp = temp << 1 else: break diff --git a/uatg/log.py b/uatg/log.py index 4898869..3eb1e49 100644 --- a/uatg/log.py +++ b/uatg/log.py @@ -41,7 +41,7 @@ def __init__(self, format=None): self.colors = { 'DEBUG': 'purple', 'INFO': 'green', - 'WARNING': 'red', + 'WARNING': 'yellow', 'ERROR': 'bold_red', 'CRITICAL': 'bold_red', } diff --git a/uatg/main.py b/uatg/main.py index 1ce3d5c..1873980 100644 --- a/uatg/main.py +++ b/uatg/main.py @@ -237,7 +237,8 @@ def from_config(config_file, verbose): module_dir = config['uatg']['module_dir'] modules = config['uatg']['modules'] - verbose = config['uatg']['verbose'] + # Uncomment to overwrite verbosity from config file. + # verbose = config['uatg']['verbose'] logger.level(verbose) module = clean_modules(module_dir, modules) diff --git a/uatg/target/dut_config.yaml b/uatg/target/dut_config.yaml deleted file mode 100644 index a5516c2..0000000 --- a/uatg/target/dut_config.yaml +++ /dev/null @@ -1,80 +0,0 @@ -ISA: RV64IMAFDCSU -iepoch_size: 2 -depoch_size: 1 -dtvec_base: 256 -s_extension: - mode: sv39 - itlb_size: 4 - dtlb_size: 4 - asid_width: 9 -pmp: - enable: true - entries: 4 - granularity: 8 -m_extension: - mul_stages: 1 - div_stages: 32 -branch_predictor: - instantiate: True - predictor: gshare - on_reset: enable - btb_depth: 64 - bht_depth: 512 - history_len: 8 - history_bits: 5 - ras_depth: 8 -icache_configuration: - instantiate: true - on_reset: enable - sets: 64 - word_size: 4 - block_size: 16 - ways: 4 - fb_size: 4 - replacement: RR - ecc_enable: false - one_hot_select: false -dcache_configuration: - instantiate: true - on_reset: enable - sets: 64 - word_size: 8 - block_size: 8 - ways: 4 - fb_size: 8 - sb_size: 2 - replacement: RR - ecc_enable: false - one_hot_select: false - rwports: 1 -reset_pc: 4096 -physical_addr_size: 32 -bus_protocol: AXI4 -fpu_trap: false -debugger_support: false -no_of_triggers: 0 -csr_configuration: - structure: daisy - counters_in_grp4: 7 - counters_in_grp5: 7 - counters_in_grp6: 7 - counters_in_grp7: 8 -bsc_compile_options: - test_memory_size: 33554432 - assertions: true - trace_dump: true - compile_target: 'sim' - suppress_warnings: ["all"] - verilog_dir: build/hw/verilog - build_dir: build/hw/intermediate - top_module: mkTbSoc - top_file: TbSoc.bsv - top_dir: test_soc - open_ocd: False -verilator_configuration: - coverage: none - trace: false - threads: 1 - verbosity: true - sim_speed: fast - out_dir: bin diff --git a/uatg/target/link.ld b/uatg/target/link.ld deleted file mode 100644 index fd99d15..0000000 --- a/uatg/target/link.ld +++ /dev/null @@ -1,17 +0,0 @@ -OUTPUT_ARCH( "riscv" ) -ENTRY(rvtest_entry_point) - -SECTIONS -{ - . = 0x80000000; - .text.init : { *(.text.init) } - . = ALIGN(0x1000); - .tohost : { *(.tohost) } - . = ALIGN(0x1000); - .text : { *(.text) } - . = ALIGN(0x1000); - .data : { *(.data) } - .data.string : { *(.data.string)} - .bss : { *(.bss) } - _end = .; -} diff --git a/uatg/target/model_test.h b/uatg/target/model_test.h deleted file mode 100644 index 7ccb03e..0000000 --- a/uatg/target/model_test.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef _COMPLIANCE_MODEL_H -#define _COMPLIANCE_MODEL_H - -#define RVMODEL_DATA_SECTION \ - .pushsection .tohost,"aw",@progbits; \ - .align 8; .global tohost; tohost: .dword 0; \ - .align 8; .global fromhost; fromhost: .dword 0; \ - .popsection; \ - .align 8; .global begin_regstate; begin_regstate: \ - .word 128; \ - .align 8; .global end_regstate; end_regstate: \ - .word 4; - -//RV_COMPLIANCE_HALT -#define RVMODEL_HALT \ -shakti_end: \ - li gp, 1; \ - sw gp, tohost, t5; \ - fence.i; \ - li t6, 0x20000; \ - la t5, begin_signature; \ - sw t5, 0(t6); \ - la t5, end_signature; \ - sw t5, 8(t6); \ - sw t5, 12(t6); - -#define RVMODEL_BOOT - -//RV_COMPLIANCE_DATA_BEGIN -#define RVMODEL_DATA_BEGIN \ - RVMODEL_DATA_SECTION \ - .align 4; .global begin_signature; begin_signature: - -//RV_COMPLIANCE_DATA_END -#define RVMODEL_DATA_END \ - .align 4; .global end_signature; end_signature: - -//RVTEST_IO_INIT -#define RVMODEL_IO_INIT -//RVTEST_IO_WRITE_STR -#define RVMODEL_IO_WRITE_STR(_R, _STR) -//RVTEST_IO_CHECK -#define RVMODEL_IO_CHECK() -//RVTEST_IO_ASSERT_GPR_EQ -#define RVMODEL_IO_ASSERT_GPR_EQ(_S, _R, _I) -//RVTEST_IO_ASSERT_SFPR_EQ -#define RVMODEL_IO_ASSERT_SFPR_EQ(_F, _R, _I) -//RVTEST_IO_ASSERT_DFPR_EQ -#define RVMODEL_IO_ASSERT_DFPR_EQ(_D, _R, _I) - -#define RVMODEL_SET_MSW_INT \ - li t1, 1; \ - li t2, 0x2000000; \ - sw t1, 0(t2); - -#define RVMODEL_CLEAR_MSW_INT \ - li t2, 0x2000000; \ - sw x0, 0(t2); - -#define RVMODEL_CLEAR_MTIMER_INT - -#define RVMODEL_CLEAR_MEXT_INT -#endif // _COMPLIANCE_MODEL_H diff --git a/uatg/test_generator.py b/uatg/test_generator.py index ab6a221..81459dc 100644 --- a/uatg/test_generator.py +++ b/uatg/test_generator.py @@ -2,7 +2,7 @@ import os import glob -from shutil import rmtree +from shutil import rmtree, copyfile from getpass import getuser from datetime import datetime import ruamel.yaml as yaml @@ -41,8 +41,10 @@ def generate_tests(work_dir, linker_dir, modules, config_dict, test_list, logger.info(f'uatg dir is {uarch_dir}') logger.info(f'work_dir is {work_dir}') isa = 'RV64I' + # yaml file containing the ISA parmaeters of the DUT + isa_yaml = config_dict['isa_dict'] try: - isa = config_dict['isa_dict']['hart0']['ISA'] + isa = isa_yaml['hart0']['ISA'] except Exception as e: logger.error(e) logger.error('Exiting UATG. ISA cannot be found/understood') @@ -58,16 +60,13 @@ def generate_tests(work_dir, linker_dir, modules, config_dict, test_list, for module in modules: module_dir = os.path.join(modules_dir, module) work_tests_dir = os.path.join(work_dir, module) - try: - module_params = config_dict['core_config'][module] - except KeyError: - # logger.critical("The {0} module is not in the dut config_file", - # format(module)) - module_params = {} - module_params['isa'] = isa + + # the yaml file containing configuration data for the DUT + core_yaml = config_dict['core_config'] + logger.debug(f'Directory for {module} is {module_dir}') logger.info(f'Starting plugin Creation for {module}') - create_plugins(plugins_path=module_dir) + create_plugins(plugins_path=module_dir, module=module) logger.info(f'Created plugins for {module}') username = getuser() time = ((str(datetime.now())).split("."))[0] @@ -99,51 +98,104 @@ def generate_tests(work_dir, linker_dir, modules, config_dict, test_list, logger.debug(f'Generating assembly tests for {module}') + # this dictionary will contain all the compile macros for each test + compile_macros_dict = {} + # Loop around and find the plugins and writes the contents from the # plugins into an asm file for plugin in manager.getAllPlugins(): - check = plugin.plugin_object.execute(module_params) + check = plugin.plugin_object.execute(core_yaml, isa_yaml) name = (str(plugin.plugin_object).split(".", 1)) - test_name = ((name[1].split(" ", 1))[0]) + t_name = ((name[1].split(" ", 1))[0]) if check: - asm_body = plugin.plugin_object.generate_asm() - # Adding License, includes and macros - asm = license_str + includes + test_entry - # Appending Coding Macros & Instructions - asm += rvcode_begin + asm_body + rvcode_end - # Appending RVTEST_DATA macros and data values - asm += rvtest_data_begin + rvtest_data(bit_width=32, - num_vals=1, - random=True, - signed=False, - align=4) - asm += rvtest_data_end - # Appending RVMODEL macros - asm += rvmodel_data_begin + rvmodel_data_end - os.mkdir(os.path.join(work_tests_dir, test_name)) - with open( - os.path.join(work_tests_dir, test_name, - test_name + '.S'), 'w') as f: - f.write(asm) - logger.debug(f'Generating test for {test_name}') + test_seq = plugin.plugin_object.generate_asm() + assert isinstance(test_seq, list) + seq = '001' + for ret_list_of_dicts in test_seq: + test_name = ((name[1].split(" ", 1))[0]) + '-' + seq + logger.debug(f'Selected test: {test_name}') + + assert isinstance(ret_list_of_dicts, dict) + # Checking for the returned sections from each test + asm_code = ret_list_of_dicts['asm_code'] + + try: + if ret_list_of_dicts['name_postfix']: + inst_name_postfix = '-' + ret_list_of_dicts[ + 'name_postfix'] + else: + inst_name_postfix = '' + except KeyError: + inst_name_postfix = '' + + # add inst name to test name as postfix + test_name = test_name + inst_name_postfix + + try: + asm_data = ret_list_of_dicts['asm_data'] + except KeyError: + asm_data = rvtest_data(bit_width=0, + num_vals=1, + random=True) + + try: + asm_sig = ret_list_of_dicts['asm_sig'] + except KeyError: + asm_sig = '\n' + + # create an entry in the compile_macros dict + if 'rv64' in isa.lower(): + compile_macros_dict[test_name] = ['XLEN=64'] + else: + compile_macros_dict[test_name] = ['XLEN=32'] + + try: + compile_macros_dict[test_name] = compile_macros_dict[ + test_name] + ret_list_of_dicts['compile_macros'] + except KeyError: + logger.debug( + f'No custom Compile macros specified for {test_name}' + ) + + # Adding License, includes and macros + asm = license_str + includes + test_entry + # Appending Coding Macros & Instructions + asm += rvcode_begin + asm_code + rvcode_end + # Appending RVTEST_DATA macros and data values + asm += rvtest_data_begin + asm_data + rvtest_data_end + # Appending RVMODEL macros + asm += rvmodel_data_begin + asm_sig + rvmodel_data_end + os.mkdir(os.path.join(work_tests_dir, test_name)) + with open( + os.path.join(work_tests_dir, test_name, + test_name + '.S'), 'w') as f: + f.write(asm) + seq = '%03d' % (int(seq, 10) + 1) + logger.debug(f'Generating test for {test_name}') else: - logger.critical(f'Skipped {test_name}') + logger.warning(f'Skipped {t_name}') logger.debug(f'Finished Generating Assembly Tests for {module}') if test_list: logger.info(f'Creating test_list for the {module}') test_list_dict.update( - generate_test_list(work_tests_dir, uarch_dir, test_list_dict)) + generate_test_list(work_tests_dir, uarch_dir, isa, + test_list_dict, compile_macros_dict)) logger.info('****** Finished Generating Tests ******') if linker_dir and os.path.isfile(os.path.join(linker_dir, 'link.ld')): - logger.debug('Using user specified linker') + logger.debug('Using user specified linker: ' + + os.path.join(linker_dir, 'link.ld')) + copyfile(os.path.join(linker_dir, 'link.ld'), work_dir + '/link.ld') else: create_linker(target_dir=work_dir) logger.debug(f'Creating a linker file at {work_dir}') if linker_dir and os.path.isfile(os.path.join(linker_dir, 'model_test.h')): - logger.debug('Using user specified model_test file') + logger.debug('Using user specified model_test file: ' + + os.path.join(linker_dir, 'model_test.h')) + copyfile(os.path.join(linker_dir, 'model_test.h'), + work_dir + '/model_test.h') else: create_model_test_h(target_dir=work_dir) logger.debug(f'Creating Model_test.h file at {work_dir}') @@ -179,8 +231,10 @@ def generate_sv(work_dir, config_dict, modules, modules_dir, alias_dict): logger.debug(f'Checking {modules_dir} for modules') modules = list_of_modules(modules_dir) isa = 'RV64I' + # yaml containing ISA parameters of DUT + isa_yaml = config_dict['isa_dict'] try: - isa = config_dict['isa_dict']['hart0']['ISA'] + isa = isa_yaml['hart0']['ISA'] except Exception as e: logger.error(e) logger.error('Exiting UATG. ISA cannot be found/understood') @@ -205,19 +259,15 @@ def generate_sv(work_dir, config_dict, modules, modules_dir, alias_dict): module_dir = os.path.join(modules_dir, module) - try: - module_params = config_dict['core_config'][module] - except KeyError: - module_params = {} - - module_params['isa'] = isa + # yaml file with core parameters + core_yaml = config_dict['core_config'] manager = PluginManager() manager.setPluginPlaces([module_dir]) manager.collectPlugins() for plugin in manager.getAllPlugins(): - _check = plugin.plugin_object.execute(module_params) + _check = plugin.plugin_object.execute(core_yaml, isa_yaml) _name = (str(plugin.plugin_object).split(".", 1)) _test_name = ((_name[1].split(" ", 1))[0]) if _check: @@ -276,14 +326,10 @@ def validate_tests(modules, config_dict, work_dir, modules_dir): work_tests_dir = os.path.join(work_dir, module) reports_dir = os.path.join(work_dir, 'reports', module) os.makedirs(reports_dir, exist_ok=True) - - try: - module_params = config_dict['core_config'][module] - except KeyError: - # logger.critical("The {0} module is not " - # "in the dut config_file",format(module)) - module_params = {} - + # YAML with ISA paramters + core_yaml = config_dict['core_config'] + # isa yaml with ISA paramters + isa_yaml = config_dict['isa_dict'] manager = PluginManager() manager.setPluginPlaces([module_dir]) manager.collectPlugins() @@ -293,7 +339,7 @@ def validate_tests(modules, config_dict, work_dir, modules_dir): for plugin in manager.getAllPlugins(): _name = (str(plugin.plugin_object).split(".", 1)) _test_name = ((_name[1].split(" ", 1))[0]) - _check = plugin.plugin_object.execute(module_params) + _check = plugin.plugin_object.execute(core_yaml, isa_yaml) _log_file_path = os.path.join(work_tests_dir, _test_name, 'log') if _check: try: diff --git a/uatg/utils.py b/uatg/utils.py index 2955bd1..f4abaec 100644 --- a/uatg/utils.py +++ b/uatg/utils.py @@ -2,6 +2,7 @@ # imports import os +import re import glob import random as rnd from uatg.log import logger @@ -394,7 +395,7 @@ def create_model_test_h(target_dir): outfile.write(out) -def create_plugins(plugins_path): +def create_plugins(plugins_path, module): """ This function is used to create Yapsy Plugin files. The YAPSY plugins are required to be in a certain pattern. This function @@ -402,14 +403,41 @@ def create_plugins(plugins_path): Yapsy will ignore all other python file which does not have a .yapsy-plugin file associated with it. """ + # the index yaml is in the modules directory + index_yaml = load_yaml(os.path.join(plugins_path, '../index.yaml')) files = os.listdir(plugins_path) + for file in files: if ('.py' in file) and (not file.startswith('.')): - module_name = file[0:-3] - f = open(os.path.join(plugins_path, module_name + '.yapsy-plugin'), - 'w') - f.write("[Core]\nName=" + module_name + "\nModule=" + module_name) - f.close() + test_name = file[0:-3] + + val = False + + try: + val = index_yaml[module][test_name] + except KeyError as e: + logger.critical(f'There is no entry for test - {test_name}') + exit(f'update the index.yaml with your new test') + + if val == True: + f = open( + os.path.join(plugins_path, test_name + '.yapsy-plugin'), + 'w') + f.write("[Core]\nName=" + test_name + "\nModule=" + test_name) + f.close() + logger.debug(f'Created plugin for {test_name}') + else: + try: + os.remove( + os.path.join(plugins_path, test_name + '.yapsy-plugin')) + logger.warn( + f'removing already existing plugin file for {test_name}' + ) + except: + logger.debug(f'no plugin for {test_name} to remove') + pass + logger.warn( + f'Skippping test {test_name} as index yaml has False') def create_config_file(config_path): @@ -571,7 +599,8 @@ def create_dut_config_files(dut_config_path): f.write(csr_grouping64) -def rvtest_data(bit_width=0, num_vals=20, random=True, signed=False, align=4): +def rvtest_data(bit_width=0, num_vals=20, random=True, signed=False, align=4)\ + -> str: """ Used to specify the data to be loaded into the test_data section of the @@ -602,7 +631,7 @@ def rvtest_data(bit_width=0, num_vals=20, random=True, signed=False, align=4): # f'.{size[bit_width]} {hex(min_unsigned)}\n' # data += f'MAX_S:\t.{size[bit_width]} {hex(max_signed)}\nMIN_S:\t' \ # f'.{size[bit_width]} {hex(min_signed)}\n' - data += 'RAND_VAL:\n' + data += 'DATA_SEC:\n' if random: for i in range(num_vals): if signed: @@ -647,7 +676,38 @@ def clean_modules(module_dir, modules): return module -def generate_test_list(asm_dir, uarch_dir, test_list): +def find_instances(string, character): + """ + The function returns all the indices of the + character present in the string. + A list is returned + """ + return [index for index, letter in enumerate(string) if letter == character] + + +def split_isa_string(isa_string): + """ + The function parses the ISA string, + removes the 'S,U,H and N' characters from it. + The updates ISA string is returned. + """ + + str_match = re.findall(r'([^\d]*?)(?!_)*(Z.*?)*(_|$)', isa_string, re.M) + extension_list = [] + standard_isa = '' + for match in str_match: + stdisa, z, ignore = match + if stdisa != '': + for e in stdisa: + extension_list.append(e) + standard_isa = stdisa + if z != '': + extension_list.append(z) + + return extension_list + + +def generate_test_list(asm_dir, uarch_dir, isa, test_list, compile_macros_dict): """ updates the test_list.yaml file with the location of the tests generated by test_generator as well the directory to dump the logs. @@ -659,14 +719,31 @@ def generate_test_list(asm_dir, uarch_dir, test_list): env_dir = os.path.join(uarch_dir, 'env/') target_dir = asm_dir + '/../' + extension_list = split_isa_string(isa) + march = '' + if 'rv32' in isa.lower(): + march += 'rv32i' + elif 'rv64' in isa.lower(): + march += 'rv64i' + if 'M' in extension_list: + march += 'm' + if 'A' in extension_list: + march += 'a' + if 'F' in extension_list: + march += 'f' + if 'D' in extension_list: + march += 'd' + if 'C' in extension_list: + march += 'c' + for test in asm_test_list: logger.debug(f"Current test is {test}") base_key = os.path.basename(test)[:-2] test_list[base_key] = {} test_list[base_key]['generator'] = 'uatg' test_list[base_key]['work_dir'] = asm_dir + '/' + base_key - test_list[base_key]['isa'] = 'rv64imafdc' - test_list[base_key]['march'] = 'rv64imafdc' + test_list[base_key]['isa'] = isa + test_list[base_key]['march'] = march test_list[base_key]['mabi'] = 'lp64' test_list[base_key]['cc'] = 'riscv64-unknown-elf-gcc' test_list[base_key][ @@ -678,7 +755,7 @@ def generate_test_list(asm_dir, uarch_dir, test_list): test_list[base_key]['asm_file'] = os.path.join(asm_dir, base_key, base_key + '.S') test_list[base_key]['include'] = [env_dir, target_dir] - test_list[base_key]['compile_macros'] = ['XLEN=64'] + test_list[base_key]['compile_macros'] = compile_macros_dict[base_key] test_list[base_key]['extra_compile'] = [] test_list[base_key]['result'] = 'Unavailable' return test_list