diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..93572931 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,28 @@ +name: Unit tests + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Install PyYAML + run: | + pip3 install -r requirements.txt + pip3 install coverage + - name: Test error outputs + run: coverage run -m unittest -b + - name: Generate coverage + run: coverage xml + - name: Upload coverage + uses: codecov/codecov-action@v2 + diff --git a/parse.py b/parse.py index 83c1d72c..243a8bbd 100755 --- a/parse.py +++ b/parse.py @@ -13,6 +13,9 @@ pp = pprint.PrettyPrinter(indent=2) logging.basicConfig(level=logging.INFO, format='%(levelname)s:: %(message)s') +def parse_constant(string): + return int(string, 0) + def process_enc_line(line, ext): ''' This function processes each line of the encoding files (rv*). As part of @@ -61,14 +64,23 @@ def process_enc_line(line, ext): temp_instr = ['-'] * 32 entries = [ x[0] for x in re.findall( - r'((\d)+\.\.(\d)+\=((0b\d+)|(0x\d+)|(\d)+))*', + r'((\d)+\.\.(\d)+\=((0b\d+)|(0x[0-9a-fA-F]+)|(\d)+))*', remaining) if x[0] != '' ] for temp_entry in entries: entry = temp_entry.split('=')[0] - f1, f2 = entry.split('..') - for ind in range(int(f1), int(f2)): + s2, s1 = entry.split('..') + msb = int(s2) + lsb = int(s1) + + # check msb < lsb + if msb < lsb: + logging.error( + f'{line.split(" ")[0]:<10} has position {msb} less than position {lsb} in it\'s encoding' + ) + raise SystemExit(1) + for ind in range(lsb, msb): # overlapping bits if temp_instr[ind] == 'X': logging.error( @@ -77,20 +89,11 @@ def process_enc_line(line, ext): raise SystemExit(1) temp_instr[ind] = 'X' - # check x < y - if int(f1) < int(f2): - logging.error( - f'{line.split(" ")[0]:<10} has position {f1} less than position {f2} in it\'s encoding' - ) - raise SystemExit(1) - # illegal value assigned as per bit width - entry_value = temp_entry.split('=')[1] - temp_base = 16 if 'x' in entry_value else 2 if 'b' in entry_value else 10 - if len(str(int(entry_value, - temp_base))[2:]) > (int(f1) - int(f2)): + entry_value = parse_constant(temp_entry.split('=')[1]) + if entry_value >= (1 << (msb - lsb + 1)): logging.error( - f'{line.split(" ")[0]:<10} has an illegal value {entry_value} assigned as per the bit width {f1 - f2}' + f'{line.split(" ")[0]:<10} has an illegal value {entry_value} assigned as per the bit width {msb - lsb}' ) raise SystemExit(1) diff --git a/test.py b/test.py new file mode 100644 index 00000000..89c014d4 --- /dev/null +++ b/test.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 + +from parse import * +import logging +import unittest + +class ConstantParseTest(unittest.TestCase): + def test_constant(self): + self.assertEqual(parse_constant('10'), 10) + self.assertEqual(parse_constant('0xa'), 10) + self.assertEqual(parse_constant('0xA'), 10) + self.assertEqual(parse_constant('0b1010'), 10) + +class EncodingLineTest(unittest.TestCase): + def setUp(self): + logger = logging.getLogger() + logger.disabled = True + + def assertError(self, string): + self.assertRaises(SystemExit, process_enc_line, string, 'rv_i') + + def test_lui(self): + name, data = process_enc_line('lui rd imm20 6..2=0x0D 1..0=3', 'rv_i') + self.assertEqual(name, 'lui') + self.assertEqual(data['extension'], ['rv_i']) + self.assertEqual(data['match'], '0x37') + self.assertEqual(data['mask'], '0x7f') + + def test_overlapping(self): + self.assertError('jol rd jimm20 6..2=0x00 3..0=7') + + def test_invalid_order(self): + self.assertError('jol 2..6=0x1b') + + def test_illegal_value(self): + self.assertError('jol rd jimm20 2..0=10') + self.assertError('jol rd jimm20 2..0=0xB') + + @unittest.skip('not implemented') + def test_overlapping_field(self): + self.assertError('jol rd rs1 jimm20 6..2=0x1b 1..0=3') + + def test_illegal_field(self): + self.assertError('jol rd jimm128 2..0=10')