| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| # | |
| # Copyright (C) 2017-2020 The Project X-Ray Authors. | |
| # | |
| # Use of this source code is governed by a ISC-style | |
| # license that can be found in the LICENSE file or at | |
| # https://opensource.org/licenses/ISC | |
| # | |
| # SPDX-License-Identifier: ISC | |
| # TODO: need better coverage for different tile types | |
| from io import StringIO | |
| import os | |
| import os.path | |
| import re | |
| import unittest | |
| import tempfile | |
| import prjxray | |
| import utils.fasm2frames as fasm2frames | |
| from prjxray.util import OpenSafeFile | |
| from textx.exceptions import TextXSyntaxError | |
| def frm2bits(txt): | |
| ''' | |
| Convert output .frm file text to set of (frame addr, word #, bit index) tuples | |
| ''' | |
| bits_out = set() | |
| for l in txt.split('\n'): | |
| l = l.strip() | |
| if not l: | |
| continue | |
| # 0x00020500 0x00000000,0x00000000,0x00000000,... | |
| addr, words = l.split(' ') | |
| addr = int(addr, 0) | |
| words = words.split(',') | |
| assert (101 == len(words)) | |
| for wordi, word in enumerate(words): | |
| word = int(word, 0) | |
| for biti in range(32): | |
| val = word & (1 << biti) | |
| if val: | |
| bits_out.add((addr, wordi, biti)) | |
| return bits_out | |
| def bitread2bits(txt): | |
| ''' | |
| Convert .bits text file (ie bitread output) to set of (frame addr, word #, bit index) tuples | |
| ''' | |
| bits_ref = set() | |
| for l in txt.split('\n'): | |
| l = l.strip() | |
| if not l: | |
| continue | |
| # bit_0002050b_004_14 | |
| m = re.match(r'bit_(.{8})_(.{3})_(.{2})', l) | |
| addr = int(m.group(1), 16) | |
| word = int(m.group(2), 10) | |
| bit = int(m.group(3), 10) | |
| bits_ref.add((addr, word, bit)) | |
| return bits_ref | |
| class TestStringMethods(unittest.TestCase): | |
| def filename_test_data(self, fname): | |
| return os.path.join(os.path.dirname(__file__), 'test_data', fname) | |
| def get_test_data(self, fname): | |
| with OpenSafeFile(self.filename_test_data(fname)) as f: | |
| return f.read() | |
| def fasm2frames(self, fin_data, **kw): | |
| with tempfile.NamedTemporaryFile(suffix='.fasm') as fin: | |
| fin.write(fin_data.encode('utf-8')) | |
| fin.flush() | |
| fout = StringIO() | |
| fasm2frames.run( | |
| self.filename_test_data('db'), "xc7a200tffg1156-1", fin.name, | |
| fout, **kw) | |
| return fout.getvalue() | |
| def test_lut(self): | |
| '''Simple smoke test on just the LUTs''' | |
| self.fasm2frames(self.get_test_data('lut.fasm')) | |
| def bitread_frm_equals(self, frm_fn, bitread_fn): | |
| fout = self.fasm2frames(self.get_test_data(frm_fn)) | |
| # Build a list of output used bits | |
| bits_out = frm2bits(fout) | |
| # Build a list of reference used bits | |
| bits_ref = bitread2bits(self.get_test_data(bitread_fn)) | |
| # Now check for equivilence vs reference design | |
| self.assertEqual(len(bits_ref), len(bits_out)) | |
| self.assertEqual(bits_ref, bits_out) | |
| def test_lut_int(self): | |
| self.bitread_frm_equals('lut_int.fasm', 'lut_int/design.bits') | |
| def test_ff_int(self): | |
| self.bitread_frm_equals('ff_int.fasm', 'ff_int/design.bits') | |
| @unittest.skip | |
| def test_ff_int_op1(self): | |
| '''Omitted key set to ''' | |
| self.bitread_frm_equals('ff_int_op1.fasm', 'ff_int/design.bits') | |
| # Same check as above, but isolated test case | |
| def test_opkey_01_default(self): | |
| '''Optional key with binary omitted value should produce valid result''' | |
| self.fasm2frames("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX") | |
| @unittest.skip | |
| def test_opkey_01_1(self): | |
| self.fasm2frames("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1") | |
| @unittest.skip | |
| def test_opkey_enum(self): | |
| '''Optional key with enumerated value should produce syntax error''' | |
| try: | |
| # CLBLM_L.SLICEM_X0.AMUXFF.O6 !30_06 !30_07 !30_08 30_11 | |
| self.fasm2frames("CLBLM_L_X10Y102.SLICEM_X0.AFFMUX.O6") | |
| self.fail("Expected syntax error") | |
| except TextXSyntaxError: | |
| pass | |
| def test_ff_int_0s(self): | |
| '''Explicit 0 entries''' | |
| self.bitread_frm_equals('ff_int_0s.fasm', 'ff_int/design.bits') | |
| def test_badkey(self): | |
| '''Bad key should throw syntax error''' | |
| try: | |
| self.fasm2frames("CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 2") | |
| self.fail("Expected syntax error") | |
| except TextXSyntaxError: | |
| pass | |
| @unittest.skip | |
| def test_dupkey(self): | |
| '''Duplicate key should throw syntax error''' | |
| try: | |
| self.fasm2frames( | |
| """\ | |
| CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 0 | |
| CLBLM_L_X10Y102.SLICEM_X0.SRUSEDMUX 1 | |
| """) | |
| self.fail("Expected syntax error") | |
| except TextXSyntaxError: | |
| pass | |
| @unittest.skip | |
| def test_sparse(self): | |
| '''Verify sparse equivalent to normal encoding''' | |
| frm_fn = 'lut_int.fasm' | |
| fout_sparse_txt = self.fasm2frames( | |
| self.get_test_data(frm_fn), sparse=True) | |
| bits_sparse = frm2bits(fout_sparse_txt) | |
| fout_full_txt = self.fasm2frames( | |
| self.get_test_data(frm_fn), sparse=False) | |
| bits_full = frm2bits(fout_full_txt) | |
| # Now check for equivilence vs reference design | |
| self.assertEquals(len(bits_sparse), len(bits_full)) | |
| self.assertEquals(bits_sparse, bits_full) | |
| # Verify the full ROI is way bigger description | |
| # It will still be decent size though since even sparse occupies all columns in that area | |
| self.assertGreaterEqual(len(fout_full_txt), len(fout_sparse_txt) * 4) | |
| def test_stepdown_1(self): | |
| self.bitread_frm_equals( | |
| 'iob/liob_stepdown.fasm', 'iob/liob_stepdown.bits') | |
| def test_stepdown_2(self): | |
| self.bitread_frm_equals( | |
| 'iob/riob_stepdown.fasm', 'iob/riob_stepdown.bits') | |
| if __name__ == '__main__': | |
| unittest.main() |