def test_example(self): example_data = '\n'.join(['b inc 5 if a > 1', 'a inc 1 if b < 5', 'c dec -10 if a >= 1', 'c inc -20 if c == 10', '']) try: with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: f.write(example_data) registers = Registers() registers.parse_file(f.name) finally: os.remove(f.name) self.assertEqual(registers.registers, {'a': 1, 'c': -10}) self.assertEqual(registers.get_largest_value(), 1) self.assertEqual(registers.get_largest_value_ever_held(), 10)
class Parser: ''' This class stepwise calls all the functions necessary to parse modules and retrieve the information necessary to extend gnu binutils and gem5. ''' def __init__(self, tcpath, modelpath): self._compiler = Compiler(None, None, tcpath) self._gem5 = Gem5([], None) self._exts = None self._models = [] self._regs = Registers() self._modelpath = modelpath self._tcpath = tcpath def restore(self): ''' Restore the toolchain to its defaults. ''' logger.info('Remove custom instructions from GNU binutils files') self._compiler.restore() self._gem5.restore() def parse_models(self): ''' Parse the c++ reference implementation of the custom instruction. ''' logger.info('Determine if modelpath is a folder or a single file') if os.path.isdir(self._modelpath): # restore the toolchain to its defaults self.restore() logger.info('Traverse over directory') self.treewalk(self._modelpath) else: logger.info('Single file, start parsing') model = Model(self._modelpath) self._models.append(model) # add model for read function self._models.append(Model(read=True)) # add model for write function self._models.append(Model(write=True)) self._exts = Extensions(self._models) self._compiler = Compiler(self._exts, self._regs, self._tcpath) self._gem5 = Gem5(self._exts, self._regs) def treewalk(self, top): logger.info('Search for models in {}'.format(top)) logger.debug('Directory content: {}'.format(os.listdir(top))) for file in os.listdir(top): pathname = os.path.join(top, file) mode = os.stat(pathname)[ST_MODE] if S_ISDIR(mode): # directory self.treewalk(pathname) elif S_ISREG(mode): # file if pathname.endswith('.cc'): logger.info('Model definition in file {}'.format(pathname)) model = Model(pathname) self._models.append(model) # registers if pathname.endswith('registers.hh'): logger.info('Custom registers in file {}'.format(pathname)) self._regs.parse_file(pathname) else: # unknown file type logger.info('Unknown file type, skip') def extend_compiler(self): ''' Extend the riscv compiler. ''' self._compiler.extend_compiler() def extend_gem5(self): ''' Extend the gem5 simulator. ''' self._gem5.extend_gem5() @property def args(self): return self._args @property def compiler(self): return self._compiler @property def decoder(self): return self._gem5 @property def extensions(self): return self._exts @property def models(self): return self._models @property def regs(self): return self._regs
class RegisterTest(unittest.TestCase): def setUp(self): self.registers = Registers() def test_init_creates_registers(self): self.assertNotIn('registers', Registers.__dict__) registers = Registers() self.assertIsInstance(registers.registers, dict) def test_parse_instruction(self): code, comparison = self.registers.parse_instruction('b inc 5 if a > 1') self.assertEqual(code, ('b', 'inc', 5)) self.assertEqual(comparison, ('a', '>', 1)) def test_is_true(self): self.registers.registers = {'a': 10} self.assertTrue(self.registers.is_true(('a', '>', 1))) self.assertFalse(self.registers.is_true(('a', '>', 10))) self.assertTrue(self.registers.is_true(('foo', '>', -1))) self.assertFalse(self.registers.is_true(('bar', '>', 0))) self.assertTrue(self.registers.is_true(('a', '<', 11))) self.assertFalse(self.registers.is_true(('a', '<', 9))) self.assertTrue(self.registers.is_true(('a', '==', 10))) self.assertFalse(self.registers.is_true(('a', '==', 1))) self.assertTrue(self.registers.is_true(('a', '!=', 1))) self.assertFalse(self.registers.is_true(('a', '!=', 10))) self.assertTrue(self.registers.is_true(('a', '>=', 9))) self.assertTrue(self.registers.is_true(('a', '>=', 10))) self.assertFalse(self.registers.is_true(('a', '>=', 11))) self.assertTrue(self.registers.is_true(('a', '<=', 11))) self.assertTrue(self.registers.is_true(('a', '<=', 10))) self.assertFalse(self.registers.is_true(('a', '<=', 9))) with self.assertRaises(RuntimeError): self.registers.is_true(('a', '===', 1)) def test_exececute_instruction(self): self.registers.registers = {} self.registers.execute_instruction(('a', 'inc', 1)) self.assertEqual(self.registers.registers['a'], 1) self.registers.execute_instruction(('a', 'inc', 2)) self.assertEqual(self.registers.registers['a'], 3) self.registers.execute_instruction(('b', 'dec', 1)) self.assertEqual(self.registers.registers['b'], -1) self.registers.execute_instruction(('b', 'dec', 2)) self.assertEqual(self.registers.registers['b'], -3) self.registers.execute_instruction(('b', 'inc', 10)) self.assertEqual(self.registers.registers['b'], 7) with self.assertRaises(RuntimeError): self.registers.execute_instruction(('a', 'mul', 2)) def test_exececute_instruction_stores_highest_value_ever(self): self.registers.registers = {} self.registers.execute_instruction(('a', 'inc', 10)) self.assertEqual(self.registers.highest_value_ever, 10) self.registers.execute_instruction(('a', 'dec', 10)) self.assertEqual(self.registers.highest_value_ever, 10) @patch.object(Registers, 'process_and_execute_instruction') def test_parse_file(self, mock_process): m = mock_open(read_data='foo\nbar\n') with patch('registers.open', m): self.registers.parse_file(sentinel.filename) m.assert_called_once_with(sentinel.filename) mock_process.assert_has_calls([call('foo\n'), call('bar\n')]) def test_get_largest_value(self): self.registers.registers = {'a': 1, 'b': -10, 'c': 5} self.assertEqual(self.registers.get_largest_value(), 5)