Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

Commit

Permalink
Merge pull request #49 from edwin7026/master
Browse files Browse the repository at this point in the history
Support for Instruction Aliases
  • Loading branch information
pawks authored Aug 29, 2022
2 parents b3f81ef + 0727704 commit ba72cb7
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 31 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.15.0] - 2022-08-25
- Added support for instruction aliases

## [0.14.0] - 2022-08-08
- Add fields to instruction object
- Enable generic coverage evaluation mechanisms for floating point instructions
Expand Down
5 changes: 4 additions & 1 deletion docs/source/cgf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -376,13 +376,16 @@ A covergroup contains the following nodes:
This string is divided into three parts - opcode list, assign list and condition list separated by :: symbol. It is parsed and all the three lists are obtained separately. The variables available for use in the expression are as follows:

* ``instr_name`` : The instruction names in the opcode list

* ``instruction_alias``: The instruction alias for a set of instructions as defined in ``/riscv_isac/data/instr_alias.yaml``

* ``rs1`` : The register number of source register 1 of the current instruction in the assign list.

* ``rs2`` : The register number of source register 2 of the current instruction in the assign list.

* ``rd`` : The register number of destination register of the current instruction in the assign list.

Instruction aliases when used will be expanded into a tuple of instruction under the given alias.
Along with the above mentioned variable any valid python comparison operators can be used in the condition list.


Expand All @@ -401,7 +404,7 @@ A covergroup contains the following nodes:

.. code-block:: python
[(add,sub) : ? : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ]
[(add,sub) : rv32i_arith : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ]
3. WAW for an add instruction followed by a subtract instruction with 3 non-consuming instructions in between.

Expand Down
2 changes: 1 addition & 1 deletion riscv_isac/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

__author__ = """InCore Semiconductors Pvt Ltd"""
__email__ = '[email protected]'
__version__ = '0.14.0'
__version__ = '0.15.0'
21 changes: 21 additions & 0 deletions riscv_isac/cgf_normalize.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# See LICENSE.incore for details
from math import *
import pprint
import riscv_isac.utils as utils
import itertools
import random
Expand Down Expand Up @@ -579,6 +580,26 @@ def expand_cgf(cgf_files, xlen,flen):
if len(cgf[labels]['mnemonics'].keys()) > 1:
logger.error(f'Multiple instruction mnemonics found when base_op label defined in {labels} label.')

# Substitute instruction aliases with equivalent tuple of instructions
if 'cross_comb' in cats:
temp = cats['cross_comb']

for covp, covge in dict(temp).items():
data = covp.split('::')
ops = data[0].replace(' ', '')[1:-1].split(':')
# Substitute with tuple of instructions
for i in range(len(ops)):
exp_alias = utils.import_instr_alias(ops[i])
if exp_alias != None:
ops[i] = tuple(exp_alias).__str__().replace("'", '').replace(" ", '')

data[0] = '[' + ':'.join(ops) + ']'
data = '::'.join(data)
del temp[covp]
temp[data] = covge

cgf[labels].insert(1, 'cross_comb', temp)

l = len(cats.items())
i = 0
for label,node in cats.items():
Expand Down
62 changes: 35 additions & 27 deletions riscv_isac/coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@

class cross():

BASE_REG_DICT = { 'x'+str(i) : 'x'+str(i) for i in range(32)}

def __init__(self,label,coverpoint):

self.label = label
Expand All @@ -58,24 +60,24 @@ def __init__(self,label,coverpoint):

## Extract relevant information from coverpt
self.data = self.coverpoint.split('::')
self.ops = [i for i in self.data[0][1:-1].split(':')]
self.assign_lst = [i for i in self.data[1][1:-1].split(':')]
self.cond_lst = [i for i in self.data[2][1:-1].split(':')]
self.ops = self.data[0].replace(' ', '')[1:-1].split(':')
self.assign_lst = self.data[1].replace(' ', '')[1:-1].split(':')
self.cond_lst = self.data[2].lstrip().rstrip()[1:-1].split(':')

def process(self, queue, window_size, addr_pairs):

'''
Check whether the coverpoint is a hit or not and update the metric
'''
if(len(self.ops)>window_size or len(self.ops)>len(queue)):
return

for index in range(len(self.ops)):

instr = queue[index]
instr_name = instr.instr_name
if addr_pairs:
if not (any([instr.instr_addr >= saddr and instr.instr_addr < eaddr for saddr,eaddr in addr_pairs])):
continue
break

rd = None
rs1 = None
Expand All @@ -92,13 +94,13 @@ def process(self, queue, window_size, addr_pairs):
rm = None

if instr.rd is not None:
rd = int(instr.rd[0])
rd = instr.rd[1] + str(instr.rd[0])
if instr.rs1 is not None:
rs1 = int(instr.rs1[0])
rs1 = instr.rs1[1] + str(instr.rs1[0])
if instr.rs2 is not None:
rs2 = int(instr.rs2[0])
rs2 = instr.rs2[1] + str(instr.rs2[0])
if instr.rs3 is not None:
rs3 = int(instr.rs3[0])
rs3 = instr.rs3[1] + str(instr.rs3[0])
if instr.imm is not None:
imm = int(instr.imm)
if instr.zimm is not None:
Expand All @@ -118,19 +120,24 @@ def process(self, queue, window_size, addr_pairs):
if instr.rm is not None:
rm = int(instr.rm)


if(self.ops[index] != '?'):
check_lst = [i for i in self.ops[index][1:-1].split(',')]
if self.ops[index].find('?') == -1:
# Handle instruction tuple
if self.ops[index].find('(') != -1:
check_lst = self.ops[index].replace('(', '').replace(')', '').split(',')
else:
check_lst = [self.ops[index]]
if (instr_name not in check_lst):
break
if (self.cond_lst[index] != '?'):
if(eval(self.cond_lst[index])):

if self.cond_lst[index].find('?') == -1:
if(eval(self.cond_lst[index], locals(), cross.BASE_REG_DICT)):
if(index==len(self.ops)-1):
self.result = self.result + 1
else:
break
if(self.assign_lst[index] != '?'):
exec(self.assign_lst[index])

if self.assign_lst[index].find('?') == -1:
exec(self.assign_lst[index], locals(), cross.BASE_REG_DICT)

def get_metric(self):
return self.result
Expand Down Expand Up @@ -610,14 +617,14 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr
commitvalue = instr.reg_commit

# assign default values to operands
nxf_rs1 = None
nxf_rs2 = None
nxf_rs3 = None
nxf_rd = None
rs1_type = None
rs2_type = None
rs3_type = None
rd_type = None
nxf_rs1 = 0
nxf_rs2 = 0
nxf_rs3 = 0
nxf_rd = 0
rs1_type = 'x'
rs2_type = 'x'
rs3_type = 'x'
rd_type = 'x'

csr_addr = None

Expand Down Expand Up @@ -956,7 +963,6 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr
store_val)
logger.error(_log)
stats.stat4.append(_log + '\n\n')

stats.covpt = []
stats.code_seq = []
stats.ucode_seq = []
Expand Down Expand Up @@ -1011,7 +1017,7 @@ def compute(trace_file, test_name, cgf, parser_name, decoder_name, detailed, xle
cgf = temp

# If cgf does not have the covergroup pertaining to the cover-label, throw error
# and exit
# and exit
if not cgf:
logger.err('Covergroup(s) for ' + str(cov_labels) + ' not found')
sys.exit(1)
Expand Down Expand Up @@ -1067,7 +1073,6 @@ def compute(trace_file, test_name, cgf, parser_name, decoder_name, detailed, xle

iterator = iter(parser.__iter__()[0])


# If number of processes to be spawned is more than that available,
# allot number of processes to be equal to one less than maximum
available_cores = mp.cpu_count()
Expand Down Expand Up @@ -1104,6 +1109,7 @@ def compute(trace_file, test_name, cgf, parser_name, decoder_name, detailed, xle
)
)
)

#Start each processes
for each in process_list:
each.start()
Expand All @@ -1126,6 +1132,8 @@ def compute(trace_file, test_name, cgf, parser_name, decoder_name, detailed, xle
obj_dict[(label,coverpt)].process(cross_cover_queue, window_size,addr_pairs)
cross_cover_queue.pop(0)



# Close all instruction queues
for each in queue_list:
each.close()
Expand Down
93 changes: 93 additions & 0 deletions riscv_isac/data/instr_alias.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# This file holds aliases for groups of instructions

rv32i_arith_reg: &rv32i_arith_reg
- add
- sub
- slt
- sltu
- xor
- or
- and

rv32i_arith_imm: &rv32i_arith_imm
- addi
- slti
- sltiu
- xori
- ori
- andi

rv32i_shift_reg: &rv32i_shift_reg
- sll
- srl
- sra

rv32i_shift_imm: &rv32i_shift_imm
- slli
- srli
- srai

rv32i_arith: &rv32i_arith [*rv32i_arith_reg, *rv32i_arith_imm]

rv32i_shift: &rv32i_shift [*rv32i_shift_reg, *rv32i_shift_imm]

rv64i_arith_reg: &rv64i_arith_reg
- *rv32i_arith_reg
- addw
- subw

rv64i_arith_imm: &rv64i_arith_imm
- *rv32i_arith_imm
- addiw

rv64i_shift_reg: &rv64i_shift_reg
- *rv32i_shift_reg
- sllw
- srlw
- sraw

rv64i_shift_imm: &rv64i_shift_imm
- *rv32i_shift_imm
- slliw
- srliw

rv64i_arith: &rv64i_arith [*rv64i_arith_reg, *rv64i_arith_imm]

rv64i_shift: &rv64i_shift [*rv64i_shift_reg, *rv64i_shift_imm]

rv32i_branch: &rv32i_branch
- beq
- bge
- bgeu
- blt
- bltu
- bne

rv64i_branch: &rv64i_branch [*rv32i_branch]

rv32i_jal: &rv32i_jal
- jal
- jalr

rv64i_jal: &rv64i_jal [*rv32i_jal]

rv32i_load: &rv32i_load
- lw
- lhu
- lh
- lbu
- lb

rv64i_load: &rv364i_load
- *rv32i_load
- ld
- lwu

rv32i_store: &rv32i_store
- sw
- sh
- sb

rv64i_store: &rv64i_store
- *rv32i_store
- sd
26 changes: 26 additions & 0 deletions riscv_isac/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os
import subprocess
import shlex
import riscv_isac
from riscv_isac.log import logger
import ruamel
from ruamel.yaml import YAML
Expand Down Expand Up @@ -388,3 +389,28 @@ def sys_command_file(command, filename):
stdout, stderr = out.communicate()
fp.close()

def import_instr_alias(alias):
'''
Return instructions pertaining to a particular alias
alias: (string) The alias to be imported
'''

# Function to flatten nested lists
from collections import Iterable
def flatten(lis):
for item in lis:
if isinstance(item, Iterable) and not isinstance(item, str):
for x in flatten(item):
yield x
else:
yield item

isac_path = os.path.dirname(riscv_isac.__file__)
alias_dict = load_yaml_file(isac_path + '/data/instr_alias.yaml')
if alias in alias_dict:
return list(flatten(alias_dict[alias]))
else:
return None

2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.14.0
current_version = 0.15.0
commit = True
tag = True

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def read_requires():

setup(
name='riscv_isac',
version='0.14.0',
version='0.15.0',
description="RISC-V ISAC",
long_description=readme + '\n\n',
classifiers=[
Expand Down

0 comments on commit ba72cb7

Please sign in to comment.