Skip to content

Commit

Permalink
Refactored address decoder code
Browse files Browse the repository at this point in the history
  • Loading branch information
jvanstraten committed Aug 20, 2019
1 parent 2c1b153 commit fab19da
Show file tree
Hide file tree
Showing 3 changed files with 255 additions and 213 deletions.
273 changes: 156 additions & 117 deletions tests/test_vhdl_decoder.py
Original file line number Diff line number Diff line change
@@ -1,200 +1,239 @@
"""Unit tests for the VHDL address decoder generator."""

from unittest import TestCase
from vhdmmio.vhdl.decoder import decoder_template, Decoder
from vhdmmio.vhdl.address_decoder import AddressDecoder
from vhdmmio.core.address import MaskedAddress
from vhdmmio.template import TemplateEngine

class TestVhdlDecoder(TestCase):
"""Unit tests for the VHDL address decoder generator."""

maxDiff = None

def _test_decoder(self, addresses, match=None,
optimize=False, allow_overlap=False, allow_duplicate=False):
dec = AddressDecoder('address', 32, optimize, allow_overlap, allow_duplicate)
for address in addresses:
dec[MaskedAddress.parse_config(address)] = str(address)
result = str(dec)
if match is not None:
self.assertEqual(result, '\n'.join(match))
return dec

def test_empty(self):
"""tests constructing an empty address decoder"""
self.assertEqual(decoder_template(32, []), '')
self._test_decoder([], [''])

def test_if(self):
"""tests address decoder if statement construction"""
self.assertEqual(decoder_template(32, [(8, 3)]), '\n'.join([
'if $address$(31 downto 2) = "000000000000000000000000000010" then',
' -- $address$ = 000000000000000000000000000010--',
'$ ADDR_0x8',
self._test_decoder(['8|3'], [
'if address(31 downto 2) = "000000000000000000000000000010" then',
' -- address = 000000000000000000000000000010--',
'',
' 8|3',
'',
'end if;',
]))
])

self.assertEqual(decoder_template(32, [(8, 3)], optimize=True), '\n'.join([
'-- $address$ = 000000000000000000000000000010--',
'$ADDR_0x8',
]))
self._test_decoder(['8|3'], optimize=True, match=[
'-- address = 000000000000000000000000000010--',
'',
'8|3',
])

def test_if_else(self):
"""tests address decoder if-else statement construction"""
self.assertEqual(decoder_template(32, [(4, 3), (0, 3)]), '\n'.join([
'if $address$(31 downto 3) = "00000000000000000000000000000" then',
' if $address$(2) = \'0\' then',
' -- $address$ = 000000000000000000000000000000--',
'$ ADDR_0x0',
self._test_decoder(['4|3', '0|3'], match=[
'if address(31 downto 3) = "00000000000000000000000000000" then',
' if address(2) = \'0\' then',
' -- address = 000000000000000000000000000000--',
'',
' 0|3',
'',
' else',
' -- $address$ = 000000000000000000000000000001--',
'$ ADDR_0x4',
' -- address = 000000000000000000000000000001--',
'',
' 4|3',
'',
' end if;',
'end if;',
]))
])

self.assertEqual(decoder_template(32, [(4, 3), (0, 3)], optimize=True), '\n'.join([
'if $address$(2) = \'0\' then',
' -- $address$ = 000000000000000000000000000000--',
'$ ADDR_0x0',
self._test_decoder(['4|3', '0|3'], optimize=True, match=[
'if address(2) = \'0\' then',
' -- address = 000000000000000000000000000000--',
'',
' 0|3',
'',
'else',
' -- $address$ = 000000000000000000000000000001--',
'$ ADDR_0x4',
' -- address = 000000000000000000000000000001--',
'',
' 4|3',
'',
'end if;',
]))
])

def test_if_elsif(self):
"""tests address decoder if-elsif statement construction"""
self.assertEqual(decoder_template(32, [(8, 7), (4, 3), (0, 3)], optimize=True), '\n'.join([
'if $address$(3) = \'1\' then',
' -- $address$ = 00000000000000000000000000001---',
'$ ADDR_0x8',
'elsif $address$(2) = \'0\' then',
' -- $address$ = 000000000000000000000000000000--',
'$ ADDR_0x0',
self._test_decoder(['8|7', '4|3', '0|3'], optimize=True, match=[
'if address(3) = \'1\' then',
' -- address = 00000000000000000000000000001---',
'',
' 8|7',
'',
'elsif address(2) = \'0\' then',
' -- address = 000000000000000000000000000000--',
'',
' 0|3',
'',
'else',
' -- $address$ = 000000000000000000000000000001--',
'$ ADDR_0x4',
' -- address = 000000000000000000000000000001--',
'',
' 4|3',
'',
'end if;',
]))
])

self.assertEqual(decoder_template(32, [(12, 3), (8, 3), (0, 7)], optimize=True), '\n'.join([
'if $address$(3) = \'0\' then',
' -- $address$ = 00000000000000000000000000000---',
'$ ADDR_0x0',
'elsif $address$(2) = \'0\' then',
' -- $address$ = 000000000000000000000000000010--',
'$ ADDR_0x8',
self._test_decoder(['12|3', '8|3', '0|7'], optimize=True, match=[
'if address(3) = \'0\' then',
' -- address = 00000000000000000000000000000---',
'',
' 0|7',
'',
'elsif address(2) = \'0\' then',
' -- address = 000000000000000000000000000010--',
'',
' 8|3',
'',
'else',
' -- $address$ = 000000000000000000000000000011--',
'$ ADDR_0xC',
' -- address = 000000000000000000000000000011--',
'',
' 12|3',
'',
'end if;',
]))
])

def test_case_statement(self):
"""tests address decoder case statement construction"""
self.assertEqual(decoder_template(32, [(8, 3), (4, 3)]), '\n'.join([
'if $address$(31 downto 4) = "0000000000000000000000000000" then',
' case $address$(3 downto 2) is',
self._test_decoder(['8|3', '4|3'], match=[
'if address(31 downto 4) = "0000000000000000000000000000" then',
' case address(3 downto 2) is',
' when "01" =>',
' -- $address$ = 000000000000000000000000000001--',
'$ ADDR_0x4',
' -- address = 000000000000000000000000000001--',
'',
' 4|3',
'',
' when "10" =>',
' -- $address$ = 000000000000000000000000000010--',
'$ ADDR_0x8',
' -- address = 000000000000000000000000000010--',
'',
' 8|3',
'',
' when others =>',
' null;',
' end case;',
'end if;',
]))
])

self.assertEqual(decoder_template(32, [(8, 3), (4, 3)], optimize=True), '\n'.join([
'case $address$(3 downto 2) is',
self._test_decoder(['8|3', '4|3'], optimize=True, match=[
'case address(3 downto 2) is',
' when "01" =>',
' -- $address$ = 000000000000000000000000000001--',
'$ ADDR_0x4',
' -- address = 000000000000000000000000000001--',
'',
' 4|3',
'',
' when others => -- "10"',
' -- $address$ = 000000000000000000000000000010--',
'$ ADDR_0x8',
' -- address = 000000000000000000000000000010--',
'',
' 8|3',
'',
'end case;',
]))
])

def test_common_suffix(self):
"""tests address decoder common suffix detection"""
self.assertEqual(decoder_template(32, [16, 32]), '\n'.join([
'if $address$(31 downto 6) = "00000000000000000000000000" then',
' if $address$(3 downto 0) = "0000" then',
' case $address$(5 downto 4) is',
self._test_decoder([16, 32], match=[
'if address(31 downto 6) = "00000000000000000000000000" then',
' if address(3 downto 0) = "0000" then',
' case address(5 downto 4) is',
' when "01" =>',
' -- $address$ = 00000000000000000000000000010000',
'$ ADDR_0x10',
' -- address = 00000000000000000000000000010000',
'',
' 16',
'',
' when "10" =>',
' -- $address$ = 00000000000000000000000000100000',
'$ ADDR_0x20',
' -- address = 00000000000000000000000000100000',
'',
' 32',
'',
' when others =>',
' null;',
' end case;',
' end if;',
'end if;',
]))
])

self.assertEqual(decoder_template(32, [16, 32], optimize=True), '\n'.join([
'case $address$(5 downto 4) is',
self._test_decoder([16, 32], optimize=True, match=[
'case address(5 downto 4) is',
' when "01" =>',
' -- $address$ = 00000000000000000000000000010000',
'$ ADDR_0x10',
' -- address = 00000000000000000000000000010000',
'',
' 16',
'',
' when others => -- "10"',
' -- $address$ = 00000000000000000000000000100000',
'$ ADDR_0x20',
' -- address = 00000000000000000000000000100000',
'',
' 32',
'',
'end case;',
]))
])

def test_duplicate(self):
"""tests address decoder duplicate address error"""
with self.assertRaisesRegex(ValueError, 'duplicate'):
decoder_template(32, [3, 3])
self._test_decoder([3, '3|0'])

self._test_decoder([3, '3|0'], allow_duplicate=True, match=[
'if address(31 downto 0) = "00000000000000000000000000000011" then',
' -- address = 00000000000000000000000000000011',
'',
' 3',
'',
' 3|0',
'',
'end if;',
])

def test_overlapping(self):
"""tests address decoder overlapping address error"""
with self.assertRaisesRegex(ValueError, 'overlap'):
decoder_template(32, [3, (3, 3)])

self.assertEqual(decoder_template(32, [3, (3, 3)], allow_overlap=True), '\n'.join([
'if $address$(31 downto 2) = "000000000000000000000000000000" then',
' if $address$(1 downto 0) = "11" then',
' -- $address$ = 00000000000000000000000000000011',
'$ ADDR_0x3',
' end if;',
'',
' -- $address$ = 000000000000000000000000000000--',
'$ ADDR_0x0',
'end if;',
]))
self._test_decoder([3, '3|3'])

def test_builder(self):
"""tests address decoder builder"""
decoder = Decoder('addr', 32, True)
decoder.add_action('block for address 0', 0)
decoder.add_action('block for address 4', 4, 2)
decoder.add_action('another block for address 0', 0)
self.assertEqual(decoder.generate(), '\n'.join([
'if addr(2) = \'0\' then',
' -- addr = 00000000000000000000000000000000',
self._test_decoder([3, '3|3'], allow_overlap=True, match=[
'if address(31 downto 2) = "000000000000000000000000000000" then',
' if address(1 downto 0) = "11" then',
' -- address = 00000000000000000000000000000011',
'',
' block for address 0',
' 3',
'',
' another block for address 0',
' end if;',
'',
'else',
' -- addr = 000000000000000000000000000001-0',
' -- address = 000000000000000000000000000000--',
'',
' block for address 4',
' 3|3',
'',
'end if;',
]))
])

def test_template(self):
"""tests adding decoders to templates"""
tple = TemplateEngine()
decoder.append_to_template(tple, 'BLOCK', 'comment for decoder')
self._test_decoder([3]).append_to_template(tple, 'BLOCK', 'comment for decoder')
self.assertEqual(tple.apply_str_to_str('$BLOCK', comment='-- '), '\n'.join([
'-- comment for decoder',
'if addr(2) = \'0\' then',
' -- addr = 00000000000000000000000000000000',
'',
' block for address 0',
'',
' another block for address 0',
'',
'else',
' -- addr = 000000000000000000000000000001-0',
'if address(31 downto 0) = "00000000000000000000000000000011" then',
' -- address = 00000000000000000000000000000011',
'',
' block for address 4',
' 3',
'',
'end if;',
''
Expand Down
10 changes: 5 additions & 5 deletions vhdmmio/vhdl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from collections import OrderedDict
from enum import Enum
from ..template import TemplateEngine, annotate_block
from .decoder import Decoder
from .address_decoder import AddressDecoder
from .types import Record, Array, SizedArray, Axi4Lite, Object, gather_defs
from .interface import Interface

Expand Down Expand Up @@ -135,10 +135,10 @@ def __init__(self, regfile):
self._interface = Interface(regfile.meta.name)

# Address decoder builders.
self._read_decoder = Decoder('r_addr', 32, optimize=regfile.optimize)
self._read_tag_decoder = Decoder('r_rtag', regfile.read_tag_width, optimize=True)
self._write_decoder = Decoder('w_addr', 32, optimize=regfile.optimize)
self._write_tag_decoder = Decoder('w_rtag', regfile.write_tag_width, optimize=True)
self._read_decoder = AddressDecoder('r_addr', 32, optimize=regfile.optimize)
self._read_tag_decoder = AddressDecoder('r_rtag', regfile.read_tag_width, optimize=True)
self._write_decoder = AddressDecoder('w_addr', 32, optimize=regfile.optimize)
self._write_tag_decoder = AddressDecoder('w_rtag', regfile.write_tag_width, optimize=True)

# Generate code for interrupts.
for interrupt in regfile.interrupts:
Expand Down
Loading

0 comments on commit fab19da

Please # to comment.