From 18d0fe0989b8bf0e0b9fcb421e625ec9d36b46da Mon Sep 17 00:00:00 2001 From: Jan Decaluwe Date: Mon, 16 May 2016 12:29:37 +0200 Subject: [PATCH] Addressed issue #167 --- example/cookbook/bitonic/Array8Sorter.v | 2 +- myhdl/_block.py | 8 ++ myhdl/test/bugs/test_issue_167.py | 160 ++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 myhdl/test/bugs/test_issue_167.py diff --git a/example/cookbook/bitonic/Array8Sorter.v b/example/cookbook/bitonic/Array8Sorter.v index 1b1fbba3..48376765 100644 --- a/example/cookbook/bitonic/Array8Sorter.v +++ b/example/cookbook/bitonic/Array8Sorter.v @@ -1,6 +1,6 @@ // File: Array8Sorter.v // Generated by MyHDL 1.0dev -// Date: Sat Mar 19 17:27:30 2016 +// Date: Sun May 15 11:40:06 2016 `timescale 1ns/10ps diff --git a/myhdl/_block.py b/myhdl/_block.py index f5e9bc56..798b7d65 100644 --- a/myhdl/_block.py +++ b/myhdl/_block.py @@ -26,6 +26,7 @@ import inspect import functools import myhdl +from myhdl._compat import PY2 from myhdl import BlockError, BlockInstanceError, Cosimulation from myhdl._instance import _Instantiator from myhdl._util import _flatten @@ -66,8 +67,15 @@ def _getCallInfo(): # caller may be undefined if instantiation from a Python module callerrec = None funcrec = stack[3] + name = funcrec[3] if len(stack) > 4: callerrec = stack[4] + # special case for list comprehension's extra scope in PY3 + if name == '': + if not PY2: + funcrec = stack[4] + if len(stack) > 5: + callerrec = stack[5] name = funcrec[3] frame = funcrec[0] diff --git a/myhdl/test/bugs/test_issue_167.py b/myhdl/test/bugs/test_issue_167.py new file mode 100644 index 00000000..6ffda93d --- /dev/null +++ b/myhdl/test/bugs/test_issue_167.py @@ -0,0 +1,160 @@ +''' Bitonic sort ''' + +# http://www.myhdl.org/examples/bitonic/ + +from __future__ import absolute_import + +import unittest +from random import randrange + +from myhdl import Signal, intbv, \ + always_comb, instance, \ + delay, block, StopSimulation + + +ASCENDING = True +DESCENDING = False + + +# modules + +@block +def compare(a_1, a_2, z_1, z_2, direction): + """ Combinatorial circuit with two input and two output signals. + Sorting to 'direction'. """ + + @always_comb + def logic(): + ''' Combinatorial logic ''' + if direction == (a_1 > a_2): + z_1.next = a_2 + z_2.next = a_1 + else: + z_1.next = a_1 + z_2.next = a_2 + + return logic + + +@block +def feedthru(in_a, out_z): + """ Equivalent of 'doing nothing'. """ + + @always_comb + def logic(): + ''' Combinatorial logic ''' + out_z.next = in_a + + return logic + + +@block +def bitonic_merge(list_a, list_z, direction): + """ bitonicMerge: + Generates the output from the input list of signals. + Recursive. """ + len_list = len(list_a) + half_len = len_list//2 + width = len(list_a[0]) + + if len_list > 1: + tmp = [Signal(intbv(0)[width:]) for _ in range(len_list)] + + comp = [compare(list_a[i], list_a[i+half_len], tmp[i], tmp[i+half_len], \ + direction) for i in range(half_len)] + + lo_merge = bitonic_merge( tmp[:half_len], list_z[:half_len], direction ) + hi_merge = bitonic_merge( tmp[half_len:], list_z[half_len:], direction ) + + return comp, lo_merge, hi_merge + else: + feed = feedthru(list_a[0], list_z[0]) + return feed + + +@block +def bitonic_sort(list_a, list_z, direction): + """ bitonicSort: + Produces a bitonic sequence. + Recursive. """ + len_list = len(list_a) + half_len = len_list//2 + width = len(list_a[0]) + + if len_list > 1: + tmp = [Signal(intbv(0)[width:]) for _ in range(len_list)] + + lo_sort = bitonic_sort( list_a[:half_len], tmp[:half_len], ASCENDING ) + hi_sort = bitonic_sort( list_a[half_len:], tmp[half_len:], DESCENDING ) + + merge = bitonic_merge( tmp, list_z, direction ) + return lo_sort, hi_sort, merge + else: + feed = feedthru(list_a[0], list_z[0]) + return feed + + +# tests + +@block +def array8sorter(a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7, + z_0, z_1, z_2, z_3, z_4, z_5, z_6, z_7): + ''' Sort Array with 8 values ''' + + list_a = [a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7] + list_z = [z_0, z_1, z_2, z_3, z_4, z_5, z_6, z_7] + + sort = bitonic_sort(list_a, list_z, ASCENDING) + return sort + + +class TestBitonicSort(unittest.TestCase): + ''' Test class for bitonic sort ''' + + def test_sort(self): + """ Check the functionality of the bitonic sort """ + length = 8 + width = 4 + + @block + def test_impl(): + ''' test implementation ''' + inputs = [ Signal(intbv(0)[width:]) for _ in range(length) ] + outputs = [ Signal(intbv(0)[width:]) for _ in range(length) ] + z_0, z_1, z_2, z_3, z_4, z_5, z_6, z_7 = outputs + a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7 = inputs + + inst = array8sorter(a_0, a_1, a_2, a_3, a_4, a_5, a_6, a_7, + z_0, z_1, z_2, z_3, z_4, z_5, z_6, z_7) + + @instance + def check(): + ''' testbench input and validation ''' + for i in range(100): + data = [randrange(2**width) for i in range(length)] + for i in range(length): + inputs[i].next = data[i] + yield delay(10) + data.sort() + self.assertEqual(data, outputs, 'wrong data') + raise StopSimulation + + return inst, check + + inst = test_impl() + inst.run_sim() + + +# convert + +def test_issue_167(): + ''' Convert to VHDL ''' + length = 8 + width = 4 + sigs = [Signal(intbv(0)[width:]) for _ in range(2*length)] + + inst = array8sorter( *sigs ) + inst.convert( hdl='VHDL' ) + +if __name__ == '__main__': + test_issue_167()